3.3 Программные каналы
Если два или несколько процессов совместно выполняют одну и ту же задачу, то они неизбежно должны использовать общие данные. Одним из способов организации взаимодействия процессов является создание программного канала (pipe).
Программный канал (или просто канал) служит для установления односторонней связи, соединяющей один процесс с другим. Процесс может посылать данные в канал при помощи системного вызова write, а другой процесс может принимать данные из канала при помощи системного вызова read.
Каналы обращаются с данными в порядке «первый вошел – первым вышел» (first-in first-out, или сокращенно FIFO). Другими словами, данные, которые помещаются в канал первыми, первыми и считываются на другом конце канала. Этот порядок нельзя изменить, поскольку вызов lseek не работает с каналами.
Таким образом, можно представлять себе канал как безымянный файл с необычным поведением. Работа с таким файлом напоминает проталкивание шаров через трубу - с одного конца мы вталкиваем данные, с другого конца - вынимаем их.
Канал создается системным вызовом pipe:
#include <unistd.h>
int pipe(int array[2]);
Системный вызов pipe() создает программный канал и присоединяет к двум концам канала два файловых дескриптора. Файловый дескриптор array[0] используется на том конце канала, откуда производится чтение, а файловый дескриптор array[1] присоединяется к концу канала, куда производится запись данных. Внутреннее устройство канала, как и внутреннее устройство открытого файла, скрывается ядром. Процесс видит только два файловых дескриптора.
Для создания двух процессов, которые взаимодействуют между собой через программный канал, нужно совместно использовать вызовы pipe() и fork(). При вызове fork() каждому из двух процессов достанется в наследство пара дескрипторов array[0] и arrray[1], обеспечивающих соединение с программным каналом. Оба процесса имеют возможность записывать и читать данные из канала. Однако программный канал будет использоваться более эффективно, если один процесс записывает данные в канал, а другой процесс читает данные из канала. Для этого, каждый процесс должен выполнять либо чтение из канала, либо запись в него и закрывать дескриптор файла, как только он стал не нужен. Для этого, процесс, выполняющий запись в канал, должен закрывать дескриптор читаемого конца канала array[0], а процесс, осуществляющий чтение, должен закрывать дескриптор записываемого конца канала array[1].
4 Пример программы
Слово состояния устройства в системе ввода-вывода представляется в виде:
№ разряда |
15 |
14 |
13 |
12 |
11 |
10 |
09 |
08 |
07 |
06 |
05 |
04 |
03 |
02 |
01 |
00 |
Значение |
C |
C |
C |
C |
C |
0 |
F |
B |
N |
N |
N |
N |
N |
N |
N |
N |
где: C..C - код состояния
F - признак ошибки (1/0)
B - признак занятости (0/1)
N..N - количество байт, переданных в последней операции.
Программа создает два процесса, взаимодействующих через канал. Дочерний процесс в цикле генерирует составные части слова состояния (код состояния, признак ошибки, признак занятости, количество байт) и помещает набор этих чисел в канал. Таким образом создается и записывается в канал заданное количество наборов чисел. Пока в канал поступают данные, родительский процесс читает набор чисел из канала и выводит прочитанные значения на экран.
При вызове программы в командной строке нужно указать количество генерируемых наборов чисел.
Текст программы приведен в приложении.
Пример работы программы:
$ ./lr41 2
Родительский процесс pid=4762
Порожденный процесс pid=4763
набор отправлен в канал
набор принят из канала
Код состояния = 2
Признак ошибки = 1
Признак занятости = 0
Количество переданных байт = 253
набор отправлен в канал
набор принят из канала
Код состояния = 25
Признак ошибки = 0
Признак занятости = 0
Количество переданных байт = 176