C语言中的多管道

C语言中的多管道,c,exec,fork,fcntl,dup2,C,Exec,Fork,Fcntl,Dup2,我尝试在C中实现多个管道, 解决方案应包括以下两个方面: cmd1 | cmd2 | cmd3 以及: |--- cmd2 cmd1 |--- cmd3 |--- cmd4 #包括 #包括 #包括 #包括 #包括 #包括 int main(int argc,char*argv[]){ char*args1[]={“ls”,NULL,NULL}; char*args2[]={“ls”,“-l”,NULL}; char*args3[]={“排序”,NUL

我尝试在C中实现多个管道, 解决方案应包括以下两个方面:

cmd1 | cmd2 | cmd3
以及:

        |--- cmd2

cmd1    |--- cmd3

        |--- cmd4
#包括
#包括
#包括
#包括
#包括
#包括
int main(int argc,char*argv[]){
char*args1[]={“ls”,NULL,NULL};
char*args2[]={“ls”,“-l”,NULL};
char*args3[]={“排序”,NULL,NULL};
char*args4[]={“wc”,“-l”,NULL};
int rc1=execute_cmd(args1,0);
//printf(“rc1=%d\n”,rc1);
int rc2=execute_cmd(args2,rc1);
//printf(“rc2=%d\n”,rc2);
int rc3=execute_cmd(args3,rc1);
//printf(“rc3=%d\n”,rc3);
int rc4=执行命令(args4,rc1);
//printf(“rc4=%d\n”,rc4);
int缓冲区[1024];
int len=0;
如果(rc2){
而((len=read(rc2,buffer,sizeof(buffer)))>0){
写入(标准文件号,“rc2\n”,4);
写入(标准文件号和缓冲区,len);
}
}否则{
printf(stderr,“ERROR\n”);
}
如果(rc3){
而((len=read(rc3,buffer,sizeof(buffer)))>0){
写入(标准文件号,“rc3\n”,4);
写入(标准文件号和缓冲区,len);
}
}否则{
printf(stderr,“ERROR\n”);
}
如果(rc4){
而((len=read(rc4,buffer,sizeof(buffer)))>0){
写入(标准文件号,“rc4\n”,4);
写入(标准文件号和缓冲区,len);
}
}否则{
printf(stderr,“ERROR\n”);
}
返回0;
}
int execute\u cmd(字符**args,int fd\u in){
int-pipefd[2];
管道(pipefd);
如果(fork()==0){
关闭(pipefd[0]);
dup2(pipefd[1],标准文件号);
dup2(pipefd[1],标准文件号);
关闭(pipefd[1]);
如果(fd_in){
dup2(fd_in,0);
}
execvp(*args,args);
printf(“未能执行%s%s”,*args,*args[0]);
}否则{
关闭(pipefd[1]);
返回管道fd[0];
}
}
一旦我看到正确的结果,一旦我看到不同的结果,程序的输出是不确定的。看起来dup2没有像我预期的那样工作,如果我多次dup2,并且对于从结果文件描述符读取的每个文件描述符-看起来它对复制的文件描述符有影响


如果它的工作方式与我在设计中提到的一样,那么我需要为这两个系统调用使用哪个系统调用?

是的,dup和dup2为同一管道创建完全等效的句柄。如果多个进程(或线程)同时尝试使用重复/分叉描述符从管道中读取数据,则其中的“随机”进程将首先到达数据,但写入管道的每个字节只传递一次


如果要将数据复制到多个不同的读卡器,则必须显式地进行编程--派生子进程(或派生线程)从一个传入管道读取一些数据,然后将其写入所有传出管道,然后继续循环,直到到达EOF。

对同一管道具有多个句柄/引用将导致许多同步问题,等等

例如,如果有两个子进程,其中一个发送“Hello\n”,然后发送“World\n”,另一个发送“Foo\n”,然后发送“Bar\n”;然后您可能会以“Hello\n World\n Foo\n Bar\n”或“Hello\n Foo\n World\n Bar”或“Foo\n Hello\n Bar\n World”等结束。输出结果是无序的(这将非常混乱)

解决方法是使用不同的管道

基本上,当主程序分叉时,它应该创建新的管道,该管道将成为子进程的STDOUT和STDERR。然后,主程序将需要从其末端读取所有新管道并(可能)缓冲信息,以便主进程可以按特定顺序将数据从子进程发送到其自己的STDOUT/STDERR,例如,第一个子进程的所有输出,第二个子进程的所有输出,然后下一个子进程的所有输出,等等

主程序还可以添加额外的信息,并进行一些格式化,以便更清楚地了解正在发生的事情。对于上面的示例,您可能会得到:

Process A (exit status = 0, OK):
    Hello
    World
Process B (exit status = 1, Failed):
    Foo
    Bar
而不仅仅是:

 Hello
 World
 Foo
 Bar
对于输入(STDIN),我不知道您希望它如何工作。如果没有子进程需要STDIN(最简单也是最可能的情况),那么您可以忽略它。如果每个子进程都需要获得主进程STDIN的各自副本,那么您需要为每个子进程创建新的管道以用作其STDIN


另一种选择是拥有一个“当前选定的子对象”,这可能会变得更加复杂(特别是当最终用户需要能够看到选定子对象的输出时,因为这意味着实现某种在“当前正在显示”之间切换的方式)子项-例如,当用户选择不同的子项时,清除屏幕并显示该子项的待办事项)。

这实际上不是一个C问题,而是一个Unix问题。
 Hello
 World
 Foo
 Bar