организовать двустороннюю поочередную связь процесса-родителя и процесса-ребенка через pipe, используя для синхронизации сигналы sigusr1 и sigusr2. |
Семафоры в UNIX как средство синхронизации процессов
Выполнение операций над семафорами. Системный вызов semop()
Для выполнения операций A, D и Z над семафорами из массива используется системный вызов semop() , обладающий довольно сложной семантикой. Разработчики System V IPC явно перегрузили этот вызов, применяя его не только для выполнения всех трех операций, но еще и для нескольких семафоров в массиве IPC-семафоров одновременно. Для правильного использования этого вызова необходимо выполнить следующие действия:
- Определиться, для каких семафоров из массива предстоит выполнить операции. Необходимо иметь в виду, что все операции реально совершаются только перед успешным возвращением из системного вызова, т.е. если вы хотите выполнить операции A(S1,5) и Z(S2) в одном вызове и оказалось, что S2 != 0, то значение семафора S1 не будет изменено до тех пор, пока значение S2 не станет равным 0. Порядок выполнения операций в случае, когда процесс не переходит в состояние ожидание, не определен. Так, например, при одновременном выполнении операций A(S1,1) и D(S2,1) в случае S2 > 1 неизвестно, что произойдет раньше – уменьшится значение семафора S2 или увеличится значение семафора S1. Если порядок для вас важен, лучше применить несколько вызовов вместо одного.
- После того как вы определились с количеством семафоров и совершаемыми операциями, необходимо завести в программе массив из элементов типа struct sembuf с размерностью, равной определенному количеству семафоров (если операция совершается только над одним семафором, можно, естественно, обойтись просто переменной). Каждый элемент этого массива будет соответствовать операции над одним семафором.
- Заполнить элементы массива. В поле sem_flg каждого элемента нужно занести значение 0 (другие значения флагов в семинарах мы рассматривать не будем). В поля sem_num и sem_op следует занести номера семафоров в массиве IPC семафоров и соответствующие коды операций. Семафоры нумеруются, начиная с 0. Если у вас в массиве всего один семафор, то он будет иметь номер 0. Операции кодируются так:
- В качестве второго параметра системного вызова semop() указать адрес заполненного массива, а в качестве третьего параметра – ранее определенное количество семафоров, над которыми совершаются операции.
Прогон примера с использованием семафора
Для иллюстрации сказанного рассмотрим простейшие программы, синхронизирующие свои действия с помощью семафоров
/* Программа 08-1a.c для иллюстрации работы с семафорами */ /* Эта программа получает доступ к одному системному семафору, ждет, пока его значение не станет больше или равным 1 после запусков программы 08-1b.c,а затем уменьшает его на 1*/ #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> #include <stdio.h> int main() { int semid; /* IPC дескриптор для массива IPC семафоров */ char pathname[] = "08-1a.c"; /* Имя файла, использующееся для генерации ключа. Файл с таким именем должен существовать в текущей директории */ key_t key; /* IPC ключ */ struct sembuf mybuf; /* Структура для задания операции над семафором */ /* Генерируем IPC-ключ из имени файла 08-1a.c в текущей директории и номера экземпляра массива семафоров 0 */ if((key = ftok(pathname,0)) < 0){ printf("Can\'t generate key\n"); exit(-1); } /* Пытаемся получить доступ по ключу к массиву семафоров, если он существует, или создать его из одного семафора, если его еще не существует, с правами доступа read & write для всех пользователей */ if((semid = semget(key, 1, 0666 | IPC_CREAT)) < 0){ printf("Can\'t get semid\n"); exit(-1); } /* Выполним операцию D(semid1,1) для нашего массива семафоров. Для этого сначала заполним нашу структуру. Флаг, как обычно, полагаем равным 0. Наш массив семафоров состоит из одного семафора с номером 0. Код операции -1.*/ mybuf.sem_op = -1; mybuf.sem_flg = 0; mybuf.sem_num = 0; if(semop(semid, &mybuf, 1) < 0){ printf("Can\'t wait for condition\n"); exit(-1); } printf("Condition is present\n"); return 0; }Листинг 8.1. Программа 08-1a.c для иллюстрации работы с семафорами
/* Программа 08-1b.c для иллюстрации работы с семафорами */ /* Эта программа получает доступ к одному системному семафору и увеличивает его на 1*/ #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> #include <stdio.h> int main() { int semid; /* IPC дескриптор для массива IPC семафоров */ char pathname[] = "08-1a.c"; /* Имя файла, использующееся для генерации ключа. Файл с таким именем должен существовать в текущей директории */ key_t key; /* IPC ключ */ struct sembuf mybuf; /* Структура для задания операции над семафором */ /* Генерируем IPC ключ из имени файла 08-1a.c в текущей директории и номера экземпляра массива семафоров 0 */ if((key = ftok(pathname,0)) < 0){ printf("Can\'t generate key\n"); exit(-1); } /* Пытаемся получить доступ по ключу к массиву семафоров, если он существует, или создать его из одного семафора, если его еще не существует, с правами доступа read & write для всех пользователей */ if((semid = semget(key, 1, 0666 | IPC_CREAT)) < 0){ printf("Can\'t get semid\n"); exit(-1); } /* Выполним операцию A(semid1,1) для нашего массива семафоров. Для этого сначала заполним нашу структуру. Флаг, как обычно, полагаем равным 0. Наш массив семафоров состоит из одного семафора с номером 0. Код операции 1.*/ mybuf.sem_op = 1; mybuf.sem_flg = 0; mybuf.sem_num = 0; if(semop(semid, &mybuf, 1) < 0){ printf("Can\'t wait for condition\n"); exit(-1); } printf("Condition is set\n"); return 0; }Листинг 8.1b. Программа 08-1b.c для иллюстрации работы с семафорами
Первая программа выполняет над семафором S операцию D(S,1) , вторая программа выполняет над тем же семафором операцию A(S,1) . Если семафора в системе не существует, любая программа создает его перед выполнением операции. Поскольку при создании семафор всегда инициируется 0, то программа 1 может работать без блокировки только после запуска программы 2. Наберите программы, сохраните под именами 08-1а.с и 08-1b.c cоответственно, откомпилируйте и проверьте правильность их поведения.
Изменение предыдущего примера
Измените программы из предыдущего раздела так, чтобы первая программа могла работать без блокировки после не менее 5 запусков второй программы.