execvp() 永远不会在管道上完成
execvp() never finishes on pipe
我正在上 OS 课程并写一篇 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() 将完成,父级中的 wait(NULL) 不会永远等待。但是如果输入的是"echo abc | cat",那么终端会输出"abc"但是程序会卡住。原因是执行 "cat" 的 execvp() 永远不会完成,所以父等待 forever.I 中的 wait(NULL) 知道 execvp() 不会 return 但它最终应该完成。我想我可能会弄乱 stdin 和 stdout 重定向的东西,但我找不到错误。
简而言之,您没有关闭足够多的文件描述符。特别是,父级必须关闭管道的两端。
此外,在 shell 中,您不能让父级在 运行 下一个子级完成之前同步等待每个子级完成(尽管这不是您的问题的一部分) .您必须立即让管道中的进程全部 运行,因为如果您有 A | B
并且在启动 B
之前等待 A
完成,但是 A
产生的数据超过管道缓冲区的容量(4 KiB 到大约 64 KiB,IIRC,取决于平台),然后 A
永远不会退出,所以 B
永远不会启动,所以系统死锁。
由于您的代码不可执行——它不是 MCVE (Minimal, Complete, Verifiable Example)——我不想尝试修复它;我无法充分说明它是如何调用的以及输入数据是什么样的。但是 cat
直到它的标准输入 returns EOF 才终止,虽然有一个进程打开了管道的写端,但它的标准输入不会 return EOF。但是您的 shell 代码相当清楚地仍然打开了管道,所以您又遇到了死锁。
我正在上 OS 课程并写一篇 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() 将完成,父级中的 wait(NULL) 不会永远等待。但是如果输入的是"echo abc | cat",那么终端会输出"abc"但是程序会卡住。原因是执行 "cat" 的 execvp() 永远不会完成,所以父等待 forever.I 中的 wait(NULL) 知道 execvp() 不会 return 但它最终应该完成。我想我可能会弄乱 stdin 和 stdout 重定向的东西,但我找不到错误。
简而言之,您没有关闭足够多的文件描述符。特别是,父级必须关闭管道的两端。
此外,在 shell 中,您不能让父级在 运行 下一个子级完成之前同步等待每个子级完成(尽管这不是您的问题的一部分) .您必须立即让管道中的进程全部 运行,因为如果您有 A | B
并且在启动 B
之前等待 A
完成,但是 A
产生的数据超过管道缓冲区的容量(4 KiB 到大约 64 KiB,IIRC,取决于平台),然后 A
永远不会退出,所以 B
永远不会启动,所以系统死锁。
由于您的代码不可执行——它不是 MCVE (Minimal, Complete, Verifiable Example)——我不想尝试修复它;我无法充分说明它是如何调用的以及输入数据是什么样的。但是 cat
直到它的标准输入 returns EOF 才终止,虽然有一个进程打开了管道的写端,但它的标准输入不会 return EOF。但是您的 shell 代码相当清楚地仍然打开了管道,所以您又遇到了死锁。