UNIX: взаимодействие процессов
Шрифт:
■ Именованные семафоры Posix: следует либо вызывать sem_open из родительского и дочерних процессов по отдельности, либо вызывать sem_open в родительском процессе, учитывая, что семафор станет общим после вызова fork.
■ Семафоры System V: никакого специального кодирования не требуется, поскольку эти семафоры всегда могут использоваться как процессами, так и потоками. Дочерним процессам достаточно знать идентификатор семафора.
■ Блокировка записей fcntl: изначально предназначена для использования несколькими процессами.
Мы приведем код только для программы с взаимными исключениями Posix.
Взаимные исключения Posix между процессами
Функция main первой программы использует взаимное исключение Posix для обеспечения синхронизации. Текст ее приведен в листинге А.32.
19-20 Поскольку мы запускаем несколько процессов, структура shared должна располагаться в разделяемой памяти. Мы вызываем функцию my_shm, текст которой приведен в листинге А.31.
21-26 Поскольку взаимное исключение помещено в разделяемую память, мы не можем статически инициализировать его, поэтому мы вызываем pthread_mutex_init после установки атрибута PTHREAD_PROCESS_SHARED. Взаимное исключение блокируется.
27-36 После создания дочерних процессов и запуска таймера блокировка снимается.
37-43 Родительский процесс ожидает завершения всех дочерних, после чего останавливает таймер.
ПРИЛОЖЕНИЕ Б
Основы многопоточного программирования
Б.1. Введение
В этом приложении приведены основные функции, используемые для работы с потоками. В традиционной модели Unix процесс, которому нужно, чтобы какое-то действие было выполнено не им самим, порождает дочерний процесс вызовом fork. Большая часть сетевых серверов под Unix написана именно так.
Хотя эта парадигма хорошо работала на протяжении многих лет, вызов fork обладает некоторыми недостатками:
■ вызов fork ресурсоемок. Память копируется от родительского процесса к дочернему, копируются все дескрипторы и т. д. Существующие реализации используют метод копирования при записи (copy-on-write), что исключает необходимость копирования адресного пространства родительского процесса, пока оно не понадобится клиенту, но, несмотря на эту оптимизацию, вызов fork остается ресурсоемким;