在C中实现管道:是否需要分叉?

在C中实现管道:是否需要分叉?,c,linux,shell,pipe,dup2,C,Linux,Shell,Pipe,Dup2,我正在尝试用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 prec

我正在尝试用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
,所以替换进程应该不会有问题。

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

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

当您在shell中时,通常会分叉并执行您键入的每个命令。尝试在shell中键入
exec
,然后输入一个命令,您会发现shell在该命令执行完毕后终止,因为在这种情况下它不会分叉

编辑 我建议您看看管道(2)手册页()上的示例。它显示了使用管道的常用方法:

  • 调用
    pipe
    以获取创建管道的命令
  • 调用fork来fork进程
  • 根据它是子管道还是父管道,关闭管道的一端并使用另一端
我认为您的问题可能是,您在分叉之前将管道的写入端设置为标准输出,导致父级和子级都有一个开放的写入端。这可能会阻止发送EOF,因为一个写入端仍处于打开状态


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

什么是
turtle\u execute
?@melpomene它是一个执行参数的函数。如果是内置的,则会编写一个实现,否则我会分叉并执行所需的命令。
grep file | ls | wc
不是一个合理的管道(假设
ls
的实现足够标准);
ls
命令不读取标准输入,因此
grep
部分不会做任何有用的事情。这里没有足够的代码,任何人都无法解释如何使用显示的少量代码。您需要创建一个MCVE()。不清楚创建以连接到进程的管道的写入端如何连接到写入进程的标准输出,或者管道的读取端如何连接到管道中下一个进程的标准输入。用内置的
ls
命令替换如此庞大的代码似乎很有趣。我猜您的实现甚至没有涵盖POSIX
ls
的所有可能性,更不用说GNU了。虽然正式使用管道并不需要fork,但很少有情况下管道到当前流程是有意义的。您可能能够在多线程进程的两个线程之间使用管道,但这充其量只是一种常规。而且,您不太容易获得EOF指示(对于写入线程或读线程,另一个线程必须关闭其管道末端)。所以,当你使用管道时,你通常使用叉子;没有任何东西说你必须使用fork(更不用说你必须使用exec)。