Функция sem_wait проверяет значение заданного семафора на положительность, уменьшает его на единицу и немедленно возвращает управление процессу. Если значение семафора при вызове функции равно нулю, процесс приостанавливается, до тех пор пока оно снова не станет больше нуля, после чего значение семафора будет уменьшено на единицу и произойдет возврат из функции. Ранее мы отметили, что операция «проверка и уменьшение» должна быть атомарной по отношению к другим потокам, работающим с этим семафором:
#include
int sem_wait(sem_t *
int sem_trywait(sem_t *
/* Обе функции возвращают 0 в случае успешного завершения. –1 – в случае ошибки */
Разница между sem_wait и sem_trywait заключается в том, что последняя не приостанавливает выполнение процесса, если значение семафора равно нулю, а просто немедленно возвращает ошибку EAGAIN.
Возврат из функции sem_wait может произойти преждевременно, если будет получен сигнал. При этом возвращается ошибка с кодом EINTR.
10.4. Функции sem_post и sem_getvalue
После завершения работы с семафором поток вызывает sem_post. Как мы уже говорили в разделе 10.1, этот вызов увеличивает значение семафора на единицу и возобновляет выполнение любых потоков, ожидающих изменения значения семафора:
#include
int sem_post(sem_t *
int sem_getvalue(sem_t *
/* Обе функции возвращают 0 в случае успешного завершения. –1 – в случае ошибки */
Функция sem_getvalue возвращает текущее значение семафора, помещая его в целочисленную переменную, на которую указывает valp. Если семафор заблокирован, возвращается либо 0, либо отрицательное число, модуль которого соответствует количеству потоков, ожидающих разблокирования семафора.
Теперь мы ясно видим отличия семафоров от взаимных исключений и условных переменных. Прежде всего взаимное исключение может быть разблокировано только заблокировавшим его потоком. Для семафоров такого ограничения нет: один из потоков может ожидать изменения значения семафора, чтобы потом уменьшить его с 1 до 0 (действие аналогично блокированию семафора), а другой поток может изменить значение семафора с 0 до 1, что аналогично разблокированию семафора.
Далее, поскольку любой семафор имеет некоторое значение, увеличиваемое операцией
Наконец, среди всех функций, работающих со средствами синхронизации — взаимными исключениями, условными переменными, блокировками чтения-записи и семафорами, только одна может быть вызвана из обработчика сигналов: sem_post.
ПРИМЕЧАНИЕ
Не следует рассматривать приведенный выше текст как доводы в пользу семафоров. Все средства синхронизации, обсуждаемые в этой книге — взаимные исключения, условные переменные, блокировки чтения-записи, семафоры и блокировка fcntl, обладают своими преимуществами и недостатками. Выбирать средства синхронизации для приложения следует с учетом их многочисленных особенностей. Из нашего сравнительного описания можно сделать вывод, что взаимные исключения больше приспособлены для блокировки, условные переменные — для ожидания, а семафоры — для того и другого, и последнее может привести к излишнему усложнению текста программы.
10.5. Простые примеры
В этом разделе мы напишем несколько простых программ, работающих с именованными семафорами Posix. Эти программы помогут нам узнать особенности функционирования и реализации семафоров. Поскольку именованные семафоры Posix обладают по крайней мере живучестью ядра, для работы с ними мы можем использовать отдельные программы.
Программа semcreate