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...);
Другие вопросы по тегам