Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Конспект Граур.doc
Скачиваний:
59
Добавлен:
14.11.2019
Размер:
3.7 Mб
Скачать

Пример. Использование канала.

Процесс посылает данные самому себе. Описан массив из двух целых чисел, который передается в функцию pipe (в системный вызов pipe), pipe его заполнил. Далее, используя нулевой дескриптор, осуществляем чтение из канала, используя первый – запись. Понятно, здесь просто строчка записывается, потом считывается. Затем закрываются оба дескриптора из pipes. Прочитанная строка записывается на стандартный вывод и после этого программа завершается. Пример условный, потому что в рамках одного процесса каналы никто не использует.

#include <unistd.h>

#include <stdio.h>

int main(int argc, char **argv)

{

char *s = ”chanel”;

char buf[80];

int pipes[2];

pipe(pipes);

write(pipes[1], s, strlen(s) + 1);

read(pipes[0], buf, strlen(s) + 1);

close(pipes[0]);

close(pipes[1]);

printf(“%s\n”, buf);

return 0;

}

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

Пример. Схема взаимодействия процессов с использованием канала.

Сначала идет вызов pipe, затем fork(), благодаря которому образуется несколько процессов, и соответственно внутри if (fork()) процесс-отец, он закрывает дескриптор чтения и пользуется только записывающей стороной. В сыне происходит, соответственно, происходит все наоборот, он закрывает дескриптор записи и осуществляет чтение из канала. Как правило, канал используется как однонаправленное средство, т.е. данные будут передвигаться только в одном направлении, в данном случае от отца к сыну: отец записывает данные – сын их читает в том же порядке, в котором их записал отец. Обратите внимание на закрывание дескрипторов. Понятно, что это, в принципе, не нужно, поскольку если программа будет завершена, то все дескрипторы и так закроются. Но в данном случае эта строка имеет очень важный смысл: ранее уже говорилось о том, что при попытке чтения большего числа байт из канала, чем в нем находится, чтение будет заблокировано в том случае, если в канале не находится символ EOF, который туда попадает, когда закрывается последний записывающий дескриптор. Последний записывающий дескриптор будет закрыт тогда, когда его закроет процесс-отец, т.е. он запишет все необходимые данные, после чего закроет дескриптор, и собственно это будет означать, что больше данных не будет. В случае, если сын не закроет свой унаследованный дескриптор записи, то после того, как отец закроет свой дескриптор записи останется еще один записывающий дескриптор к тому же каналу в процессе сыне. Хоть он его и не использует (но системе-то это неизвестно), в последнем чтении процесс-сын зациклится, т.к. символ EOF в канал не попадает (поскольку в момент закрытия записывающей стороны это не последний записывающий дескриптор). Поэтому ненужные дескрипторы записи в канал важно обязательно закрывать, потому что иначе последнее чтение из канала будет заблокировано навечно.

#include <sys/types.h>

#include <unistd.h>

int main(int argc, char **argv)

{

int fd[2];

pipe(fd);

if (fork())

{/*процесс-родитель*/

close(fd[0]); /* закрываем ненужный дескриптор */

write (fd[1], …);

close(fd[1]);

}

else

{/*процесс-потомок*/

close(fd[1]); /* закрываем ненужный дескриптор */

while(read (fd[0], …))

{

}

}

}