C:管道和叉子关闭。什么都没打印出来

时间:2016-01-26 04:27:42

标签: c linux pipe

cmds是要调用的命令列表。在我的情况下,我打电话给ls | grep c。当我运行程序时,没有任何内容被打印出来。似乎grep正在等待什么?

注意:如果我只使用ls(通过execPipe(cmds,1)),一切正常。

有什么问题?

int execPipe(char*** cmds,int len){
    int i;
    int pipefd[100][2];
    for(i = 0; i < len; i++)
        pipe(pipefd[i]);
    i = 0;
    for(i = 0; i < len; i++){
        if (fork() == 0){
            printf("executing #%d %s\n",i,cmds[i][0]);  
            //i=0:  in=sdtin, out=1
            //i=1:  in=1,out=3
            //i=2:  in=3,out=5
            //i=len in=len*2-1, out=sdtout

            close(pipefd[i][0]);

            if(i != 0){
                dup2(pipefd[i-1][1],0); //read becomes the write of last one
            }
            if(i != len-1){
                dup2(pipefd[i][1],1);   //write becomes pipefd[i][1]
            }

            execvp(cmds[i][0],cmds[i]);
            return EXIT_SUCCESS;
        } 
        close(pipefd[i][0]);
        close(pipefd[i][1]);
        wait(NULL);
    }
    return 0;
}
int main(){

    char*** cmds = malloc(2*sizeof(char**));
    cmds[0] = malloc(2*sizeof(char**));
    cmds[0][0] = "ls";
    cmds[0][1] = NULL;

    cmds[1] = malloc(3*sizeof(char**));
    cmds[1][0] = "grep";
    cmds[1][1] = "c";
    cmds[1][2] = NULL;

    execPipe(cmds,2);
    return 0;
}

1 个答案:

答案 0 :(得分:2)

此代码有效:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

static
int execPipe(char ***cmds, int len)
{
    int i;
    int pids[len];
    int pipefd[len][2];

    for (i = 0; i < len - 1; i++)
        pipe(pipefd[i]);

    for (i = 0; i < len; i++)
    {
        int pid;
        if ((pid = fork()) == 0)
        {
            printf("PID %d: executing #%d %s\n", (int)getpid(), i, cmds[i][0]);
            if (i != 0)
            {
                dup2(pipefd[i - 1][0], 0); // JL: Fix
            }
            if (i != len - 1)
            {
                dup2(pipefd[i][1], 1);   // write becomes pipefd[i][1]
            }
            for (int j = 0; j < len - 1; j++)    // JL: Fix
            {
                close(pipefd[j][0]);
                close(pipefd[j][1]);
            }
            execvp(cmds[i][0], cmds[i]);
            fprintf(stderr, "Failed to execute command %s\n", cmds[i][0]);
            return EXIT_FAILURE;
        }
        else if (pid < 0)
        {
            fprintf(stderr, "failed to fork for %s\n", cmds[i][0]);
            exit(1);
        }
        else
            pids[i] = pid;
    }

    for (i = 0; i < len - 1; i++)       // JL: Fix
    {
        close(pipefd[i][0]);
        close(pipefd[i][1]);
    }

    int corpse;
    int status;
    int kids = len;
    while (kids > 0 && (corpse = wait(&status)) > 0)
    {
        printf("PID %d died with status 0x%.4X\n", corpse, status);
        for (i = 0; i < kids; i++)
        {
            if (pids[i] == corpse)
            {
                pids[i] = pids[kids-1];
                kids--;
                break;
            }
        }
    }

    return 0;
}

int main(void)
{
    char ***cmds = malloc(2 * sizeof(char **));
    cmds[0] = malloc(2 * sizeof(char **));
    cmds[0][0] = "ls";
    cmds[0][1] = NULL;

    cmds[1] = malloc(3 * sizeof(char **));
    cmds[1][0] = "grep";
    cmds[1][1] = "c";
    cmds[1][2] = NULL;

    execPipe(cmds, 2);
    return 0;
}

评论:

  • 注意有多少关闭操作。
  • 将闭环因子分解为在需要时调用的函数是合理的。
  • 没有第一个孩子关闭管道就可以逃脱,但打破对称是很愚蠢的。
  • 父母关闭管道是至关重要的 - 但只有在管道完成之后(也就是说,在创建完所有子项之后)。
  • wait()循环处理父进程有孩子的情况,这些子进程在孩子启动之前终止了 - 这是一个相当不寻常但非常不可能的情况。可能只是等到所有孩子都死了,但也许以前创建的孩子中的一个不会终止。循环等到所有已知的孩子都死了然后退出。
  • 一个更复杂的机制,任何时候都只能打开两个管道,即使是在50个流程管道中,而不是一次打开所有49个管道,但这是后来的改进。

您应该将此扩展到3个进程或更长的管道,并检查它是否有效。可能的管道包括:

 who | awk '{print $1}' | sort
 who | awk '{print $1}' | sort | uniq -c
 who | awk '{print $1}' | sort | uniq -c | sort -n

注意:shell会删除单引号。