UNIX: взаимодействие процессов
Шрифт:
19-25 Мы создаем три семафора, передавая их имена функции px_ipc_name. Флаг O_EXCL мы указываем, для того чтобы гарантировать инициализацию каждого семафора правильным значением. Если после преждевременно завершенного предыдущего запуска программы остались неудаленные семафоры, мы обработаем эту ситуацию, вызвав перед их созданием sem_unlink и игнорируя ошибки. Мы могли бы проверять возвращение ошибки EEXIST при вызове sem_open с флагом O_EXCL, а затем вызывать sem_unlink и еще раз sem_open, но это усложнило бы программу. Если нам нужно проверить, что запущен только один экземпляр программы (что следует сделать перед созданием семафоров),
26-29 Создаются два потока, один из которых является производителем, а другой — потребителем. При запуске никакие аргументы им не передаются.
30-36 Главный поток ждет завершения работы производителя и потребителя, а затем удаляет три семафора.
ПРИМЕЧАНИЕ
Мы могли бы вызвать для каждого семафора sem_close, но это делается автоматически при завершении процесса. А вот удалить имя семафора из файловой системы необходимо явно.
В листинге 10.9 приведен текст функций produce и consume.
44 Производитель вызывает sem_wait для семафора nempty, ожидая появления свободного места. В первый раз при выполнении этой команды значение семафора nempty уменьшится с NBUFF до NBUFF-1.
45-48 Перед помещением нового элемента в буфер производитель должен установить блокировку на семафор mutex. В нашем примере, где производитель просто сохраняет значение в элементе массива с индексом i % NBUFF, для описания состояния буфера не используется никаких разделяемых переменных (то есть мы не используем связный список, который нужно было бы обновлять каждый
раз при помещении элемента в буфер). Следовательно, установка и снятие семафора mutex не являются обязательными. Тем не менее мы иллюстрируем эту технику, потому что обычно ее применение является необходимым в задачах такого рода (обновление буфера, разделяемого несколькими потоками).После помещения элемента в буфер блокировка с семафора mutex снимается (его значение увеличивается с 0 до 1) и увеличивается значение семафора nstored. Первый раз при выполнении этой команды значение nstored изменится с начального значения 0 до 1.
57-62 Если значение семафора nstored больше 0, в буфере имеются объекты для обработки. Потребитель изымает один элемент из буфера и проверяет правильность его значения, защищая буфер в момент доступа к нему с помощью семафора mutex. Затем потребитель увеличивает значение семафора nempty, указывая производителю на наличие свободных полей.
Зависание
Что произойдет, если мы по ошибке поменяем местами вызовы Sem_wait в функции consumer (листинг 10.9)? Предположим, что первым запускается производитель (как в решении, предложенном для упражнения 10.1). Он помещает в буфер NBUFF элементов, уменьшая значение семафора nempty от NBUFF до 0 и увеличивая значение семафора nstored от 0 до NBUFF. Затем производитель блокируется в вызове Sem_wait(shared. nempty), поскольку буфер полон и помещать элементы больше некуда.
Запускается потребитель и проверяет первые NBUFF элементов буфера. Это уменьшает значение семафора nstored от NBUFF до 0 и увеличивает значение семафора nempty от 0 до NBUFF. Затем потребитель блокируется в вызове Sem_wait(shared, nstored) после вызова Sem_wait(shared, mutex). Производитель мог бы продолжать работу, поскольку значение семафора nempty уже отлично от 0, но он вызвал Sem_wait(shared, mutex) и его выполнение было приостановлено.
Это называется зависанием программы (deadlock). Производитель ожидает освобождения семафора mutex, а потребитель не снимает с него блокировку, ожидая освобождения семафора nstored. Но производитель не может изменить nstored, пока он не получит семафор mutex. Это одна из проблем, которые часто возникают с семафорами: если в программе сделать ошибку, она будет работать неправильно.
ПРИМЕЧАНИЕ
Стандарт Posix позволяет функции sem_wait обнаруживать зависание и возвращать ошибку EDEADLK, но ни одна из систем, использовавшихся для написания примеров (Digital Unix 4.0B и Solaris 2.6), не обнаружила ошибку в данном случае.
10.7. Блокирование файлов
Вернемся к задаче о порядковом номере из главы 9. Здесь мы напишем новые версии функций my_lock и my_unlосk, использующие именованные семафоры Posix. В листинге 10.10 приведен текст этих функций.