Уильям Стивенс - UNIX: взаимодействие процессов
//svsem/semsetvalues.с
1 #include "unpipc.h"
2 int
3 main(int argc, char **argv)
4 {
5 int semid, nsems, i;
6 struct semid_ds seminfo;
7 unsigned short *ptr;
8 union semun arg;
9 if (argc < 2)
10 err_quit("usage: semsetvalues <pathname> [ values … ]");
11 /* получение количества семафоров в наборе */
12 semid = Semget(Ftok(argv[1], 0), 0, 0);
13 arg.buf = &seminfo;
14 Semctl(semid, 0, IPC_STAT, arg);
15 nsems = arg.buf->sem_nsems;
16 /* получение значений из командной строки */
17 if (argc != nsems + 2)
18 err_quit("%d semaphores in set, %d values specified", nsems, argc-2);
19 /* выделение памяти под значения семафоров набора, помещение этих значений в новый массив */
20 ptr = Calloc(nsems, sizeof(unsigned short));
21 arg.array = ptr;
22 for (i = 0; i < nsems; i++)
23 ptr[i] = atoi(argv[i +2]);
24 Semctl(semid, 0, SETALL, arg);
25 exit(0);
26 }
Программа semgetvalues
В листинге 11.4 приведен текст программы semgetvalues, которая получает и выводит значения всех семафоров набора.
Получение количества семафоров в наборе11-15 После получения идентификатора семафора с помощью semget мы вызываем semctl с командой IPC_STAT для получения значений полей структуры semi d_ds данного семафора. Поле sem_nsems содержит нужную нам информацию о количестве семафоров в наборе.
Получение всех значений16-22 Мы выделяем память под массив беззнаковых коротких целых, по одному элементу на каждый семафор набора. Вызов semctl с командой GETALL позволяет получить все значения семафоров набора одновременно. Каждое значение выводится.
Листинг 11.4. Программа semgetvalues//svsem/semgetvalues.c
1 #include "unpipc.h"
2 int
3 main(int argc, char **argv)
4 {
5 int semid, nsems, i;
6 struct semid_ds seminfo;
7 unsigned short *ptr;
8 union semun arg;
9 if (argc != 2)
10 err_quit("usage: semgetvalues <pathname>");
11 /* получаем количество семафоров в наборе */
12 semid = Semget(Ftok(argv[1], 0), 0, 0);
13 arg.buf = &seminfo;
14 Semctl(semid, 0, IPC_STAT, arg);
15 nsems = arg.buf->sem_nsems;
16 /* выделяем память под имеющееся количество элементов */
17 ptr = Calloc(nsems, sizeof(unsigned short));
18 arg.array = ptr;
19 /* получаем и выводим значения семафоров */
20 Semctl(semid, 0, GETALL, arg);
21 for (i = 0; i < nsems; i++)
22 printf("semval[%d] = %dn", i, ptr[i]);
23 exit(0);
24 }
Программа semops
В листинге 11.5 приведен текст программы semops, позволяющей выполнять последовательность действий над набором семафоров.
Параметры командной строки7-19 Параметр –n соответствует установленному флагу IPC_NOWAIT для каждой операции, а параметр –u аналогичным образом позволяет указать для каждой операции флаг SEM_UNDO. Обратите внимание, что функция semop позволяет указывать свой набор флагов для каждого элемента структуры sembuf (то есть для каждой из операций в отдельности), но для простоты мы в нашей программе задаем одинаковые флаги для всех операций.
Выделение памяти под набор операций20-29 После открытия семафора вызовом semget мы выделяем память под массив структур sembuf, по одному элементу на каждую операцию из командной строки. В отличие от предыдущих двух программ эта позволяет пользователю задать меньше команд, чем имеется семафоров в наборе.
Выполнение операций30 Вызов semop выполняет последовательность операций над семафорами набора.
Листинг 11.5. Программа semops//svsem/semops.c
1 #include "unpipc.h"
2 int
3 main(int argc, char **argv)
4 {
5 int с, i, flag, semid, nops;
6 struct sembuf *ptr;
7 flag = 0;
8 while ((c = Getopt(argc, argv, "nu")) != –1) {
9 switch (c) {
10 case 'n':
11 flag |= IPC_NOWAIT; /* для всех операций */
12 break;
13 case 'u':
14 flag |= SEM_UNDO; /* для всех операций */
15 break;
16 }
17 }
18 if (argc = optind < 2) /* argc – optind = количество оставшихся аргументов */
19 err_quit("usage: semops [ –n ] [ –u ] <pathname> operation …");
20 semid = Semget(Ftok(argv[optind], 0), 0, 0);
21 optind++;
22 nops = argc – optind;
23 /* выделение памяти под операции, сохранение их в массиве и выполнение */
24 ptr = Calloc(nops, sizeof(struct sembuf));
25 for (i = 0; i < nops; i++) {
26 ptr[i].sem_num = i;
27 ptr[i].sem_op = atoi(argv[optind + i]); /* <0, 0, or >0 */
28 ptr[i].sem_flg = flag;
29 }
30 Semop(semid, ptr, nops);
31 exit(0);
32 }
Примеры
Теперь мы продемонстрируем работу пяти приведенных выше программ и исследуем некоторые свойства семафоров System V:
solaris % touch /tmp/rich
solaris % semcreate –e /tmp/rich 3
solaris % semsetvalues /tmp/rich 1 2 3
solaris % semgetvalues /tmp/rich
semval[0] = 1
semval[1] = 2
semval[2] = 3
Сначала мы создали файл с именем /tmp/rich, который использовался при вызове ftok для вычисления идентификатора набора семафоров. Программа semcreate создает набор с тремя элементами. Программа semsetvalues устанавливает значения этих элементов (1, 2 и 3), a semgetvalues выводит их значения.
Теперь продемонстрируем атомарность выполнения последовательности операций над набором:
solaris % semops –n /tmp/rich –1 –2 –4
semctl error: Resource temporarily unavailable
solaris % semgetvalues /tmp/rich
semval[0] = 1
semval[1] = 2
semval[2] = 3
В командной строке мы указываем параметр, отключающий блокировку (-n), и три операции, каждая из которых уменьшает одно из значений набора семафоров. Первая операция завершается успешно (мы можем вычесть 1 из значения первого элемента набора, потому что до вычитания оно равно 1), вторая операция также проходит (вычитаем 2 из значения второго семафора, равного 2), но третья операция выполнена быть не может (мы не можем вычесть 4 из значения третьего семафора, потому что оно равно 3). Поскольку последняя операция последовательности не может быть выполнена и поскольку мы отключили режим блокирования процесса, функция возвращает ошибку EAGAIN. Если бы мы не указали флаг отключения блокировки, выполнение процесса было бы приостановлено до тех пор, пока операция вычитания не стала бы возможной. После этого мы проверяем, чтобы ни одно из значений семафоров набора не изменилось. Хотя первые две операции и могли бы быть выполнены, ни одна из трех на самом деле произведена не была, поскольку последняя операция была некорректной. Атомарность semop и означает, что выполняются либо все операции, либо ни одна из них.
Теперь продемонстрируем работу флага SEM_UNDO:
solaris % semsetvalues /tmp/rich 1 2 3 устанавливаем конкретные значения
solaris % semops –u /tmp/rich -1 –2 –3 для каждой операции указывается флаг SEM_UNDO
solaris % semgetvalues /tmp/rich
semval[0] = 1 все произведенные изменения были сброшены
после завершения работыпрограммы semops
semval[1] = 2
semval[2] = 3
solaris % semops /tmp/rich -1 –2 –3 теперь мы не указываем флаг SEM_UNDO
solaris % semgetvalues /tmp/rich
semval[0] = 0
semval[1] = 0
semval[2] = 0
Сначала мы заново устанавливаем значения семафоров в наборе равными 1, 2 и 3 с помощью программы semsetvalues, а затем запускаем программу semops с операциями –1, –2, –3. При этом все три значения семафоров становятся нулевыми, но, так как мы указали параметр –u при вызове semops, для всех трех операций устанавливается флаг SEM_UNDO. При этом значения semadj для элементов набора семафоров становятся равными 1, 2 и 3 соответственно. После завершения программы semops эти значения добавляются к значениям семафоров, в результате чего их значения становятся равными 1, 2 и 3, как будто мы и не запускали программу. В этом мы убеждаемся, запустив semgetvalues. Затем мы снова запускаем semops, но уже без параметра –u, и убеждаемся, что при этом значения семафоров становятся нулевыми и остаются таковыми даже после выхода из программы.
11.6. Блокирование файлов
С помощью семафоров System V можно реализовать еще одну версию функций my_lock и my_unlock из листинга 10.10. Новый вариант приведен в листинге 11.6.