You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Linux下atexit函数不生效求助:子进程无退出回调输出

子进程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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 09:45:34