如何等待退出非子进程

CsT*_*mas 30 c linux process wait

对于子进程,可以使用wait()waitpid()函数暂停当前进程的执行,直到子进程退出.但是此功能不能用于非子进程.

还有其他功能,可以等待退出任何进程吗?

cha*_*aos 26

没有什么比wait().通常的做法是使用轮询kill(pid, 0),寻找返回值-1和errnoESRCH,表明进程已经一去不复返了.

  • @chaos:没有什么能保证`kill(pid,0)`会发出你感兴趣的进程的信号.在你调用`nanosleep`期间,它可能已经死了并被另一个正在运行的进程所取代.我担心我觉得不需要详细说明:已经提出了三个好的建议(FIFO,信号量和`ptrace`方法,尽管IMHO非常具有平台特性,但它优于其他所有方法). (7认同)
  • 有这样的忙循环可以吗? (2认同)
  • @Sam Hocevar:没有关于竞争条件的组成或如何在没有竞争条件的情况下做到这一点。并没有真正的帮助。 (2认同)

Hon*_*gli 13

在BSD和OS X上,您可以使用带有EVFILT_PROC + NOTE_EXIT的kqueue来完成该操作.不需要投票.不幸的是,没有Linux等价物.

  • 他们没有移植kqueue在Linux上感到羞耻. (8认同)

Dav*_*d Z 11

到目前为止,我已经在Linux上找到了三种方法:

  • 轮询:您经常检查是否存在流程,通过使用kill或通过测试存在/proc/$pid,如在大多数其他答案中
  • 使用ptrace系统调用像调试器一样附加到进程,以便在退出时收到通知,如a3nm的答案
  • 使用netlink界面来监听PROC_EVENT_EXIT消息 - 这样每当进程退出时,内核就会告诉您的程序,而您只需等待正确的进程ID.我只在互联网上的一个地方看过这个.

无耻的插件:我正在开发一个程序(当然是开源的; GPLv2),可以执行这三个程序中的任何一个.


Aar*_*lla 6

您还可以创建套接字或FIFO并读取它们.FIFO特别简单:将您孩子的标准输出连接到FIFO并读取.读取将阻止,直到子进程退出(出于任何原因)或直到它发出一些数据.因此,您需要一个小循环来丢弃不需要的文本数据.

如果您可以访问子项的源,请在启动时打开FIFO进行写入,然后将其忘记.当子进程终止并且等待的"父"进程将被唤醒时,操作系统将清除打开的文件描述符.

现在这可能是一个你没有开始或拥有的过程.在这种情况下,您可以使用启动实际二进制文件的脚本替换二进制可执行文件,但也可以添加监视,如上所述.


l_b*_*lev 5

这是一种等待 Linux 中的任何进程(不一定是子进程)退出(或被杀死)而不进行轮询的方法:

使用inotify等待/proc'pid'被删除将是完美的解决方案,但不幸的是inotify不适用于像/proc这样的伪文件系统。但是我们可以将它与进程的可执行文件一起使用。当该进程仍然存在时,该文件将保持打开状态。因此我们可以使用 inotify 和 IN_CLOSE_NOWRITE 来阻塞,直到文件关闭。当然,它可能会因其他原因而关闭(例如,如果具有相同可执行文件的另一个进程退出),因此我们必须通过其他方式过滤这些事件。

我们可以使用kill(pid, 0),但这不能保证它是否仍然是同一个进程。如果我们真的对此感到偏执,我们可以做点别的事情。

这是一种 100% 安全地防止 pid 重用问题的方法:我们打开伪目录 /proc/'pid',并保持它打开状态直到我们完成。如果同时使用相同的 pid 创建一个新进程,我们持有的目录文件描述符仍将引用原始进程(或者如果旧进程不再存在,则变得无效),但永远不会引用新进程重用的 pid。然后我们可以通过openat()检查目录中是否存在“cmdline”文件来检查原始进程是否仍然存在。当进程退出或被终止时,这些伪文件也不再存在,因此 openat() 将失败。

这是一个示例代码:

// return -1 on error, or 0 if everything went well
int wait_for_pid(int pid)
{
    char path[32];
    int in_fd = inotify_init();
    sprintf(path, "/proc/%i/exe", pid);
    if (inotify_add_watch(in_fd, path, IN_CLOSE_NOWRITE) < 0) {
        close(in_fd);
        return -1;
    }
    sprintf(path, "/proc/%i", pid);
    int dir_fd = open(path, 0);
    if (dir_fd < 0) {
        close(in_fd);
        return -1;
    }

    int res = 0;
    while (1) {
        struct inotify_event event;
        if (read(in_fd, &event, sizeof(event)) < 0) {
            res = -1;
            break;
        }
        int f = openat(dir_fd, "fd", 0);
        if (f < 0) break;
        close(f);
    }

    close(dir_fd);
    close(in_fd);
    return res;
}
Run Code Online (Sandbox Code Playgroud)