Чтение онлайн

ЖАНРЫ

UNIX: взаимодействие процессов

Стивенс Уильям Ричард

Шрифт:
Блокировка записей 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 определен) Обязателен
Память с общим доступом System V (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), которые осуществляют собственно вызов функции, проверяют возвращаемое значение и завершают работу при возникновении ошибок. В соответствии с соглашениями имена функций-оберток совпадают с именами самих функций, за исключением первой буквы, которая делается заглавной, например

Sem_post(ptr);

Пример функции-обертки приведен в листинге 1.1 [1]

Листинг 1.1. Функция-обертка к функции sem_post

// lib/wrapunix.c

387 void

388 Sem_post(sem_t *sem)

389 {

390 if (sem_post(sem) == –1)

391 err_sys("sem_post error");

392 }

Если в тексте вы встретите имя функции, начинающееся с заглавной буквы, знайте: это наша собственная функция-обертка. Она вызывает функцию с тем же именем, начинающимся со строчной буквы. Функция-обертка приводит к завершению работы процесса с выводом сообщения об ошибке, если таковая возникает.

1

Все исходные тексты, опубликованные в этой книге, вы можете найти по адресу http://www.piter.com/download.

При описании исходного кода, включенного в книгу, мы всегда говорим о вызываемой функции самого низкого уровня (например, sem_post), а не о функции-обертке (например, Sem_post). Аналогично в алфавитном указателе приведены имена самих функций, а не оберток к ним.

ПРИМЕЧАНИЕ

Вышеприведенный формат исходного кода используется во всем тексте. Все непустые строки нумеруются. Текст, описывающий разделы кода, начинается с номеров первой и последней строк на пустом поле слева. Иногда перед абзацем текста присутствует краткий заголовок, набранный полужирным шрифтом, излагающий основное содержание описываемого кода.

В начале кода указывается имя исходного файла. В данном примере — это файл wrapunix.c в каталоге lib. Поскольку исходный код всех примеров этой книги распространяется свободно (см. предисловие), вы можете легко найти требуемый файл. Компиляция, выполнение и особенно изменение этих программ в процессе чтения книги — лучший способ изучить концепции взаимодействия процессов.

Хотя может показаться, что использовать такие функции-обертки не слишком выгодно, вы избавитесь от этого заблуждения в главе 7, где мы обнаружим, что функции для работы с потоками (thread functions) не присваивают значение стандартной переменной Unix errno при возникновении ошибки; вместо этого код ошибки просто возвращается функцией. Это означает, что при вызове функции pthread мы должны каждый раз выделять память под переменную, сохранять в ней возвращаемое функцией значение, а затем устанавливать значение переменной errno равным этой переменной, прежде чем вызывать функцию err_sys (листинг В.4). Чтобы не загромождать текст фигурными скобками, мы используем оператор языка Си «запятая» (comma) и совмещаем присваивание значения переменной errno и вызов err_sys в одном операторе, как в нижеследующем примере:

int n;

if ((n = pthread_mutex_lock(&ndone_mutex))!=0) errno=n, err_sys("pthread_mutex_lock error");

Альтернативой является определение новой функции обработки ошибок, принимающей код ошибки в качестве аргумента. Однако мы можем сделать этот фрагмент кода гораздо более читаемым, записав

Pthread_mutex_lock(&ndone_mutex);

где используется наша собственная функция-обертка, приведенная в листинге 1.2.

Листинг 1.2. Реализация обертки к функции pthread_mutex_lock
Поделиться с друзьями: