在C中实现管道:是否需要派生?

时间:2018-09-23 20:50:30

标签: c linux shell pipe dup

我正在尝试在C语言中实现Linux管道链。例如:

grep file | ls | wc

因此,有一段代码使用管道作为分隔符将参数拆分为令牌,然后将每个部分发送给以下函数,并使用整数指定其是否在管道之前:

int control_flow(char** args, int precedes){

    int stdin_copy = dup(0);
    int stdout_copy = dup(1);

    // if the command and its args precedes a pipe
    if (precedes){

        int fd[2];

        if (pipe(fd) == -1){
            fprintf(stderr, "pipe failed\n");
        }

        if (dup2(fd[1], 1)!=1)
            perror("dup2 error 1 to p_in\n"); // 1 points to pipe's input

        status = turtle_execute(args); // executes the argument list, output should go into the pipe

       // Code stops running here

        if (dup2(fd[0], 0)!=0)
            perror("dup2 error 0 to p_out\n"); // 0 points to pipe's output, any process that reads next will read from the pipe

        if (dup2(stdout_copy, 1)!=1)
            perror("dup2 error 1 to stdout_copy\n"); // 1 points back to stdout

    }

    // if the command does not precede a pipe
    else{

        status = turtle_execute(args); // input to this is coming from pipe

        if (dup2(stdin_copy, 0)!=0)  // 0 points back to stdin
            perror("dup2 error 1 to stdin_copy");

    }

    return 0;
}

执行第一个命令后,我的代码停止运行。我怀疑在使用此管道之前必须先分叉一个进程,这是为什么呢?如果是这样,如何在我的代码中做到这一点而又不改变我打算做什么?

编辑: 这大致是turtle_execute所做的:

turtle_execute(args){
    if (args[0] is cd or ls or pwd or echo)
         // Implement by calling necessary syscalls
    else
         // Do fork and exec the process

因此,无论我在哪里使用过exec,我都首先使用过fork,因此更换进程应该不是问题。

1 个答案:

答案 0 :(得分:1)

exec系统调用将您正在执行的程序替换为当前进程。因此,turtle_execute之后,您的流程自然会停止工作,因为它已被新流程替换。

要执行新流程,通常需要先创建当前流程的副本,然后在副本中执行。

在外壳程序中时,通常键入的每个命令都会被派生并执行。尝试在外壳程序中键入exec,然后在外壳程序中键入命令,您会发现该命令执行完后外壳会终止,因为在这种情况下它不会分叉。

编辑

我建议您看一下pipe(2)手册页(http://man7.org/linux/man-pages/man2/pipe.2.html#EXAMPLE)上的示例。它显示了使用管道的常用方法:

  • 调用pipe来创建管道
  • 调用fork来分叉进程
  • 根据是孩子还是父母,关闭管道的一端,然后使用另一端

我认为您的问题可能是您在分叉之前将管道的书写端设置为标准输出,从而导致父母和孩子的书写端都处于开放状态。由于一个书写端仍处于打开状态,因此可能会阻止发送EOF。

我只能猜测在大多数turtle_execute中会发生什么,但是如果您分叉,在一个进程上执行exec,然后在另一个进程上等待它,而又不消耗管道中的数据,则可能会填满管道并达到写入的地步被阻止。写入管道时,应始终使用管道中的数据。毕竟是管子,而不是水箱。有关更多信息,请查看“管道容量”部分下的pipe(7)手册页。