未使用wait函数时父进程仍处于活跃状态的原因探究
问题:注释
wait后父进程未退出,ps仍能同时看到父子进程 我使用管道展示文件内容,注释掉父进程末尾的wait(NULL)后,原本以为文件读取完成后父进程会退出,此时ps输出应该只剩子进程(less),但实际执行ps时能同时看到父进程(编译后的xpager)和子进程。运行平台为Ubuntu 22.04.4 LTS,编译代码生成可执行文件xpager后,通过xpager filename命令执行。
完整代码
#include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <errno.h> #include <fcntl.h> #include <sys/wait.h> #define BUFSIZE 8192 #define PPAGER "/usr/bin/less" int main(int argc, char *argv[]) { pid_t pid; int eerrno, n; int in, pfd[2]; char buf[BUFSIZE]; char *pager, *argv0; if (argc != 2) { printf("usage: %s <pathname>\n", argv[0]); exit(1); } if ( (in = open(argv[1], O_RDONLY)) < 0) { eerrno = errno; printf("Open error. %s(%d)\n", strerror(errno), errno); exit(eerrno); } if ( pipe(pfd) < 0) { eerrno = errno; printf("pipe error. %s(%d)\n", strerror(errno), errno); exit(eerrno); } pid = fork(); switch (pid) { case -1: eerrno = errno; printf("fork error. %s(%d)\n", strerror(errno), errno); exit(eerrno); case 0: close(pfd[1]); close(in); if (STDIN_FILENO != pfd[0]) { if (dup2(pfd[0], STDIN_FILENO) < 0) { eerrno = errno; printf("dup2 error. %s(%d)\n", strerror(errno), errno); exit(eerrno); } close(pfd[0]); } if ((pager=getenv("PPAGER")) == NULL) pager = PPAGER; if ((argv0=strrchr(pager, '/')) != NULL) argv0++; else argv0=pager; execlp(pager, argv0, argv[1], (char *)0); default: close(pfd[0]); while((n=read(in, buf, BUFSIZE)) > 0) { if (write(pfd[1], buf, n) < 0) { eerrno = errno; printf("write errno. %s(%d)\n", strerror(errno), errno); kill (pid, 9); exit(eerrno); } } if (n < 0) { eerrno = errno; printf("read error. %s(%d)\n", strerror(errno), errno); kill(pid, 9); exit(eerrno); } close(pfd[1]); // wait(NULL); } exit(0); }
精简版代码
#include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <fcntl.h> #include <sys/wait.h> #define BUFSIZE 8192 #define PPAGER "/usr/bin/less" int main(int argc, char *argv[]) { pid_t pid; int n,in, pfd[2]; char buf[BUFSIZE]; char *pager, *argv0; in = open(argv[1], O_RDONLY); pipe(pfd); pid = fork(); switch (pid) { case 0: close(pfd[1]); dup2(pfd[0], STDIN_FILENO); pager = PPAGER; argv0="less"; execlp(pager, argv0, argv[1], (char *)0); default: close(pfd[0]); while ((n=read(in, buf, BUFSIZE)) > 0) write(pfd[1], buf, n); close(pfd[1]); // wait(NULL); } return(0); }
问题原因分析
less未读取管道内容:代码中给less传递了文件名参数argv[1],less的默认行为是:如果传入文件名,就直接打开该文件读取,不会从标准输入(管道)读取数据。这导致管道的读端没有被消费。- 父进程被
write阻塞:管道的缓冲区容量有限(默认约64KB),父进程不断向管道写入文件内容,但读端无读取操作,管道很快被写满,父进程的write调用会进入阻塞状态,无法执行后续的close和exit,因此父进程会一直存活。 less持续运行:less在展示文件时会保持运行状态,直到用户主动退出(如按q键),所以子进程会一直存在。
修复方案
如果希望父进程通过管道将内容传递给less,需去掉execlp中的文件名参数,让less从标准输入读取:
// 修正后的execlp调用 execlp(pager, argv0, (char *)0);
修改后,less会从管道读取父进程写入的内容,管道读端被持续消费,父进程写完文件后会正常关闭管道并退出(即使没有wait),此时ps只会显示运行中的less进程。
若保留wait(NULL),父进程会等待子进程退出后再终止,会一直存活到用户关闭less。
内容的提问来源于stack exchange,提问作者tahzibi.jafar
相关产品推荐
相关产品推荐

