C Language
Komunikacja międzyprocesowa (IPC)
Szukaj…
Wprowadzenie
Mechanizmy komunikacji międzyprocesowej (IPC) pozwalają na komunikację między różnymi niezależnymi procesami. Standard C nie zapewnia żadnych mechanizmów IPC. Dlatego wszystkie takie mechanizmy są zdefiniowane przez system operacyjny hosta. POSIX definiuje szeroki zestaw mechanizmów IPC; Windows definiuje inny zestaw; a inne systemy definiują własne warianty.
Semafory
Semafory służą do synchronizacji operacji między dwoma lub więcej procesami. POSIX definiuje dwa różne zestawy funkcji semaforów:
- „System V IPC” -
semctl()
,semop()
,semget()
. - „POSIX Semaphores” -
sem_close()
,sem_destroy()
,sem_getvalue()
,sem_init()
,sem_open()
,sem_post()
,sem_trywait()
,sem_unlink()
.
W tej sekcji opisano semafory IPC Systemu V, tzw. Ponieważ pochodzą z systemu Unix V.
Najpierw musisz dołączyć wymagane nagłówki. Wymagane były stare wersje POSIX #include <sys/types.h>
; nowoczesny POSIX i większość systemów tego nie wymaga.
#include <sys/sem.h>
Następnie musisz zdefiniować klucz zarówno nadrzędny, jak i podrzędny.
#define KEY 0x1111
Ten klucz musi być taki sam w obu programach, w przeciwnym razie nie będą odnosić się do tej samej struktury IPC. Istnieją sposoby generowania uzgodnionego klucza bez sztywnego kodowania jego wartości.
Następnie, w zależności od kompilatora, może być konieczne wykonanie następującego kroku: zadeklarowanie unii dla celów operacji semaforowych.
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
};
Następnie zdefiniuj swoje try ( semwait
) i podbij ( semsignal
) struktury. Nazwy P i V pochodzą z języka niderlandzkiego
struct sembuf p = { 0, -1, SEM_UNDO}; # semwait
struct sembuf v = { 0, +1, SEM_UNDO}; # semsignal
Teraz zacznij od uzyskania identyfikatora semafora IPC.
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 */
}
W rodzicu zainicjuj semafor, aby miał licznik 1.
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 */
}
Teraz możesz zmniejszać lub zwiększać semafor według potrzeb. Na początku sekcji krytycznej zmniejszasz licznik za pomocą funkcji semop()
:
if (semop(id, &p, 1) < 0) {
/* error handling code */
}
Aby zwiększyć semafor, użyj &v
zamiast &p
:
if (semop(id, &v, 1) < 0) {
/* error handling code */
}
Zauważ, że każda funkcja zwraca 0
w przypadku sukcesu, a -1
w przypadku niepowodzenia. Nie sprawdzanie tych statusów zwrotu może powodować druzgocące problemy.
Przykład 1.1: Wyścigi z wątkami
Poniższy program będzie miał proces fork
dziecko, a zarówno rodzic, jak i dziecko spróbują wydrukować znaki na terminalu bez synchronizacji.
#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);
}
}
}
Wyjście (1. przebieg):
aAABaBCbCbDDcEEcddeFFGGHHeffgghh
(2. bieg):
aabbccAABddBCeeCffgDDghEEhFFGGHH
Kompilacja i uruchomienie tego programu powinno za każdym razem dawać inne wyjście.
Przykład 1.2: Unikaj wyścigu z semaforami
Modyfikując Przykład 1.1, aby używać semaforów, mamy:
#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);
}
}
}
Wynik:
aabbAABBCCccddeeDDffEEFFGGHHgghh
Kompilacja i uruchomienie tego programu daje za każdym razem to samo wyjście.