如何解决当父进程调用 exit(0) 时,子进程卡在 fork() 中
背景:
我的程序 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() 系统调用完成之前就退出了,这导致了某种死锁。这似乎发生在一个非常小的时间窗口中,因为在 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。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。