- 此问题在此处已有答案**:
Pipe two or more shell commands in C using a loop(1个答案)
Implementation of multiple pipes in C(6个答案)
Combining two commands with a pipe(1个答案)
Connecting n commands with pipes in a shell?(2个答案)
十小时前关门了。
我需要创建一个程序,在shell中执行此命令,有两个管道和三个进程:LS|分类|grep r.我编写的代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <signal.h>
#include <fcntl.h>
#include <string.h>
#include <sys/wait.h>
#define WRITE 1
#define READ 0
int main(int argc, char** argv)
{
int fd1[2],fd2[2];
pid_t pid1,pid2;
if( (pid1 = fork()) < 0)
{
perror("fork");
exit(-1);
}
if( pipe(fd1) < 0)
{
perror("pipe 1");
exit(-1);
}
if( pipe(fd2) < 0)
{
perror("pipe 2");
exit(-1);
}
if( pid1 == 0 )
pid2 = fork();
if(pid1>0)
{
close(fd2[READ]);
close(fd2[WRITE]);
close(fd1[READ]);
dup2(fd1[WRITE],STDOUT_FILENO);
close(fd1[WRITE]);
execlp("ls","ls",NULL);
perror("ls");
exit(-1);
}
if(pid2>0)
{
close(fd1[WRITE]);
dup2(fd1[READ],STDIN_FILENO);
close(fd1[READ]);
close(fd2[READ]);
dup2(fd2[WRITE],STDOUT_FILENO);
close(fd2[WRITE]);
execlp("sort","sort",NULL);
perror("sort");
exit(-1);
}
if(pid2==0)
{
close(fd1[READ]);
close(fd1[WRITE]);
close(fd2[WRITE]);
dup2(fd2[READ],STDIN_FILENO);
close(fd2[READ]);
execlp("grep","grep","r",NULL);
perror("grep");
exit(-1);
}
}
可能是我在这两个管道的沟通上出了问题,因为我今天才开始学习它们是如何工作的。如果我弄错了管道的一些重要的东西,我很抱歉。我希望有人能帮助我并解释我错在哪里。谢谢。
1条答案
按热度按时间2vuwiymt1#
在第一个分叉之后(但在第二个分叉之前)创建了管道。
这意味着您调用了
pipe
4次,在第一个fork之后的每个进程中调用了2次。因此
fd1
和fd2
有一组值用于主进程(pid1>0
为其执行ls
的进程),另一组值用于子进程和孙进程(第二个fork是在创建管道之后完成的,所以这里没有问题:执行X1 M5 N1 X和X1 M6 N1 X的两个进程共享相同的文件描述符)。所以对于
sort | grep
部分来说,没有问题,sort的输出是fd2[1]
,而grep
的输入是fd2[0]
,fd2
是同一个pipe
调用的结果,它在第二个fork之前执行,因此在这两个进程之间共享。但是对于
ls | sort
部分,您所做的事情与执行类似操作时完全相同无法按预期工作。两个进程的
fd
之间没有任何关系。因此,您需要在分叉之前创建管道。
至少,您打算在将由这个fork创建的进程之间共享的管道。
我想我知道你为什么要做这件奇怪的事了。因为3条工艺管道链的另一个警告:我们经常看到人们在两次分叉之前就创建了所有管道,但代码却无法正常工作。2还有一个原因:他们常常忘记了他们的管道存在于3个进程中,他们需要关闭所有他们不使用的管道。即使在与
fd1
无关的进程中,fd1
的两端也必须关闭。编辑:你做的另一件奇怪的事情是,即使在你想使用
fd1[WRITE]
的进程中(就在dup 2之后),你也要关闭它。你不能这么做。这不像在dup2
之后,你可以关闭原始管道,因为你会有一个副本,或者类似的东西。它是同一个文件的描述符的副本。如果你关闭了这个文件,它也会为副本关闭。重写你的代码
注意,这不是唯一的解决方案,我可以在第一个fork之前无条件地创建
pipe(fd2)
,然后在“ls”进程中关闭fd2[0]
和fd2[1]
,这也是可以的。要点是:
fd[1]
上写,并且从另一个进程的fd[0]
上读取已经写的内容,那么这些进程必须共享由相同的pipe
调用发出的相同的fd
值;因此实际上,这意味着必须在分隔这两个进程的fork
之前调用pipe(fd)
fd?[?]
,即使你只是通过另一个fd来使用它们,你在另一个fd上使用了fd?[?]
。fd?[?]
,即由pipe
调用创建的fd?[?]
(您必须在心里记住哪个进程拥有哪个fd
),并且您不需要。