Linux下atexit函数不生效求助:子进程无退出回调输出
问题描述
我通过fork()创建进程,在进程末尾调用exit(0),并通过atexit(endfunc)注册退出回调函数,目的是监控进程是否退出以避免僵尸进程。但子进程的atexit回调没有输出,怀疑产生了僵尸进程。以下是代码及执行输出,请问为何子进程的atexit无输出?子进程是否未退出?
代码示例
//fork parent process (first process fork): if ((pid = fork()) == 0) { printf("parent1: %d in %d\n", getpid(), getpgid(pid)); atexit(endfunc); char* argf[MAXARGS]; int a; printf("buf: %s\n", buf); if (strchr(cmdline, '|') != NULL) { a = make_tokens(buf, 0, argf, "|"); printf("In pipe\n"); int fd[200][2]; pid_t pids[200]; for (int i = 0; i < a - 1; i++) { pipe(fd[i]); //somewhere in parent fork child: if ((pids[0] = fork()) == 0) { printf("child: %d in %d\n", getpid(), getpgid(pid)); atexit(endfunc); close(fd[0][0]); for (int i = 1; i < a - 1; i++) { close(fd[i][0]); close(fd[i][1]); } char* arg[MAXARGS]; parseline(argf[0], arg); execvp(arg[0], arg); exit(0); } //at the end of parent process wait for childrens pid_t wpid; for (int i = 0; i < a; i++) { wpid = waitpid(pids[i], NULL, 0); if (wpid < 0) { perror("waitpids"); } else if (wpid >= 0) { printf("wait for %d\n", pids[i]); } exit(0);//parent process exit //endfunc: function for atexit() void endfunc(void) { printf("process %d ended\n", getpid()); }
执行输出
mini> ls -al | grep t parent1: 23154 in 23140 buf: ls -al | grep t In pipe child: 23155 in 23140 child: 23156 in 23140 //output for command wait for 23155 wait for 23156 process 23154 ended wait for 23154
原因分析与解答
1. 子进程atexit无输出的核心原因
你的子进程在注册atexit回调后,调用了execvp(arg[0], arg)——exec系列函数会完全替换当前进程的整个地址空间,包括代码段、数据段、堆栈以及所有注册的回调函数。一旦execvp成功执行,原进程中注册的atexit回调会被彻底清除,后续的exit(0)也根本不会被执行。
只有当execvp执行失败时(比如指定的命令不存在),才会执行后续的exit(0),进而触发atexit回调。但在你的测试中,ls和grep都是系统有效命令,execvp成功完成进程替换,所以原进程的回调逻辑直接被覆盖,自然不会有输出。
2. 子进程已正常退出,无僵尸进程
从输出中的wait for 23155和wait for 23156可以明确:两个子进程已经正常终止,并且被父进程(pid 23154)通过waitpid成功回收。僵尸进程的定义是父进程未调用wait/waitpid回收,子进程处于终止状态但仍占用进程表项,而你的父进程已经完成了回收操作,因此不存在僵尸进程问题。
3. 验证方法
如果想看到子进程的atexit输出,可以故意让execvp执行失败,比如将命令改为不存在的程序(如lss -al)。此时execvp会返回-1,后续的exit(0)会被执行,atexit回调也会触发,你就能看到对应的输出。
内容的提问来源于stack exchange,提问作者newb hi

