C в Unix: форк, waitpid и трубы
Мой вопрос о том, как контролировать выполнение процесса в отношении каналов, и, в частности, реализацию wait
/ waitpid
функция.
Когда я создаю канал для следующей команды ls | head -3
Я делаю следующее:
- Я создаю трубу, разворачиваю процесс создания ребенка
- Для ребенка я вызываю dup2 для stdin, я выполняю
head -3
команда, закрывающая выходную сторону канала у дочернего элемента - Для родителя я вызываю dup2 для stdout, я выполняю
ls
команда, закрывающая входную сторону канала в родительском
Мой вопрос: исходя из этого обсуждения, мне нужно подождать, пока ребенок завершит выполнение, то есть выполнение head -3
, Но как / где я могу реализовать waitpid
функционировать таким образом, чтобы он не конфликтовал с close[]
команда?
Основываясь на этом великом тексте, описание гласит:
Если родитель хочет получить данные от потомка, он должен закрыть fd1, а потомок должен закрыть fd0. Если родитель хочет отправить данные ребенку, он должен закрыть fd0, а ребенок должен закрыть fd1. Поскольку дескрипторы разделяются между родителем и потомком, мы всегда должны быть уверены, что закрыли конец канала, который нас не касается. С технической точки зрения, EOF никогда не будет возвращен, если ненужные концы канала явно не закрыты.
Таким образом, ребенку придется ждать завершения родительского процесса перед выполнением.
Я также видел примеры, где две вилки сделаны для одного процесса с трубой. Может ли это быть целью избежать процессов зомби, точно так же, как описание в моей копии APUE ch.8?
Рассмотрим следующую реализацию кода:
#include <stdlib.h>
#include <stdio.h>
#include <sys/wait.h>
#include <sys/types.h>
int main()
{
int pid, status;
int fd[2];
char *com1[2];
char *com2[3];
com1[0] = "ls";
com1[1] = NULL;
com2[0] = "head";
com2[1] = "-3";
com2[2] = NULL;
pipe(fd);
if((pid = fork()) == -1)
{
printf("fork error");
exit(1);
}
if(pid == 0)
{
/* Child process closes up output side of pipe */
dup2(fd[0], 0);
close(fd[1]);
execvp(com2[0], com2);
}
else
{
/* if(waitpid(0, WIFEXITED(&status), 0) != pid)
{
printf("wait error");
}
is this even needed here? */
/* Parent process closes up input side of pipe */
dup2(fd[1], 1);
close(fd[0]);
execvp(com1[0], com1);
}
exit(0);
}
Я должен признать, что я просмотрел много подобных вопросов здесь, на StackExchange. Однако почти все они имеют конкретное содержание. Что я ищу, так это объяснения принципов и комбинаций функций. Спасибо за ваше время!
1 ответ
Вы не можете хотеть ждать ребенка и исполнительного. Так что вам нужно иметь двоих детей. Есть два распространенных способа сделать это: либо иметь родительский процесс, который создает двух прямых потомков, и может / должен ждать обоих; или иметь дочерний элемент, который сам создает второго, так что родитель должен ждать только одного завершения процесса.
Вариант 1 (наследственная труба)
pipe(pi);
pid1 = fork();
if (pid1==0) { // child1
// make redirections
exec("head"...);
}
pid2 = fork();
if (pid2==0) { // child2
// make redirections
exec("ls"...);
}
// parent process
// close unuseful pipe
waitpid(pid1...);
waitpid(pid2...);
Вариант 2 (труба видна только заинтересованным процессам)
pid1 = fork();
if (pid1==0) { // child
pipe(pi);
pid2 = fork();
if (pid2==0) { // gran child
// make redirections
exec("ls"...);
}
// make redirections
exec("head"...);
}
// parent process
waitpid(pid1...);