在C语言中监控进程是否终止

7

介绍

我正在用C语言编写一个监控程序,该程序执行fork()exec()循环。但我需要检查子进程是否已经终止,而不会阻塞主进程,即监控程序。类似于下面的代码:

主进程

pid_t child_pid = fork();
if (child_pid == 0)
    exec(bar);

while (1) {
    if (the child process has finished)
        foo();
    else
        bar();
}

我尝试过的方法

考虑到我有子进程pid,我尝试了以下方法:

  • 使用信号0发送kill调用并检查errno

    if (kill(child_pid, 0) == -1 || errno == ESRCH),但我认为这不是跟踪子进程状态的好方法,因为它不安全,容易出现竞态条件。此外,它似乎也没有起作用。

  • 使用stat(2)检查proc/child_pid是否存在。在这种情况下,所有上述负面参数都是正确的,而且这种方法更慢。

  • waitpid(2)。不幸的是,它会阻塞主进程。

问题

是否还有其他方法可以获取这种信息?或者,我可能从我已经尝试的解决方案中漏掉了什么?


2
使用任何信号kill一个名为child_pid的进程是完全安全的,只要你还没有回收(=等待)这个子进程,但使用0信号来判断一个进程是否已死亡并不是一个好方法。反复杀死僵尸进程是完全可以接受的,直到你wait时才会出现任何ESRCH错误,此时竞争条件就出现了。 - Petr Skocik
4个回答

14
如果你向 waitpid 传递 WNOHANG,它就不应该被阻塞。
if(waitpid(child_pid, &status, WNOHANG) != 0) {
    // child process has finished
    foo();
} else {
    // child process still running
    bar();
}

status 变量是什么,它被初始化为什么? - Babaktrad
@Babaktrad status 是一个整数,你不需要初始化它。它将由 waitpid 函数设置,并且您可以使用其他宏稍后从中获取信息。 - Eric Renouf

11

当一个进程终止时,你可以设置父进程来接收并处理 SIGCHLD 信号,请参阅 signal(7);信号处理程序只能调用异步信号安全函数,或者设置在处理程序外面测试的 volatile sigatomic_t flag(例如,在主事件循环中使用 poll(2) ...),或者对一些文件描述符(例如管道或某些 eventfd(2))进行 write(2)。如 Bruce Ediger所答复的,也可以使用Linux特有的signalfd

然后,您可以使用一些等待函数,例如 waitpid(2)(如果不想阻塞,可以使用 WNOHANG 参数)或 wait4(2) 来等待进程并获取其状态,等等。

还可以阅读 《高级Linux编程》 获取更多信息。它有几章关于这些问题。


7
如果您希望您的进程不被阻塞,或者您有其他需要检查的文件描述符,如果您编写的程序是针对Linux系统的话,那么您应该考虑使用signalfd()系统调用。
这个系统调用会返回一个特殊的文件描述符,您可以在select()poll()epoll()系统调用中使用。如果正确设置,子进程退出将导致内核使特殊文件描述符可读。从特殊文件描述符读取将给您提供一个填好信息的结构体,其中包含了关于子进程退出状态的信息。

5

Basile Starynkevitch的回答涵盖了大多数常见情况。

如果您遇到更不寻常的情况,有时使用管道可能会很有用。在创建子进程之前,使用pipe系统调用创建一个管道。在创建子进程后,在父进程中关闭管道的写端。从管道中读取会阻塞,直到子进程终止。

这与waitpid的区别如下:

  • 从管道中读取将等待子进程及其所有子进程终止(除非其中一些在此之前关闭文件描述符)。
  • 可以有多个进程从同一文件描述符读取,导致它们全部等待同一进程(或进程)终止。
  • 您不仅限于等待子进程终止。使用此方法,您还可以等待父进程或兄弟进程终止。
  • 通过使用close on exec,您可以等待进程终止或成功执行execve系统调用。
  • 您可以在管道上使用selectpoll系统调用以及其他文件描述符。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接