13 op.sem_flg = 0;
14 if (semop(sem->sem_semid, &op, 1) < 0)
15 return(-1);
16 return(0);
17 }
Функция sem_wait
Следующая функция приведена в листинге 10.42; она называется sem_wait и ожидает изменения значения семафора с нулевого на ненулевое, после чего уменьшает значение семафора на 1.
11-16 Мы вызываем semop с операцией, уменьшающей значение семафора на 1.
//my_pxsem_svsem/sem_wait.c
1 #include "unpipc.h"
2 #include "semaphore.h"
3 int
4 mysem_wait(mysem_t *sem)
5 {
6 struct sembuf op;
7 if (sem->sem_magic != SEM_MAGIC) {
8 errno = EINVAL;
9 return(-1);
10 }
11 op.sem_num = 0;
12 op.sem_op = –1;
13 op.sem_flg = 0;
14 if (semop(sem->sem_semid, &op, 1) < 0)
15 return(-1);
16 return(0);
17 }
Функция sem_trywait
В листинге 10.43 приведен текст нашей функции sem_trywait, которая представляет собой неблокируемую версию sem_wait.
13 Единственное отличие от функции sem_wait из листинга 10.42 заключается в том, что флагу sem_flg присваивается значение IPC_NOWAIT. Если операция не может быть завершена без блокирования вызвавшего потока, функция semop возвращает ошибку EAGAIN, а это именно тот код, который должен быть возвращен sem_trywait, если операция не может быть завершена без блокирования потока.
//my_pxsem_svsem/sem_trywait.c
1 #include "unpipc.h"
2 #include "semaphore.h"
3 int
4 mysem_trywait(mysem_t *sem)
5 {
6 struct sembuf op;
7 if (sem->sem_magic != SEM_MAGIC) {
8 errno = EINVAL;
9 return(-1);
10 }
11 op.sem_num = 0;
12 op.sem_op = –1;
13 op.sem_flg = IPC_NOWAIT;
14 if (semop(sem->sem_semid, &op, 1) < 0)
15 return(-1);
16 return(0);
17 }
Функция sem_getvalue
Последняя функция приведена в листинге 10.44. Это функция sem_getvalue, возвращающая текущее значение семафора.
11-14 Текущее значение семафора получается отправкой команды GETVAL функции semctl.
//my_pxsem_svsem/sem_getvalue.с
1 #include "unpipc.h"
2 #include "semaphore.h"
3 int
4 mysem_getvalue(mysem_t *sem, int *pvalue)
5 {
6 int val;
7 if (sem->sem_magic != SEM_MAGIC) {
8 errno = EINVAL;
9 return(-1);
10 }
11 if ((val = semctl(sem->sem_semid, 0, GETVAL)) < 0)
12 return(-1);
13 *pvalue = val;
14 return(0);
15 }
10.17. Резюме
Семафоры Posix представляют собой семафоры-счетчики, для которых определены три основные операции:
1. Создание семафора.
2. Ожидание изменения значения семафора на ненулевое и последующее уменьшение значения.
3. Увеличение значения семафора на 1 и возобновление выполнения всех процессов, ожидающих его изменения.
Семафоры Posix могут быть именованными или неименованными (размещаемыми в памяти). Именованные семафоры всегда могут использоваться отдельными процессами, тогда как размещаемые в памяти должны для этого изначально планироваться как разделяемые между процессами. Эти типы семафоров также отличаются друг от друга по живучести: именованные семафоры обладают по меньшей мере живучестью ядра, тогда как размещаемые в памяти обладают живучестью процесса.
Задача производителей и потребителей является классическим примером для иллюстрации использования семафоров. В этой главе первое решение состояло из одного потока-производителя и одного потока-потребителя; второе решение имело нескольких производителей и одного потребителя, а последнее решение допускало одновременную работу и нескольких потребителей. Затем мы показали, что классическая задача двойной буферизации является частным случаем задачи производителей и потребителей с одним производителем и одним потребителем.