Ядро LINUX
if((childpid = fork()) == -1) { perror("fork"); exit(1); } if(childpid == 0) { /* Потомок закрывает вход */ close(fd[0]); } else { /* Родитель закрывает выход */ close(fd[1]); } . . }
Как было упомянуто ранее, раз канал был установлен, то файловые дескрипторы могут обрабатываться подобно дескрипторам нормальных файлов. /************************************************************************* Excerpt from "Linux Programmer's Guide - Chapter 6" (C)opyright 1994-1995, Scott Burkett ************************************************************************* MODULE: pipe.c *************************************************************************/ #include #include #include int main(void) { int fd[2], nbytes; pid_t childpid; char string[] = "Hello, world!\n"; char readbuffer[80]; pipe(fd); if((childpid = fork()) == -1) { perror("fork"); exit(1); } if(childpid == 0) { /* Потомок закрывает вход */ close(fd[0]); /* Посылаем "string" через выход канала */ write(fd[1], string, strlen(string)); exit(0); } else { /* Родитель закрывает выход */ close(fd[1]); /* Чтение строки из канала */ nbytes = read(fd[0], readbuffer, sizeof(readbuffer)); printf("Received string: %s", readbuffer); } return(0); }
Часто дескрипторы потомка раздваиваются на стандартный ввод или вывод. Потомок может затем exec() другую программу, которая наследует стандартные потоки. Давайте посмотрим на системный вызов dup(): SYSTEM CALL: dup(); PROTOTYPE: int dup( int oldfd ); RETURNS: new descriptor on success -1 on error: errno = EBADF (oldfd некорректен) EBADF ($newfd is out of range$) EMFILE (слишком много дескрипторов для процесса)
NOTES: старый дескриптор не закрыт! Оба работают совместно!
Несмотря на то, что старый и новосозданный дескрипторы взаимозаменяемы, мы будем сначала закрывать один из стандартных потоков. Системный вызов dup() использует наименьший по номеру неиспользуемый дескриптор для нового.
Рассмотрим: . . childpid = fork(); if(childpid == 0) { /* Закрываем стандартный ввод потомка */ close(0); /* Дублируем вход канала на stdin */ dup(fd[0]); execlp("sort", "sort", NULL); . }
Поскольку файловый дескриптор 0 (stdin) был закрыт, вызов dup() дублировал дескриптор ввода канала (fd0) на его стандартный ввод. Затем мы сделали вызов execlp(), чтобы покрыть код потомка кодом программы sort. Поскольку стандартные потоки exec()-нутой программы наследуются от родителей, это означает, что вход канала ста для потомка стандартным вводом! Теперь все, что первоначальный процесс-родитель посылает в канал, идет в sort.
Существует другой системный вызов, dup2(), который также может использоваться. Этот особенный вызов произошел с Version 7 of UNIX и был поддержан BSD, и теперь требуется по стандарту POSIX. SYSTEM CALL: dup2(); PROTOTYPE: int dup2( int oldfd, int newfd ); RETURNS: новый дескриптор в случае успеха -1 в случае ошибки: errno = EBADF (oldfd некорректен) EBADF ($newfd is out of range$) EMFILE (слишком много дескрипторов для процесса) NOTES: старый дескриптор закрыл dup2()!
Благодаря этому особенному вызову мы имеем закрытую операцию и действующую копию за один системный вызов. Вдобавок, он гарантированно неделим, что означает, что он никогда не будет прерван поступающим сигналом. С первым системным вызовом dup() программисты были вынуждены предварительно выполнять операцию close(). Это приводило к наличию двух системных вызовов с малой степенью защищенности в краткий промежуток времени между ними. Если бы сигнал поступил в течение этого интервала времени, копия дескриптора не состоялась бы. dup2() разрешает для нас эту проблему.
Рассмотрим: . . childpid = fork(); if(childpid == 0) { /* Закрываем стандартный ввод, дублируем вход канала на стандартный ввод */ dup2(0, fd[0]); execlp("sort", "sort", NULL); . . }