execvp()永远不会在管道上完成

时间:2015-01-30 03:26:55

标签: c printf pipe echo execvp

我正在学习操作系统课程并编写一个shell。我遇到了关于execvp()和管道的问题。以下代码是问题发生的代码的简化版本。

static pid_t
command_exec(command_t *cmd, int *pass_pipefd) //the function that executes commands. this function will be called multiple times if the input line contains multiple commands. Ex. "echo abc | cat"
{
    pid_t pid = 0; //child pid
    int pipefd[2]; //array for pipe
    if(cmd->controlop == CMD_PIPE){
        //if the control operation of the command is pipe(to the left of a '|'), create a pipe
        pipe(pipefd);
    }
    pid = fork();
if(pid==0){ //child branch
    dup2(*pass_pipefd,0);//redirect stdin to the pipe from last command
    if(strcmp(cmd->argv[0],"cd")){ //if the command is not cd
        if(cmd->controlop == CMD_PIPE){
            dup2(pipefd[1],1); 
            //if command's control operation is pipe(to the left of a '|'), redirect stdout to pipefd[1]
        }
        if(execvp(cmd->argv[0],cmd->argv)<0)//execute the command,use stdin as input and stdout as output(but they may be redirected)
            printf("%s fails\n",arg[0]);
    }
    exit(0);
}else{//if parent
    wait(NULL); //wait for the child
    if(cmd->controlop == CMD_PIPE){
        *pass_pipefd = pipefd[0];//if command's control operation is pipe(to the left of a '|'), set *pass_pipefd to the output of the pipe array.
        return pid;
    }
}

如果输入是“echo a”,那么输出没问题。 execvp()将完成并在父级中等待(NULL)将不会永远等待。但是如果输入是“echo abc | cat”,则“abc”将输出到终端,但程序将被卡住。原因是执行“cat”的execvp()永远不会完成,因此父级中的wait(NULL)会永远等待。我知道execvp()不会返回但最终应该完成。我想我可能搞乱stdin和stdout重定向的东西,但我找不到bug。

1 个答案:

答案 0 :(得分:0)

简单地说,你没有关闭足够的文件描述符。特别是,父级必须关闭管道的两端。

此外,在shell中,在运行下一个孩子之前,你不能让父母同步等待每个孩子完成(虽然这不是你问题的一部分)。您必须让管道中的流程一次全部运行,因为如果您有A | B并且在启动A之前等待B完成,但A产生的数据多于适合管道缓冲区(4 KiB到64 KiB,IIRC,取决于平台),然后A永不退出,因此B永远不会启动,因此系统死锁。

由于您的代码不可执行 - 它不是MCVE(Minimal, Complete, Verifiable Example) - 我不倾向于尝试修复它;我无法充分说明它是如何调用的以及输入数据的样子。但是cat在标准输入返回EOF之前不会终止,并且在管道的写入结束打开的过程中,其标准输入不会返回EOF。但是你的shell代码显然仍然打开了管道,所以你再次陷入僵局。