C和ls中的两个管道|分类|grep r [重复]

mpbci0fu  于 2023-02-03  发布在  其他
关注(0)|答案(1)|浏览(121)
    • 此问题在此处已有答案**:

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);
    }

}

可能是我在这两个管道的沟通上出了问题,因为我今天才开始学习它们是如何工作的。如果我弄错了管道的一些重要的东西,我很抱歉。我希望有人能帮助我并解释我错在哪里。谢谢。

2vuwiymt

2vuwiymt1#

在第一个分叉之后(但在第二个分叉之前)创建了管道。
这意味着您调用了pipe 4次,在第一个fork之后的每个进程中调用了2次。
因此fd1fd2有一组值用于主进程(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部分,您所做的事情与执行类似操作时完全相同

if(fork()){
        int fd[2];
        pipe(fd);
        close(fd[0]);
        dup2(fd[1], 1);
        printf("Hello\n");
        exit(0);
    }else{
        int fd[2];
        pipe(fd);
        close(fd[1]);
        dup2(fd[0], 0);
        char s[100];
        scanf("%s", s);
        printf("Should be hello=%s\n", s);
        exit(0);
    }

无法按预期工作。两个进程的fd之间没有任何关系。
因此,您需要在分叉之前创建管道。
至少,您打算在将由这个fork创建的进程之间共享的管道。
我想我知道你为什么要做这件奇怪的事了。因为3条工艺管道链的另一个警告:我们经常看到人们在两次分叉之前就创建了所有管道,但代码却无法正常工作。2还有一个原因:他们常常忘记了他们的管道存在于3个进程中,他们需要关闭所有他们不使用的管道。即使在与fd1无关的进程中,fd1的两端也必须关闭。
编辑:你做的另一件奇怪的事情是,即使在你想使用fd1[WRITE]的进程中(就在dup 2之后),你也要关闭它。你不能这么做。这不像在dup2之后,你可以关闭原始管道,因为你会有一个副本,或者类似的东西。它是同一个文件的描述符的副本。如果你关闭了这个文件,它也会为副本关闭。
重写你的代码

#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( pipe(fd1) < 0)
    {
        perror("pipe 1");
        exit(-1);
    }

    if( (pid1 = fork()) < 0)
    {
        perror("fork");
        exit(-1);
    }
    
    
    if( pid1 == 0 ){
        if( pipe(fd2) < 0)
        {
            perror("pipe 2");
            exit(-1);
        }
        pid2 = fork();
    }
        
    if(pid1>0)
    {
        // I need to close fd1[READ] that I don't use (and that "sort" will use)
        // I must keep fd1[WRITE] that I use
        // I leave fd2[READ/WRITE] alone, since that variable is unitialized here (I've called "pipe(fd2)" only in the case
        // pid1==0 which it is not here)
        close(fd1[READ]); 
        dup2(fd1[WRITE],STDOUT_FILENO);
        execlp("ls","ls",NULL);
        perror("ls");
        exit(-1);
    }
    
    if(pid2>0)
    {
        // I need to read fd1, but not write it (it is ls that need to write it). 
        // So before doing anything, I close fd1[WRITE]. Must I must keep the other end, fd1[READ] open
        close(fd1[WRITE]);
        dup2(fd1[READ],STDIN_FILENO);
        // Likewise, I need to write on fd2[WRITE], and have "grep" read on the other end. 
        // So I close fd2[READ] this side, and keep fd2[WRITE] open
        close(fd2[READ]);
        dup2(fd2[WRITE],STDOUT_FILENO);
        execlp("sort","sort",NULL);
        perror("sort");
        exit(-1);
    }
    if(pid2==0)
    {
        // Last process. It needs to read fd2[READ], that I keep open, and don't need to write on fd2[WRITE] (that is "sort" job)
        // so I close fd2[WRITE]
        // I don't really case for fd1
        // Situation is not exactly symetrical that for 1st process and fd2 tho. Because for 1st process, fd2 what not even initialized.
        // While here, even if we don't need it, fd1 has been created by our grandfather. We shared it from our father, that shared it from its father
        // So, since we use none of fd1, we need to close both.
        close(fd1[READ]);
        close(fd1[WRITE]);
        close(fd2[WRITE]);
        dup2(fd2[READ],STDIN_FILENO);
        execlp("grep","grep","r",NULL);
        perror("grep");
        exit(-1);
    }

}

注意,这不是唯一的解决方案,我可以在第一个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),并且您不需要。

相关问题