Пример. Реализация конвейера

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

Сначала идет вызов 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], …))

{

}

}

}

. Конвейер – это две программы (два процесса), которые исполняются параллельно и при этом стандартный вывод первой программы посылается на стандартный ввод второй программы, т.е. по мере того, как 1-й процесс генерирует свой вывод, он сразу же выдается на ввод второму процессу. Реализуется это очень легко с помощью каналов. Первым делом порождается канал, затем происходит порождение процесса потомка. В процессе потомке с помощью системного вызова dup2(), записывающая сторона канала дублируется на стандартный вывод, после чего системный вызов dup2() открывает второй дескриптор и теперь дескриптор с номером 1, описывающий стандартный вывод, будет смотреть в канал (весь вывод будет помешен в канал) после чего закрываются ненужные дескрипторы, записывающий в канал и читающий, и вызывается замена тела процесса на ту программу, которая собственно является первой программой. В процессе отце происходит всё наоборот, здесь в читающей стороне канала открывается второй дескриптор с номером 0 (стандартный ввод), это означает, что в дальнейшем весь стандартный ввод будет браться из канала, происходит тоже самое, закрываются ненужные дескрипторы, записывающий в канал и читающий, и происходит замена тела программы на программу wc.

Пример реализации конвейера print|wc вывод программы print будет подаваться на вход программыwc.Программа printпечатает некоторый текст. Программа wcсчитает количество прочитанных строк, слов и символов.

#include <sys/types.h>

#include <unistd.h>

#include <stdio.h>

int main(int argc, char **argv)

{

int fd[2];

pipe(fd); /*организован канал*/

if (fork())

{

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

dup2(fd[1], 1); /* отождествили стандартный вывод с файловым дескриптором канала, предназначенным для записи */

close(fd[1]); /* закрыли файловый дескриптор канала, предназначенный для записи */

close(fd[0]); /* закрыли файловый дескриптор канала, предназначенный для чтения */

exelp(“print”, ”print”, 0); /* запустили программу print */

}

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

dup2(fd[0], 0); /* отождествили стандартный ввод с файловым дескриптором канала, предназначенным для чтения*/

close(fd[0]); /* закрыли файловый дескриптор канала, предназначенный для чтения */

close(fd[1]); /* закрыли файловый дескриптор канала, предназначенный для записи */

execl(“/usr/bin/wc”, ”wc”, 0); /* запустили программу wc */

}


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: