【发布时间】:2020-05-08 23:59:40
【问题描述】:
我已经用 C 编写了一个类似于 Linux shell 的 bash。
(链接到我的存储库:https://github.com/oneiro-naut/wish
.文件src/execute.c里面有execute_pipeline函数)
我在执行产生一些输出的内置命令时遇到问题。不产生任何输出的内置函数,如 cd 和 exit 工作得很好。外部命令运行流畅。 Shell 也支持管道结构和 I/O 重定向。问题是当我尝试运行内部命令时(它只是打印一条消息)。当我使用管道将内置命令的输出重定向到 grep 命令时,grep 子进程挂起,我认为这是因为 grep 在从管道读取时从未遇到流(或 EOF)的结尾。
注意:当管道中只有外部命令时不会发生这种情况。(我注意在下一个命令尚未启动之前没有(外部)命令完成,因此它不会挂在那里)
这是我的问题的简化版本 `
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
int main()
{
int pipe_fd[2] = {-1,-1};
int EXIT_STAT = 0;
pipe(pipe_fd);//creating pipe
char* argv[]={"grep","Radiohead",NULL};
int std_out_dup = dup(1);
dup2(pipe_fd[1],1); // redirecting stdout to parent pipe's read end
//close(pipe_fd[0]);//closing parent pipe's write end
printf("Alice in Chains\n");
printf("Radiohead\n");
printf("Dinosaur Jr.\n");
printf("Pixies\n");
printf("Soundgarden\n");
printf("Porcupine Tree\n");
printf("Pain of Salvation\n");
pid_t pid = fork();
if(pid == 0)//child process
{
dup2(pipe_fd[0],0); //redirecting grep's stdin to parent pipe's write end
close(pipe_fd[1]);
execvp(argv[0],argv);//executing grep command
perror("\n");
exit(-1);
}
else if(pid > 0)//parent process
{
int i;//closing pipes for parent
for(i=0;i<2;i++)
close(pipe_fd[i]);
do{ // waiting for grep to exit
waitpid(pid,&EXIT_STAT,WUNTRACED);
//write(,"\x4",1);
}while(!WIFEXITED(EXIT_STAT)&&!WIFSIGNALED(EXIT_STAT));
}
else ;//fork error
dup2(std_out_dup,1); // restore stdout fd for parent process
return 0;
}
`
如果我尝试在不打印任何内容的情况下运行它,程序就会挂起。
但是按 ^D 会终止产生输出的程序:
Alice in Chains
Radiohead
Dinosaur Jr.
Pixies
Soundgarden
Porcupine Tree
Pain of Salvation
所以 grep 似乎无法无限期地读取,因为它没有收到任何 EOF 信号。 请指出我的错误我真的很困惑。
编辑:现在即使按 ^D 也不会终止,所以我只需要 ^C 程序。
改正后的程序:
int main()
{
int pipe_fd[2] = {-1,-1};
int EXIT_STAT = 0;
pipe(pipe_fd);//creating pipe
char* argv[]={"grep","Radiohead",NULL};
int std_out_dup = dup(1);
//printf("%d \n",std_out_dup);
dup2(pipe_fd[1],1); // redirecting stdout to parent pipe's read end
//close(pipe_fd[0]);//closing parent pipe's write end
printf("Alice in Chains\n");
printf("Radiohead\n");
printf("Dinosaur Jr.\n");
printf("Pixies\n");
printf("Soundgarden\n");
printf("Porcupine Tree\n");
printf("Pain of Salvation\n");
fflush(stdout);
pid_t pid = fork();
if(pid == 0)//child process
{
close(pipe_fd[1]);
dup2(pipe_fd[0],0); //redirecting grep's stdin to parent pipe's write end
dup2(std_out_dup,1); // restore stdout fd for parent process
execvp(argv[0],argv);//executing grep command
perror("\n");
exit(-1);
}
else if(pid > 0)//parent process
{
int i;//closing pipes for parent
for(i=0;i<2;i++)
close(pipe_fd[i]);
dup2(std_out_dup,1); // restore stdout fd for parent process
do{ // waiting for grep to exit
waitpid(pid,&EXIT_STAT,WUNTRACED);
}while(!WIFEXITED(EXIT_STAT)&&!WIFSIGNALED(EXIT_STAT));
}
else ;//fork error
return 0;
}
【问题讨论】:
-
默认情况下,libc 将不会发送到 TTY 的输出配置为进行大量缓冲,以提高吞吐量而不是延迟。如果标准输入永远挂起,并且没有足够的数据被打印以强制刷新,则内容可以无限期地位于该缓冲区中。
-
您在
fork()之前关闭pipe_fd[0],然后在fork()之后的子项中关闭dup2。dup2调用将失败,因为旧的 fd 不是打开的文件描述符。 -
...无论如何,如果没有看到 EOF,请检查其他进程(f/e,来自其他实例使用
fork()生成相同的进程。 -
@Ian Abbott 哦,我没有注意到,感谢您的指出。我将删除 close(fd) 调用并检查。我还没有关闭原始文件中的 fd,即使它一直挂起。
-
@Charles Duffy 我会再次检查,谢谢。
标签: c shell grep pipe io-redirection