【问题标题】:child process stuck in fork() when parent calls exit(0)当父调用exit(0)时子进程卡在fork()中
【发布时间】:2021-08-22 08:46:35
【问题描述】:

背景:

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

来自 test.c 的代码 sn-p 创建一个守护进程。

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("/");
...

我想了解的内容:

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

【问题讨论】:

标签: c linux process fork daemon


【解决方案1】:

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

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

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

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

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

【讨论】:

  • 我再也无法确定性地重新创建这个问题,它在一台具有我的代码的特定二进制文件的特定机器上被看到,以及后续版本的二进制文件,从未见过这个。它发生在一个非常小的时间窗口内。为了防止这种情况再次发生,我在孩子完成初始化后终止了父母。我不确定 pthread_atfork() ,我可以在使用的库中看到一些对 pthread_atfork() 的调用,但我不确定。我需要进行更多研究,以更好地了解事物在幕后的运作方式。感谢您引导我朝着正确的方向前进。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-16
  • 1970-01-01
  • 1970-01-01
  • 2014-12-06
相关资源
最近更新 更多