当父进程调用 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("/");
...
我想了解的:
- 如何调试此问题以了解 exit() 导致 fork() 挂起的原因。
- 解决这个问题的理想方法是什么?我在想我可以在成功执行 fork() 后向父级发出信号,只有在它收到父级应该退出的信号后。
我认为这个问题与 atfork handlers 有关。
从您的 GDB 回溯,父进程正在进程终止前进行清理工作,这称为 __unregister_atfork()。
出于调试目的,您可以附加到子进程并找出它在等待什么。我希望子进程是 运行 atfork 处理程序。
如果将 exit()
更改为 _exit()
以避免父级清理,或将 fork()
更改为原始系统调用以避免 运行 atfork 处理程序,则此问题应该消失。
根据 SysV Daemon 要求,您可以在子初始化完成后使用双叉并终止父进程。
如果您确定您的程序没有滥用pthread_atfork()
,您可以尝试更新的编译器和glibc。
背景:
我的程序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("/");
...
我想了解的:
- 如何调试此问题以了解 exit() 导致 fork() 挂起的原因。
- 解决这个问题的理想方法是什么?我在想我可以在成功执行 fork() 后向父级发出信号,只有在它收到父级应该退出的信号后。
我认为这个问题与 atfork handlers 有关。
从您的 GDB 回溯,父进程正在进程终止前进行清理工作,这称为 __unregister_atfork()。
出于调试目的,您可以附加到子进程并找出它在等待什么。我希望子进程是 运行 atfork 处理程序。
如果将 exit()
更改为 _exit()
以避免父级清理,或将 fork()
更改为原始系统调用以避免 运行 atfork 处理程序,则此问题应该消失。
根据 SysV Daemon 要求,您可以在子初始化完成后使用双叉并终止父进程。
如果您确定您的程序没有滥用pthread_atfork()
,您可以尝试更新的编译器和glibc。