Как уже обсуждалось, в UNIX процессы выполняются в собственном адресном пространстве и по существу изолированы друг от друга. Тем самым сведены к минимуму возможности влияния процессов друг на друга, что является необходимым в многозадачных операционных системах. Однако от одиночного изолированного процесса мало пользы. Сама концепция UNIX заключается в модульности, т.е. основана на взаимодействии между отдельными процессами.
Для реализации взаимодействия требуется:
□ обеспечить средства взаимодействия между процессами и одновременно
□ исключить нежелательное влияние одного процесса на другой.
Взаимодействие между процессами необходимо для решения следующих задач:
□ Передача данных . Один процесс передает данные другому процессу, при этом их объем может варьироваться от десятков байтов до нескольких мегабайтов.
□ Совместное использование данных . Вместо копирования информации от одного процесса к другому, процессы могут совместно использовать одну копию данных, причем изменения, сделанные одним процессом, будут сразу же заметны для другого. Количество взаимодействующих процессов может быть больше двух. При совместном использовании ресурсов процессам может понадобиться некоторый протокол взаимодействия для сохранения целостности данных и исключения конфликтов при доступе к ним.
□ Извещения . Процесс может известить другой процесс или группу процессов о наступлении некоторого события. Это может понадобиться, например, для синхронизации выполнения нескольких процессов.
Очевидно, что решать данную задачу средствами самих процессов неэффективно, а в рамках многозадачной системы — опасно и потому невозможно. Таким образом, сама операционная система должна обеспечить механизмы межпроцессного взаимодействия (Inter-Process Communication, IPC).
К средствам межпроцессного взаимодействия, присутствующим во всех версиях UNIX, можно отнести:
□ сигналы
□ каналы
□ FIFO (именованные каналы)
□ сообщения (очереди сообщений)
□ семафоры
□ разделяемую память
Последние три типа IPC обычно обобщенно называют System V IPC .
Во многих версиях UNIX есть еще одно средство IPC — сокеты, впервые предложенные в BSD UNIX (им посвящен отдельный раздел главы).
Сигналы изначально были предложены как средство уведомления об ошибках, но могут использоваться и для элементарного IPC, например, для синхронизации процессов или для передачи простейших команд от одного процесса к другому. [42] Например, для сервера системы имен (DNS) named(1M) таким образом используется сигнал SIGHUP , по существу являющийся командой обновления базы данных.
Однако использование сигналов в качестве средства IPC ограничено из-за того, что сигналы очень ресурсоемки. Отправка сигнала требует выполнения системного вызова, а его доставка — прерывания процесса-получателя и интенсивных операций со стеком процесса для вызова функции обработки и продолжения его нормального выполнения. При этом сигналы слабо информативны и их число весьма ограничено. Поэтому сразу переходим к следующему механизму — каналам.
Вспомните синтаксис организации программных каналов при работе в командной строке shell:
cat myfile | wc
При этом (стандартный) вывод программы cat(1) , которая выводит содержимое файла myfile, передается на (стандартный) ввод программы wc(1) , которая, в свою очередь подсчитывает количество строк, слов и символов. В результате мы получим что-то вроде:
12 45 260
что будет означать количество строк, слов и символов в файле myfile.
Таким образом, два процесса обменялись данными. При этом использовался программный канал, обеспечивающий однонаправленную передачу данных между двумя задачами.
Для создания канала используется системный вызов pipe(2) :
int pipe(int* fildes);
который возвращает два файловых дескриптора — fildes[0]
для записи в канал и fildes[1]
для чтения из канала. Теперь, если один процесс записывает данные в fildes[0]
, другой сможет получить эти данные из fildes[1]
. Вопрос только в том, как другой процесс сможет получить сам файловый дескриптор fildes[1]
?
Вспомним наследуемые атрибуты при создании процесса. Дочерний процесс наследует и разделяет все назначенные файловые дескрипторы родительского. То есть доступ к дескрипторам fildes
канала может получить сам процесс, вызвавший pipe(2) , и его дочерние процессы. В этом заключается серьезный недостаток каналов, поскольку они могут быть использованы для передачи данных только между родственными процессами. Каналы не могут использоваться в качестве средства межпроцессного взаимодействия между независимыми процессами.
Читать дальше