Unix管道 - 从子描述符中的stdin读取数据

时间:2013-01-14 03:19:02

标签: c unix piping

我正在尝试在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)?

1 个答案:

答案 0 :(得分: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
$