|
организовать двустороннюю поочередную связь процесса-родителя и процесса-ребенка через 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 запусков второй программы.