Поиск…


Вступление

Механизмы межпроцессной коммуникации (МПК) позволяют различным независимым процессам взаимодействовать друг с другом. Стандарт C не предусматривает каких-либо механизмов МПК. Следовательно, все такие механизмы определяются операционной системой хоста. POSIX определяет обширный набор механизмов МПК; Windows определяет другой набор; и другие системы определяют их собственные варианты.

семафоры

Семафоры используются для синхронизации операций между двумя или несколькими процессами. POSIX определяет два разных набора функций семафора:

  1. «System V IPC» - semctl() , semop() , semget() .
  2. «Семафоры POSIX» - sem_close() , sem_destroy() , sem_getvalue() , sem_init() , sem_open() , sem_post() , sem_trywait() , sem_unlink() .

В этом разделе описываются семафоры System V IPC, так называемые, потому что они возникли в Unix System V.

Во-первых, вам нужно будет включить нужные заголовки. Для старых версий POSIX требуется #include <sys/types.h> ; современный POSIX, и большинство систем не требуют этого.

#include <sys/sem.h>

Затем вам нужно определить ключ как у родителя, так и у ребенка.

#define KEY 0x1111 

Этот ключ должен быть одинаковым в обеих программах или они не будут ссылаться на одну и ту же структуру IPC. Существуют способы создания согласованного ключа без жесткого кодирования его значения.

Далее, в зависимости от вашего компилятора, вам может понадобиться или не нужно делать этот шаг: объявить объединение для операций семафора.

union semun {
    int val;
    struct semid_ds *buf;
    unsigned short  *array;
};

Затем определите структуры try ( semwait ) и raise ( semsignal ). Имена P и V происходят от голландских

struct sembuf p = { 0, -1, SEM_UNDO}; # semwait
struct sembuf v = { 0, +1, SEM_UNDO}; # semsignal

Теперь начните с получения идентификатора для вашего семафора 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 */
}

В родителе инициализируйте семафор, чтобы иметь счетчик из 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 */
}

Теперь вы можете уменьшать или увеличивать семафор по мере необходимости. В начале вашего критического раздела вы уменьшаете счетчик с помощью функции semop() :

if (semop(id, &p, 1) < 0) {
    /* error handling code */
}

Чтобы увеличить семафор, вы используете &v вместо &p :

if (semop(id, &v, 1) < 0) {
    /* error handling code */
}

Обратите внимание, что каждая функция возвращает 0 при успехе и -1 при сбое. Не проверять эти статусы возврата могут привести к разрушительным проблемам.


Пример 1.1: Гонки с потоками

В приведенной ниже программе будет выполняться процесс fork для дочернего элемента, а родительский и дочерний объекты будут пытаться печатать символы на терминале без какой-либо синхронизации.

#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);
        }
    }
}

Выход (1-й прогон):

aAABaBCbCbDDcEEcddeFFGGHHeffgghh

(2-й прогон):

aabbccAABddBCeeCffgDDghEEhFFGGHH

Компиляция и запуск этой программы должны давать вам разные результаты каждый раз.

Пример 1.2: Избегайте гонок с помощью семафоров

Изменив пример 1.1 для использования семафоров, мы имеем:

#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);
        }
    }
}

Выход:

aabbAABBCCccddeeDDffEEFFGGHHgghh

Компиляция и запуск этой программы даст вам один и тот же результат каждый раз.



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow