C Language
Interprocess Communication (IPC)
Zoeken…
Invoering
Interprocescommunicatie (IPC) mechanismen laten verschillende onafhankelijke processen toe om met elkaar te communiceren. Standaard C biedt geen IPC-mechanismen. Daarom worden al dergelijke mechanismen gedefinieerd door het besturingssysteem van de host. POSIX definieert een uitgebreide reeks IPC-mechanismen; Windows definieert een andere set; en andere systemen definiëren hun eigen varianten.
semaforen
Semaphores worden gebruikt om bewerkingen tussen twee of meer processen te synchroniseren. POSIX definieert twee verschillende sets semafoorfuncties:
- 'System V IPC' -
semctl()
,semop()
,semget()
. - 'POSIX Semaphores' -
sem_close()
,sem_destroy()
,sem_getvalue()
,sem_init()
,sem_open()
,sem_post()
,sem_trywait()
,sem_unlink()
.
In deze sectie worden de System V IPC-semaforen beschreven, zo genoemd omdat ze zijn ontstaan met Unix System V.
Eerst moet u de vereiste kopteksten opnemen. Oude versies van POSIX vereist #include <sys/types.h>
; moderne POSIX en de meeste systemen vereisen dit niet.
#include <sys/sem.h>
Vervolgens moet u een sleutel definiëren in zowel de ouder als het kind.
#define KEY 0x1111
Deze sleutel moet in beide programma's hetzelfde zijn, anders verwijzen ze niet naar dezelfde IPC-structuur. Er zijn manieren om een overeengekomen sleutel te genereren zonder de waarde ervan hard te coderen.
Vervolgens moet u, afhankelijk van uw compiler, deze stap wel of niet uitvoeren: een unie declareren voor semafoorbewerkingen.
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
};
Definieer vervolgens je try ( semwait
) en raise ( semsignal
) structuren. De namen P en V zijn afkomstig uit het Nederlands
struct sembuf p = { 0, -1, SEM_UNDO}; # semwait
struct sembuf v = { 0, +1, SEM_UNDO}; # semsignal
Begin nu met het verkrijgen van de id voor uw IPC-semafoor.
int id;
// 2nd argument is number of semaphores
// 3rd argument is the mode (IPC_CREAT creates the semaphore set if needed)
if ((id = semget(KEY, 1, 0666 | IPC_CREAT) < 0) {
/* error handling code */
}
Initialiseer de semafoor in de ouder om een teller van 1 te hebben.
union semun u;
u.val = 1;
if (semctl(id, 0, SETVAL, u) < 0) { // SETVAL is a macro to specify that you're setting the value of the semaphore to that specified by the union u
/* error handling code */
}
Nu kunt u de semafoor naar wens verlagen of verhogen. Aan het begin van uw kritieke sectie semop()
u de teller met de semop()
-functie:
if (semop(id, &p, 1) < 0) {
/* error handling code */
}
Om de semafoor te verhogen, gebruikt u &v
plaats van &p
:
if (semop(id, &v, 1) < 0) {
/* error handling code */
}
Merk op dat elke functie 0
retourneert bij succes en -1
bij mislukking. Het niet controleren van deze retourstatussen kan verwoestende problemen veroorzaken.
Voorbeeld 1.1: Racen met threads
Onderstaande programma zal een proces fork
een kind en zowel ouder als kind poging om tekens op de terminal af te drukken zonder enige synchronisatie.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main()
{
int pid;
pid = fork();
srand(pid);
if(pid < 0)
{
perror("fork"); exit(1);
}
else if(pid)
{
char *s = "abcdefgh";
int l = strlen(s);
for(int i = 0; i < l; ++i)
{
putchar(s[i]);
fflush(stdout);
sleep(rand() % 2);
putchar(s[i]);
fflush(stdout);
sleep(rand() % 2);
}
}
else
{
char *s = "ABCDEFGH";
int l = strlen(s);
for(int i = 0; i < l; ++i)
{
putchar(s[i]);
fflush(stdout);
sleep(rand() % 2);
putchar(s[i]);
fflush(stdout);
sleep(rand() % 2);
}
}
}
Uitgang (1e run):
aAABaBCbCbDDcEEcddeFFGGHHeffgghh
(2e run):
aabbccAABddBCeeCffgDDghEEhFFGGHH
Het compileren en uitvoeren van dit programma zou je elke keer een andere output moeten geven.
Voorbeeld 1.2: Vermijd racen met semaphores
Wijzigingen in voorbeeld 1.1 om semaforen te gebruiken, hebben we:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#define KEY 0x1111
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
};
struct sembuf p = { 0, -1, SEM_UNDO};
struct sembuf v = { 0, +1, SEM_UNDO};
int main()
{
int id = semget(KEY, 1, 0666 | IPC_CREAT);
if(id < 0)
{
perror("semget"); exit(11);
}
union semun u;
u.val = 1;
if(semctl(id, 0, SETVAL, u) < 0)
{
perror("semctl"); exit(12);
}
int pid;
pid = fork();
srand(pid);
if(pid < 0)
{
perror("fork"); exit(1);
}
else if(pid)
{
char *s = "abcdefgh";
int l = strlen(s);
for(int i = 0; i < l; ++i)
{
if(semop(id, &p, 1) < 0)
{
perror("semop p"); exit(13);
}
putchar(s[i]);
fflush(stdout);
sleep(rand() % 2);
putchar(s[i]);
fflush(stdout);
if(semop(id, &v, 1) < 0)
{
perror("semop p"); exit(14);
}
sleep(rand() % 2);
}
}
else
{
char *s = "ABCDEFGH";
int l = strlen(s);
for(int i = 0; i < l; ++i)
{
if(semop(id, &p, 1) < 0)
{
perror("semop p"); exit(15);
}
putchar(s[i]);
fflush(stdout);
sleep(rand() % 2);
putchar(s[i]);
fflush(stdout);
if(semop(id, &v, 1) < 0)
{
perror("semop p"); exit(16);
}
sleep(rand() % 2);
}
}
}
Output:
aabbAABBCCccddeeDDffEEFFGGHHgghh
Door dit programma te compileren en uit te voeren, krijgt u elke keer dezelfde uitvoer.