一个模拟Linux 的 Shell的程序.解析多管道的问题

问题描述:

        最近在弄一个模拟shell的程序, 简单的重定向 和 管道解析没问题。但是到了一个指令当中有多个多个管道的时候就出现问题了, 求大哥大姐们给看看 :-) 。比如如下指令:

ps -aux | grep 1 | grep 2


想法

        创建三个子进程p1, p2, p3, 在创建p1的时候先创建 管道 c1, 再fork, 则p1 和 父进程共享c1

        在创建p2进程时, 先创建 管道 c2, 再fork, 则此时 p1, p2, 父进程共享c1, p2 和 父进程共享c2

        在创建p3进程时, 先关闭对c1的读写文件描述符, 在fork, 则此时 p1, p2, 共享 c1p2, p3, 父进程 共享 c2

则在每个子进程中关闭对应管道的读端和写端后进行重定向到标准输入输出即可。

父进程需要将其共享的管道的读写端全部关闭。 最终, 所有的管道的读写端的数量均为1


代码实现

/** 
 * 三个子进程 
 * 实现 ps -aux | grep 1 | grep 2 指令的解析 
 */ 
#include <stdio.h> 
#include <unistd.h> 
#include <stdlib.h> 
int pipe_no[3][2]; 
int main() 
{ 
      
    int i = 0; 
    for(i = 0; i < 3; ++i) 
    { 
        // 创建pipe 
        if(i != 2) 
        { 
            int ret = pipe(pipe_no[i]); 
            if(ret < 0) 
            { 
                printf("创建管道失败\n"); 
                exit(1); 
            } 
 
        } 
 
        // 删除pipe 
        if(i >=2 ) 
        { 
            close(pipe_no[i - 2][0]); 
            close(pipe_no[i - 2][1]); 
        } 
        int child_id = fork(); 
        if(child_id == 0) 
            break; 
    } 
    
    // 第一个子进程
    if(i == 0)
    {
        printf("子进程1\n");
        sleep(2);
        close(pipe_no[i][1]);
        dup2(pipe_no[i][0], STDOUT_FILENO);
        execlp("ps", "ps", "-aux", NULL);
    }

    // 第二个子进程
    else if(i == 1)
    {
        printf("子进程2\n");
        close(pipe_no[i - 1][0]);
        dup2(pipe_no[i - 1][1], STDIN_FILENO);
        
        close(pipe_no[i][1]);
        dup2(pipe_no[i][0], STDOUT_FILENO);

        execlp("grep", "grep", "1", NULL);
    }

    // 第三个子进程
    else if(i == 2) 
    {
        printf("子进程3\n");
        close(pipe_no[i - 1][0]);
        dup2(pipe_no[i - 1][1], STDIN_FILENO);
        
        close(pipe_no[i][1]);
        dup2(pipe_no[i][0], STDOUT_FILENO);

        execlp("grep", "grep", "2", NULL);
    }
    // 父进程
    else
    {
        int i = 0;
        // 即使在父进程当中关闭也再关闭一次。
        // 即使在父进程当中关闭也再关闭一次。
        for(i = 0; i < 3 - 1; ++i)
        {
            close(pipe_no[i][0]);
            close(pipe_no[i][1]);
        }
        int ret = 0;
        do{
            ret = wait(NULL);
        }while(ret > 0);
    }
    return 0;
}


报错:

子进程2
grep: (standard input): Bad file descriptor
子进程3
grep: (standard input): Bad file descriptor
子进程1
ps: write error: Bad file descriptor
~~~~```
阅读 3.8k
2 个回答

这代码写的也太难理解了,而且都没有初始化pipe_no[2]就拿来用了。仔细看了下,是colse和dup2对应的fd搞反了吧。
实际上不用三个管道吧,两个就行了。

ps -> stdout -> stdin -> grep -> stdout -> stdin -> grep -> stdout
      |    pipe1    |            |    pipe2    |

小改了下,运行看着没问题。

/**
 * 三个子进程
 * 实现 ps -aux | grep 1 | grep 2 指令的解析
 */
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int pipe_no[2][2];
int main()
{
    int i = 0;
    for(i = 0; i < 3; ++i)
    {
        // 创建pipe
        if(i != 2)
        {
            int ret = pipe(pipe_no[i]);
            if(ret < 0)
            {
                printf("创建管道失败\n");
                exit(1);
            }

        }

        int child_id = fork();
        if(child_id == 0)
            break;
    }

    // 第一个子进程
    if(i == 0)
    {
        printf("子进程1\n");
        //sleep(2);
        close(pipe_no[i][0]);
        dup2(pipe_no[i][1], STDOUT_FILENO);
        execlp("ps", "ps", "-a", NULL);
    }

        // 第二个子进程
    else if(i == 1)
    {
        printf("子进程2\n");
        close(pipe_no[0][1]);
        dup2(pipe_no[0][0], STDIN_FILENO);

        close(pipe_no[1][0 ]);
        dup2(pipe_no[1][1], STDOUT_FILENO);

        execlp("grep", "grep", "4", NULL);
    }

        // 第三个子进程
    else if(i == 2)
    {
        printf("子进程3\n");
        close(pipe_no[0][0]);
        close(pipe_no[0][1]);

        close(pipe_no[1][1]);
        dup2(pipe_no[1][0], STDIN_FILENO);

        execlp("grep", "grep", "8", NULL);
    }
        // 父进程
    else
    {
        int i = 0;
        // 即使在父进程当中关闭也再关闭一次。
        // 即使在父进程当中关闭也再关闭一次。
        for(i = 0; i < 2; ++i)
        {
            close(pipe_no[i][0]);
            close(pipe_no[i][1]);
        }
        int ret = 0;
        do{
            ret = wait(NULL);
        }while(ret > 0);
    }
    return 0;
}

无需关闭 pipe。

// gcc -std=c11 -Wall
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

typedef struct _APP
{
    char *cmd, *argv;
    int inFD, outFD;
} APP;

void chain_app(APP *apps, size_t n)
{
    apps[0].inFD = -1;
    apps[n - 1].outFD = -1;

    for (int i = 0; i < n - 1; i++)
    {
        int fds[2];
        pipe(fds);
        apps[i].outFD = fds[1];
        apps[i + 1].inFD = fds[0];
    }
}

void run_app(APP *apps, size_t n)
{
    for (int i = 0; i < n; i++)
    {
        APP *app = &apps[i];
        if (fork() > 0)
        {
            if (app->inFD != -1)
                dup2(app->inFD, STDIN_FILENO);
            if (app->outFD != -1)
                dup2(app->outFD, STDOUT_FILENO);
            execlp(app->cmd, app->cmd, app->argv, NULL);
        }
    }
}

void wait_child(size_t n)
{
    for (int i = 0; i < n; i++)
    {
        int status;
        wait(&status);
    }
}

int main()
{
    APP apps[] = {
        {"ps", "-aux"},
        {"grep", "usr"},
        {"grep", "daemon"},
    };
    size_t n = sizeof(apps) / sizeof(apps[0]);

    chain_app(apps, n);
    run_app(apps, n);
    wait_child(n);
}
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题