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:

  1. „System V IPC” - semctl() , semop() , semget() .
  2. „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.



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow