C 使用管道()系统调用

C 使用管道()系统调用,c,posix,piping,C,Posix,Piping,我一直在尝试使用pipe()系统调用来创建一个支持管道的shell(使用任意数量的命令) 不幸的是,我在使用pipe()时运气不太好。在花了几天时间查看各种在线资源后,我决定编写一个过于简化的程序,该程序的效果与执行ls | sort的效果相同,以查看是否可以为两个兄弟进程、子进程使用管道。代码如下: #include <sys/wait.h> #include <unistd.h> void run(char *cmd) { char *args[2];

我一直在尝试使用pipe()系统调用来创建一个支持管道的shell(使用任意数量的命令)

不幸的是,我在使用pipe()时运气不太好。在花了几天时间查看各种在线资源后,我决定编写一个过于简化的程序,该程序的效果与执行
ls | sort
的效果相同,以查看是否可以为两个兄弟进程、子进程使用管道。代码如下:

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

void run(char *cmd) {
   char *args[2];
   args[0] = cmd;
   args[1] = NULL;

   execvp(cmd, args);
}

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

    pid_t pid_a, pid_b;

    if (!(pid_a = fork())) {
        dup2(filedes[1], 1);
        run("ls");
    }

    if (!(pid_b = fork())) {
        dup2(filedes[0], 0);
        run("sort");
    }

    waitpid(pid_a, NULL, 0);
    waitpid(pid_b, NULL, 0);

    return 0;
}
#包括
#包括
无效运行(char*cmd){
char*args[2];
args[0]=cmd;
args[1]=NULL;
execvp(cmd,args);
}
内部主(空){
int filedes[2];
管道(filedes);
pid_t pid_a,pid_b;
如果(!(pid_a=fork()){
dup2(filedes[1],1);
运行(“ls”);
}
如果(!(pid_b=fork()){
dup2(filedes[0],0);
运行(“排序”);
}
waitpid(pid_a,NULL,0);
waitpid(pid_b,NULL,0);
返回0;
}
管道是在父进程中创建的,我知道在execvp()调用之后,每个子进程都会继承管道()在父进程中创建的文件描述符。对于
ls
进程,我使用dup2()将其标准输出(1)重定向到管道的写入端,对于
sort
进程,将标准输入(0)重定向到管道的读取端

最后,我等待这两个进程完成,然后退出

我的直觉告诉我这应该行得通,但是不行


有什么建议吗?

您需要(至少)关闭父进程中管道的写入端。否则,管道的读取端将永远不会读取EOF状态,
sort
将永远不会完成。

您必须关闭不使用的管道。 至少
sort
将从其stdin读取数据,直到stdin关闭

在本例中,它的stdin永远不会关闭,因为您仍然有两个打开的文件描述符

  • ls子级中的filedes[0](在ls完成时可能会关闭)
  • 父程序中的filedes[0](当您等待排序结束的PID()时,它永远不会关闭,但它永远不会关闭,因为父程序将其stdin保持打开状态)
将程序更改为

if (!(pid_a = fork())) {
    dup2(filedes[1], 1);
    closepipes(filedes);
    run("ls");
}

if (!(pid_b = fork())) {
    dup2(filedes[0], 0);
    closepipes(filedes);
    run("sort");
}
closepipe(filedes);
waitpid(pid_a, NULL, 0);
waitpid(pid_b, NULL, 0);
闭式管道是什么样子的

void closepipes(int *fds)
{ 
 close(fds[0]);
 close(fds[1]);
}

在父进程中调用
waitpid
之前,必须关闭管道中所有不需要的文件描述符。这些是:

  • pid\u a中的filedes[0]
  • pid\b中的filedes[1]
  • 父进程中的
    filedes[0]
    filedes[1]

您还应该检查
pipe()
fork()
是否没有返回
-1
,这意味着发生了错误。

此代码工作正常

    #include <sys/wait.h>
    #include <unistd.h>
    using namespace std;

    void run(char *cmd) {
       char *args[2];
       args[0] = cmd;
       args[1] = NULL;

       execvp(cmd, args);
    }
    void closepipe(int *fds)
    { 
     close(fds[0]);
     close(fds[1]);
    }

    int main(int argc,char *argv[]) {

        int filedes[2];
        pipe(filedes);
        char lss[]="ls";
        char sorts[]="sort";
        pid_t pid_a, pid_b;
     chdir(argv[1]);
    if (!(pid_a = fork())) {
        dup2(filedes[1], 1);
        closepipe(filedes);
        run(lss);
    }

    if (!(pid_b = fork())) {
        dup2(filedes[0], 0);
        closepipe(filedes);
        run(sorts);
    }
    closepipe(filedes);
    waitpid(pid_a, NULL, 0);
    waitpid(pid_b, NULL, 0);

        return 0;
    }
#包括
#包括
使用名称空间std;
无效运行(char*cmd){
char*args[2];
args[0]=cmd;
args[1]=NULL;
execvp(cmd,args);
}
空心闭合管(int*fds)
{ 
关闭(fds[0]);
关闭(fds[1]);
}
int main(int argc,char*argv[]){
int filedes[2];
管道(filedes);
字符lss[]=“ls”;
字符排序[]=“排序”;
pid_t pid_a,pid_b;
chdir(argv[1]);
如果(!(pid_a=fork()){
dup2(filedes[1],1);
封闭管道(filedes);
运行(lss);
}
如果(!(pid_b=fork()){
dup2(filedes[0],0);
封闭管道(filedes);
运行(排序);
}
封闭管道(filedes);
waitpid(pid_a,NULL,0);
waitpid(pid_b,NULL,0);
返回0;
}

发生了什么?你能解释一下为什么会发生这种情况吗?是的,父进程阻塞了,我怀疑这是由于一个或多个子进程由于在其标准输入流上没有接收到EOF字符而从未完成。我在执行任何分叉之前尝试关闭写入端(
close(filedes[1])
)然而,现在我的程序退出了,第二个子进程似乎没有在管道的读取端接收任何东西。我尝试用
wc
替换
sort
,结果是
0
,这表明第二个进程在读取端没有接收到任何内容。。。。看来我应该在等待之前,而不是在分岔之前,把管道的末端封闭起来。根据下面的建议,我还关闭了子进程中不需要的端点,现在它可以工作了!在实际情况中,我检查错误——我把它们忘在这里了,所以示例可能很简单。太好了,这很有效!我还有两个问题:(1)我假设一个文件描述符在所有引用它的进程调用close()时是真正关闭的,而不仅仅是一个?(2)我们只需要关闭PIDYA的读结束,因为它是“链”中的第一个进程,我们关闭pIDub的写结束,因为它是链中的最后一个进程,但是如果中间有进程,它们将使用管道的两端,所以这些进程的两端都不会被关闭,对吗?(1)这是正确的。语句(2)稍微复杂一点。无论何时
fork()
,所有文件描述符都是重复的,然后它们有两个“名称”(实际上是小数字)。分叉后,通常会再次复制文件描述符(到
stdin
stdout
)。此时,您不再需要原始的文件描述符,也不再需要它们。