2

我正在尝试在 c 中实现 unix 管道(即执行 ls | wc)。我找到了解决我的问题的相关解决方案(C Unix Pipes Example),但是我不确定为什么解决的代码片段的特定部分有效。

这是代码:

/* Run WC. */
int filedes[2];
pipe(filedes);

/* Run LS. */
pid_t pid = fork();
if (pid == 0) {
    /* Set stdout to the input side of the pipe, and run 'ls'. */
    dup2(filedes[1], 1);
    char *argv[] = {"ls", NULL};
    execv("/bin/ls", argv);
 } else {
    /* Close the input side of the pipe, to prevent it staying open. */
     close(filedes[1]);
 }

 /* Run WC. */
 pid = fork();
 if (pid == 0) {
      dup2(filedes[0], 0);
      char *argv[] = {"wc", NULL};
      execv("/usr/bin/wc", argv);
  }

在执行 wc 命令的子进程中,虽然它将stndin 附加到文件描述符,但似乎我们并没有显式读取第一个子进程中 ls 产生的输出。因此,在我看来,ls 似乎是独立运行的,而 wc 是独立运行的,因为我们在执行 wc 时没有明确使用 ls 的输出。那么这段代码是如何工作的(即它执行 ls | wc)?

4

1 回答 1

3

显示的代码几乎可以工作(它削减了许多角落,但它有效),因为分叉的孩子确保执行进程将写入(在 的情况下ls)和读取的文件描述符(在 的情况下wc)是管道的适当端。您不必再做任何事情了;标准输入是文件描述符 0,因此wc从标准输入读取没有(文件名)参数。 ls始终写入标准输出,文件描述符 1,除非它正在写入错误消息。

代码片段中有三个进程;父进程和两个子进程,每个进程一个fork()。父进程也应该关闭管道的两端;它只关闭一个。

通常,在对管道文件描述符执行dup()或调用之后,应该关闭管道的两端。dup2()您在这里侥幸逃脱,因为ls生成数据并终止;你不会在所有情况下。

评论:

/* Set stdout to the input side of the pipe, and run 'ls'. */

不准确;您正在设置stdout到管道的输出端,而不是输入端。

execv()调用后您应该有一个错误退出;如果它们失败,它们会返回,并且该过程可能会造成严重破坏(例如,如果ls失败,您最终会得到两个wc运行的副本。

一个SSCCE

请注意在每个过程中仔细关闭管道的两端。一旦启动了两个子进程,父进程就不再使用管道。我将早期关闭的代码留filedes[1]在原地(但将其从显式else块中删除,因为以下代码也仅在执行时才else执行)。我很可能closes()在需要关闭文件的三个代码路径中的每一个中都保留了对。

#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>

int main(void)
{
    int filedes[2];
    int corpse;
    int status;
    pipe(filedes);

    /* Run LS. */
    pid_t pid = fork();
    if (pid == 0)
    {
        /* Set stdout to the output side of the pipe, and run 'ls'. */
        dup2(filedes[1], 1);
        close(filedes[1]);
        close(filedes[0]);
        char *argv[] = {"ls", NULL};
        execv("/bin/ls", argv);
        fprintf(stderr, "Failed to execute /bin/ls\n");
        exit(1);
    }
    /* Close the input side of the pipe, to prevent it staying open. */
    close(filedes[1]);

    /* Run WC. */
    pid = fork();
    if (pid == 0)
    {
        /* Set stdin to the input side of the pipe, and run 'wc'. */
        dup2(filedes[0], 0);
        close(filedes[0]);
        char *argv[] = {"wc", NULL};
        execv("/usr/bin/wc", argv);
        fprintf(stderr, "Failed to execute /usr/bin/wc\n");
        exit(1);
    }

    close(filedes[0]);

    while ((corpse = waitpid(-1, &status, 0)) > 0)
        printf("PID %d died 0x%.4X\n", corpse, status);
    return(0);

}

示例输出:

$ ./pipes-14312939
      32      32     389
PID 75954 died 0x0000
PID 75955 died 0x0000
$
于 2013-01-14T03:51:25.240 回答