当父进程调用 exit(0) 时,子进程卡在 fork() 中

child process stuck in fork() when parent calls exit(0)

背景:

我的程序test.c正在创建守护进程。

test.c 中创建守护进程的代码片段。

sigset_t set;
pid_t   pid;
if ((pid = fork()) < 0)
{
    printf("Did not create the daemon process\n");
    exit(1);
}
else if (pid != 0)
{
    exit(0);
}
setsid();
chdir("/");
closefiles();
...

我使用 rc 脚本启动我的程序 test.c。

问题:

在极少数情况下,我看到脚本挂起并且 test.c 守护进程挂在 fork() 系统调用中。

在 gdb 中看到的进程回溯。

(gdb) bt
#0  0x00007f6743dd5859 in __unregister_atfork () from /lib64/libc.so.6
#1  0x00007f6744f215f3 in __do_global_dtors_aux () from <an internal library>
#2  0x00007ffd358e29b0 in ?? ()
#3  0x00007f674566786a in _dl_fini () from /lib64/ld-linux-x86-64.so.2
Backtrace stopped: frame did not save the PC

我认为这可能是由于竞争条件造成的。我可以理解父进程甚至在子进程中的 fork() 系统调用完成执行之前就退出了,这导致了某种死锁。这似乎是在很短的时间内发生的 window,因为在 exit(0) 之前添加 printf() 语句导致脚本和守护进程成功执行。

此代码有效!

sigset_t set;
pid_t   pid;
if ((pid = fork()) < 0)
{
    printf("Did not create the daemon process\n");
    exit(1);
}
else if (pid != 0)
{
    printf("Parent process about to exit\n");
    exit(0);
}
printf("Started child process\n");
setsid();
chdir("/");
...

我想了解的:

  1. 如何调试此问题以了解 exit() 导致 fork() 挂起的原因。
  2. 解决这个问题的理想方法是什么?我在想我可以在成功执行 fork() 后向父级发出信号,只有在它收到父级应该退出的信号后。

我认为这个问题与 atfork handlers 有关。

从您的 GDB 回溯,父进程正在进程终止前进行清理工作,这称为 __unregister_atfork()

出于调试目的,您可以附加到子进程并找出它在等待什么。我希望子进程是 运行 atfork 处理程序。 如果将 exit() 更改为 _exit() 以避免父级清理,或将 fork() 更改为原始系统调用以避免 运行 atfork 处理程序,则此问题应该消失。

根据 SysV Daemon 要求,您可以在子初始化完成后使用双叉并终止父进程。

如果您确定您的程序没有滥用pthread_atfork(),您可以尝试更新的编译器和glibc。