23 Мы вызываем sem_close, хотя, если бы мы не сделали этот вызов, семафор все равно закрылся бы автоматически при завершении процесса и ресурсы системы были бы высвобождены.
Программа semunlink
Программа в листинге 10.4 удаляет именованный семафор.
Листинг 10.4. Удаление именованного семафора
//pxsem/semunlink.c
1 #include "unpipc.h"
2 int
3 main(int argc, char **argv)
4 {
5 if (argc != 2)
6 err_quit("usage: semunlink <name>");
7 Sem_unlink(argv[1]);
8 exit(0);
9 }
Программа semgetvalue
В листинге 10.5 приведен текст простейшей программы, которая открывает указанный именованный семафор, получает его текущее значение и выводит его.
Листинг 10.5. Получение и вывод значения семафора
//pxsem/semgetvalue.с
1 #include "unpipc.h"
2 int
3 main(int argc, char **argv)
4 {
5 sem_t *sem;
6 int val;
7 if (argc != 2)
8 err_quit("usage: semgetvalue <name>");
9 sem = Sem_open(argv[1], 0);
10 Sem_getvalue(sem, &val);
11 printf("value = %dn", val);
12 exit(0);
13 }
Открытие семафора
9 Семафор, который мы открываем, должен быть заранее создан другой программой. Вторым аргументом sem_open будет 0: мы не указываем флаг O_CREAT и нам не нужно задавать никаких других параметров открытия 0_ххх.
Программа semwait
Программа в листинге 10.6 открывает именованный семафор, вызывает semwait (которая приостанавливает выполнение процесса, если значение семафора меньше либо равно 0, а при положительном значении семафора уменьшает его на 1), получает и выводит значение семафора, а затем останавливает свою работу навсегда при вызове pause.
Листинг 10.6. Ожидание изменения значения семафора и вывод нового значения
//pxsem/semwait.c
1 #include "unpipc.h"
2 int
3 main(int argc, char **argv)
4 {
5 sem_t *sem;
6 int val;
7 if (argc != 2)
8 err_quit("usage: semwait <name>");
9 sem = Sem_open(argv[1], 0);
10 Sem_wait(sem);
11 Sem_getvalue(sem, &val);
12 printf("pid %ld has semaphore, value = %dn", (long) getpid(), val);
13 pause(); /* блокируется, пока не будет удален */
14 exit(0);
15 }
Программа sempost
В листинге 10.7 приведена программа, которая выполняет операцию post для указанного семафора (то есть увеличивает его значение на 1), а затем получает значение этого семафора и выводит его.
Листинг 10.7. Увеличение значения семафора
//pxsem/sempost.c
1 #include "unpipc.h"
2 int
3 main(int argc, char **argv)
4 {
5 sem_t *sem;
6 int val;
7 if (argc != 2)
8 err_quit("usage: sempost <name>");
9 sem = Sem_open(argv[1], 0);
10 Sem_post(sem);
11 Sem_getvalue(sem, &val);
12 printf("value = %dn", val);
13 exit(0);
14 }
Примеры
Для начала мы создадим именованный семафор в Digital Unix 4.0B и выведем его значение, устанавливаемое по умолчанию при инициализации:
alpha % semcreate /tmp/test1
alpha % ls-l /tmp/test1
-rw-r--r-- 1 rstevens system 264 Nov 13 08:51 /tmp/test1
alpha %semgetvalue /tmp/test1
value = 1
Аналогично очередям сообщений Posix система создает файл семафора с тем именем, которое мы указали при вызове функции.
Теперь подождем изменения семафора и прервем работу программы, установившей блокировку:
alpha % semwait /tmp/test1
pid 9702 has semaphore, value = 0 значение после возврата из sem_wait
^? клавиша прерывания работы в нашей системе
alpha % semgetvalue /tmp/test1
value = 0 значение остается нулевым
Приведенный пример иллюстрирует упомянутые ранее особенности. Во-первых, значение семафора обладает живучестью ядра. Значение 1, установленное при создании семафора, хранится в ядре даже тогда, когда ни одна программа не пользуется этим семафором. Во-вторых, при выходе из программы semwait, заблокировавшей семафор, значение его не изменяется, то есть ресурс остается заблокированным. Это отличает семафоры от блокировок fcntl, описанных в главе 9, которые снимались автоматически при завершении работы процесса.
Покажем теперь, что в этой реализации отрицательное значение семафора используется для хранения информации о количестве процессов, ожидающих разблокирования семафора:
alpha % semgetvalue /tmp/test1
value = 0 это значение сохранилось с конца предыдущего примера
alpha % semwait /tmp/test1 & запуск в фоновом режиме
[1] 9718 блокируется в ожидании изменения значения семафора
alpha % semgetvalue /tmp/test1
value = –1 один процесс ожидает изменения семафора
alpha % semwait /tmp/test1 & запуск еще одного процесса в фоновом режиме
[2] 9727 он также блокируется
alpha % semgetvalue /tmp/test1
value = –2 два процесса ожидают изменения семафора
alpha % sempost /tmp/test1
value = –1 значение после возвращенияиз sem_post
pid 9718 has semaphore, value = –1 вывод программы semwait
alpha % sempost /tmp/test1
value = 0
pid 9727 has semaphore, value = 0 вывод программы semwait
При первом вызове sem_post значение семафора изменилось с –2 на –1 и один из процессов, ожидавших изменения значения семафора, был разблокирован.
Выполним те же действия в Solaris 2.6, обращая внимание на различия в реализации:
solaris % semcreate /test2
solaris % ls –l /tmp/.*test2*
-rw-r--r-- 1 rstevens other1 48 Nov 13 09:11 /tmp/.SEMDtest2
–rw-rw-rw– 1 rstevens other1 0 Nov 13 09:11 /tmp/.SEMLtest2
solaris % semgetvalue /test2
value = 1
Аналогично очередям сообщений Posix файлы создаются в каталоге /tmp, причем указываемое при вызове имя становится суффиксом имен файлов. Разрешения первого файла соответствуют указанным в вызове sem_open, а второй файл, как можно предположить, используется для блокировки доступа.
Проверим, что ядро не осуществляет автоматического увеличения значения семафора при завершении работы процесса, установившего блокировку:
solaris % semwait /test2
pid 4133 has semaphore, value = 0
^? нажимаем клавишу прерывания выполнения
solaris % semgetvalue /test2
value = 0
Посмотрим теперь, как меняется значение семафора в этой реализации при появлении новых процессов, ожидающих изменения значения семафора:
solaris % semgetvalue /test2
value = 0 значение сохранилось с конца предыдущего примера
solaris % semwait /test2& запуск в фоновом режиме
[1] 4257 программа блокируется
solaris % semgetvalue /test2
value = 0 в этой реализации отрицательные значения не используются
solaris % semwait /test2& еще один фоновый процесс
[2] 4263
solaris % semgetvalue /test2
value 0 и для двух ожидающих процессов значение остается нулевым
solaris % sempost /test2 выполняем операцию post
pid 4257 has semaphore, value = 0 вывод программы semwait
value = 0
solaris % sempost /test2
pid 4263 has semaphore, value = 0 вывод программы semwait
value = 0
Можно заметить отличие по сравнению с результатами выполнения той же последовательности команд в Digital Unix 4.0B: после изменения значения семафора управление сразу же передается ожидающему изменения семафора процессу.
10.6. Задача производителей и потребителей
В разделе 7.3 мы описали суть задачи производителей и потребителей и привели несколько возможных ее решений, в которых несколько потоков-производителей заполняли массив, который обрабатывался одним потоком-потребителем.