minishell管道实现中关闭写端未向另一进程发送EOF的问题
问题原因
你遇到的问题核心是子进程没有关闭自身不使用的管道文件描述符,导致管道写端的引用计数无法归零,读端进程永远收不到EOF。
具体逻辑拆解
- 调用
pipe(pipefd)创建管道后,父进程持有pipefd[0](读端)和pipefd[1](写端)两个文件描述符,每次fork子进程时,子进程会完整继承这两个fd,此时两个管道fd的内核引用计数都会随着fork递增。 - 管道读端收到EOF的前提是:所有持有管道写端的进程都主动关闭了该写端。
- 你的代码中,左边写管道的子进程(执行
ls的i=0进程)、右边读管道的子进程(执行wc的i=1进程)都继承了管道的写端pipefd[1],且都没有主动关闭:
即使后续父进程关闭了自己持有的pipefd[1],读端子进程仍然持有pipefd[1]的引用,管道写端总引用计数不会降到0,内核不会给读端发送EOF,wc就会一直阻塞在读标准输入的操作中。 - 如果替换为不需要读管道的命令(比如
printenv),命令不会等待管道输入,所以能正常执行,符合你观察到的现象。
修复方案
在子进程ft_exec函数中,执行dup2前后关闭不需要的管道fd即可:
void ft_exec(int i) { char **ls = malloc(sizeof(char *) * 2); ls[0] = "/bin/ls"; ls[1] = NULL; char **wc = malloc(sizeof(char *) * 3); wc[0] = "/usr/bin/wc"; wc[1] = "-l"; wc[2] = NULL; printf("ENTERED BY %d\n", getpid()); if (i == 0) { // 写进程不需要管道读端,先关闭 close(pipefd[0]); dup2(pipefd[1], 1); // dup2完成后,原管道写端已经不需要,可以关闭 close(pipefd[1]); execve(ls[0], ls, environ); } if (i == 1) { // 读进程不需要管道写端,先关闭 close(pipefd[1]); dup2(pipefd[0], 0); // dup2完成后,原管道读端已经不需要,可以关闭 close(pipefd[0]); execve(wc[0], wc, environ); } }
额外优化:父进程可以在fork完所有子进程后,直接关闭自己持有的两个管道fd,不需要等子进程退出再关,进一步避免残留引用。
内容的提问来源于stack exchange,提问作者Mampac
相关产品推荐
相关产品推荐

