UNIX: взаимодействие процессов
Шрифт:
Обратите внимание, что ни один тип IPC в этой таблице не обладает живучестью, определяемой файловой системой. Мы уже упомянули о том, что три типа объектов IPC в стандарте Posix могут иметь этот тип живучести в зависимости от реализации. Очевидно, что запись данных в файл обеспечивает живучесть, определяемую файловой системой, но обычно IPC таким образом не реализуются. Большая часть объектов IPC не предназначена для того, чтобы существовать и после перезагрузки, потому что ее не переживают процессы. Требование живучести, определяемой файловой системой, скорее всего, снизит производительность данного типа IPC, а обычно одной из задач разработчика является именно обеспечение высокой производительности.
1.4. Пространства имен
Если два неродственных процесса используют какой-либо вид IPC для обмена информацией, объект IPC должен иметь имя или идентификатор, чтобы один из процессов (называемый обычно сервером — server) мог создать этот объект, а другой процесс (обычно один или несколько клиентов — client) мог обратиться к этому конкретному объекту.
Программные каналы (pipes) именами не обладают (и поэтому не могут использоваться для взаимодействия между неродственными процессами), но каналам FIFO сопоставляются имена в файловой системе, являющиеся их идентификаторами (поэтому каналы FIFO могут использоваться для взаимодействия неродственных процессов). Для других типов IPC,
В табл. 1.2 сведены соглашения об именовании для различных видов IPC.
Таблица 1.2. Пространства имен для различных типов IPC
Тип IPC | Пространство имен для создания или открытия | Идентификатор после открытия | Posix.1 1996 | Unix 98 |
---|---|---|---|---|
Канал | (Без имени) | Дескриптор | • | • |
FIFO | Имя файла (pathname) | Дескриптор | • | • |
Взаимное исключение Posix | (Без имени) | Указатель типа pthread_mutex_t | • | • |
Условная переменная Posix | (Без имени) | Указатель типа pthread_cond_t | • | • |
Блокировка чтения-записи Posix | (Без имени) | Указатель типа pthread_rwlock_t | • | |
Блокировка записей fcntl | Имя файла | Дескриптор | • | • |
Разделяемая память Posix | Posix-имя IPC | Дескриптор | • | • |
Очередь сообщений System V | Ключ key_t | Идентификатор IPC System V | • | |
Семафор System V | Ключ key_t | Идентификатор IPC System V | • | |
Разделяемая память System V | Ключ key_t | Идентификатор IPC System V | • | |
Двери (doors) | Имя файла | Дескриптор | ||
Удаленный вызов процедур (RPC) Sun | Программа/версия | Дескриптор (handle) RPC | ||
Сокет TCP | IP-адрес и порт TCP | Дескриптор | .1g | • |
Сокет UDP | IP-адрес и порт TCP | Дескриптор | .1g | • |
Доменный сокет Unix (domain socket) | Полное имя файла | Дескриптор | .1g | • |
Здесь также указано, какие формы IPC содержатся в стандарте Posix.1 1996 года и какие были включены в стандарт Unix 98. Об обоих этих стандартах более подробно рассказано в разделе 1.7. Для сравнения мы включили в эту таблицу три типа сокетов, которые подробно описаны в [24]. Обратите внимание, что интерфейс сокетов (Application Program Interface — API) стандартизируется рабочей группой Posix.1g и должен в будущем стать частью стандарта Posix.1.
Хотя стандарт Posix. 1 и дает возможность использования семафоров, их поддержка не является обязательной для производителей. В табл. 1.3 сведены функции, описанные в стандартах Posix.1 и Unix 98. Каждая функция может быть обязательной (mandatory), неопределенной (not defined) или необязательной (дополнительной — optional). Для необязательных функций мы указываем имя константы (например, _POSIX_THREADS), которая будет определена (обычно в заголовочном файле <unistd.h>), если эта функция поддерживается. Обратите внимание, что Unix 98 содержит в себе Posix.1 в качестве подмножества.
Таблица 1.3. Доступность различных форм IPC
Тип IPC | Posix.1 1996 | Unix 98 |
---|---|---|
Программный канал | Обязателен | Обязателен |
FIFO | Обязателен | Обязателен |
Взаимное исключение Posix | _POSIX_THREADS | Обязателен |
Условная переменная Posix | _POSIX_THREADS | Обязателен |
Взаимные исключения и условные переменные между процессами | _POSIX_THREADS_PROCESS_SHARED | Обязателен |
Блокировка чтения-записи Posix | (He определен) | Обязателен |
Блокировка записей fcntl | Обязателен | Обязателен |
Очередь сообщений Posix | _POSIX_MESSAGE_PASSING | _XOPEN_REALTIME |
Семафоры Posix | _POSIX_SEMAPHORES_ | _XOPEN_REALTIME |
Память с общим доступом Posix | _POSIX_SHARED_MEMORY_OBJECTS | _XOPEN_REALTIME |
Очередь сообщений System V | (He определен) | Обязателен |
Семафор System V | (He определен) | Обязателен |
Память
| (He определен) | Обязателен |
Двери (doors) | (He определен) | (Не определен) |
Удаленный вызов процедур Sun | (He определен) | (Не определен) |
Отображение памяти mmap | _POSIX_MAPPED_FILES или POSIX_SHARED_MEMORY_OBJECTS | Обязателен |
Сигналы реального времени (realtime signals) | _POSIX_REALTIME_SIGNALS | _XOPEN_REALTIME |
1.5. Действие команд fork, exec и exit на объекты IPC
Нам нужно достичь понимания действия функций fork, exec и _exit на различные формы IPC, которые мы обсуждаем (последняя из перечисленных функций вызывается функцией exit). Информация по этому вопросу сведена в табл. 1.4.
Большинство функций описаны далее в тексте книги, но здесь нужно сделать несколько замечаний. Во-первых, вызов fork из многопоточного процесса (multithreaded process) приводит к беспорядку в безымянных переменных синхронизации (взаимных исключениях, условных переменных, блокировках и семафорах, хранящихся в памяти). Раздел 6.1 книги [3] содержит необходимые детали. Мы просто отметим в добавление к таблице, что если эти переменные хранятся в памяти с общим доступом и создаются с атрибутом общего доступа для процессов, они будут доступны любому процессу, который может обращаться к этой области памяти. Во-вторых, три формы IPC System V не могут быть открыты или закрыты. Из листинга 6.6 и упражнений 11.1 и 14.1 видно, что все, что нужно знать, чтобы получить доступ к этим трем формам IPC, — это идентификатор. Поэтому они доступны всем процессам, которым известен этот идентификатор, хотя для семафоров и памяти с общим доступом требуется некая особая обработка.
Таблица 1.4. Действие fork, exec и _exit на IPC
Тип IPC | fork | exec | _exit |
---|---|---|---|
Неименованные и именованные каналы | Порожденный процесс получает копии всех дескрипторов родительского процесса | Все открытые дескрипторы остаются открытыми, если для них не установлен бит FD_CLOEXEC | Все открытые дескрипторы закрываются, данные из программного канала и FIFO удаляются после последнего закрытия |
Очереди сообщений Posix | Порожденный процесс получает копии всех открытых родительских процессов | Все открытые дескрипторы очередей сообщений закрываются | Все открытые дескрипторы очередей сообщений закрываются |
Очереди сообщений System V | Не действует | Не действует | Не действует |
Взаимные исключения и условные переменные Posix | Общий доступ, если используется разделяемая память с атрибутом разделения между процессами | Исчезает, если не хранится в разделяемой памяти, которая остается открытой и имеет атрибут разделения | Исчезает, если не находится в разделяемой памяти, которая остается открытой и имеет атрибут разделения |
Блокировки чтения-записи Posix | Общий доступ, если используется память с общим доступом и атрибутом разделения между процессами | Исчезает, если не хранится в разделяемой памяти, которая остается открытой и имеет атрибут разделения | Исчезает, если не хранится в разделяемой памяти, которая остается открытой и имеет атрибут разделения |
Семафоры Posix, хранящиеся в памяти | Общий доступ, если используется память с общим доступом и атрибутом разделения между процессами | Исчезает, если не хранится в разделяемой памяти, которая остается открытой и имеет атрибут разделения | Исчезает, если не хранится в разделяемой памяти, которая остается открытой и имеет атрибут разделения |
Именованные семафоры Posix | Все открытые в родительском процессе остаются открытыми в порожденном | Все открытые закрываются | Все открытые закрываются |
Семафоры System V | Все значения semadj в порожденном процессе устанавливаются в 0 | Все значения semadj передаются новой программе | Все значения semadj добавляются к значению соответствующего семафора |
Блокировка записей fcntl | Блокировки в родительском процессе не наследуются порожденным процессом | Блокировки не изменяются до тех пор, пока не закроется дескриптор | Все несброшенные блокировки, установленные процессом, снимаются |
Отображение памяти | Отображения памяти родительского процесса сохраняются в порожденном | Отображения памяти сбрасываются (unmap) | Отображения памяти сбрасываются |
Разделяемая память Posix | Отображения памяти родительского процесса сохраняются в порожденном | Отображения памяти сбрасываются | Отображения памяти сбрасываются |
Разделяемая память System V | Присоединенные сегменты разделяемой памяти остаются присоединенными в порожденном процессе | Присоединенные сегменты разделяемой памяти отсоединяются | Присоединенные сегменты разделяемой памяти отсоединяются |
Двери (doors) | Порожденный процесс получает копии всех открытых дескрипторов родительского процесса, но только родительский процесс является сервером при активизации дверей через дескрипторы | Все дескрипторы дверей должны быть закрыты, потому что они создаются с установленным битом FD_CLOEXEC | Все открытые дескрипторы закрываются |
1.6. Обработка ошибок: функции-обертки
В любой реальной программе при любом вызове требуется проверка возвращаемого значения на наличие ошибки. Поскольку обычно работа программ при возникновении ошибок завершается, мы можем сократить объем текста, определив функции-обертки (wrapper functions), которые осуществляют собственно вызов функции, проверяют возвращаемое значение и завершают работу при возникновении ошибок. В соответствии с соглашениями имена функций-оберток совпадают с именами самих функций, за исключением первой буквы, которая делается заглавной, например