37

对于子进程,wait()andwaitpid()函数可用于暂停当前进程的执行,直到子进程退出。但是这个函数不能用于非子进程。

是否有另一个函数可以等待任何进程的退出?

4

11 回答 11

32

没有什么相当于wait()。通常的做法是轮询使用kill(pid, 0)并寻找返回值 -1 和errnoofESRCH来指示进程已经消失。

更新:由于 linux 内核 5.3 有一个pidfd_open系统调用,它为给定的 pid 创建一个 fd,可以在 pid 退出时对其进行轮询以获取通知。

于 2009-07-21T07:32:36.373 回答
14

在 BSD 和 OS X 上,您可以使用 kqueue 和 EVFILT_PROC+NOTE_EXIT 来做到这一点。无需投票。不幸的是,没有 Linux 等价物。

于 2011-09-19T21:08:45.790 回答
14

到目前为止,我已经找到了三种在 Linux 上执行此操作的方法:

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

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

于 2014-11-03T13:24:15.507 回答
7

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

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

现在,这可能是一个您没有开始或拥有的过程。在这种情况下,您可以将二进制可执行文件替换为启动真正二进制文件的脚本,但也如上所述添加监控。

于 2009-07-21T07:47:41.723 回答
4

这是一种等待 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;
}
于 2019-02-19T11:33:27.877 回答
3

您可以使用ptrace(2). 从外壳来看,strace -p PID >/dev/null 2>&1似乎工作。这避免了忙等待,尽管它会减慢被跟踪的进程,并且不会在所有进程上工作(只有你的,这比只有子进程好一点)。

于 2011-08-26T15:34:55.227 回答
1

我不知道。除了混乱的解决方案之外,如果您可以更改要等待的程序,则可以使用信号量。

库函数是sem_open(3)sem_init(3), sem_wait(3),...

sem_wait(3)执行等待,因此您不必像在混乱解决方案中那样忙于等待。当然,使用信号量会使您的程序更加复杂,而且可能不值得这么麻烦。

于 2009-07-21T07:47:05.923 回答
1

也许可以等待 /proc/[pid] 或 /proc/[pid]/[something] 消失?

有 poll() 和其他文件事件等待函数,也许有帮助?

于 2010-08-03T11:40:02.977 回答
1

从 linux 内核 5.3 开始,有一个pidfd_open系统调用,它为给定的 pid 创建一个 fd,可以在 pid 退出时轮询它以获取通知。

于 2020-07-13T19:08:27.203 回答
0

只需轮询 /proc/[PID]/stat 的值 22 和 2。值 2 包含可执行文件的名称,而 22 包含开始时间。如果它们发生变化,则其他一些进程已采用相同(已释放)的 PI​​D。因此该方法非常可靠。

于 2021-10-20T21:49:28.127 回答
0

您可以使用eBPF来实现这一点。

bcc工具包实现了许多基于eBPF. 其中,exitsnoop跟踪进程终止,显示命令名称和终止原因,退出或致命信号。

   It catches processes of all users, processes in containers,  as  well  as  processes  that
   become zombie.

   This  works by tracing the kernel sched_process_exit() function using dynamic tracing, and
   will need updating to match any changes to this function.

   Since this uses BPF, only the root user can use this tool.

相关实现可以参考这个工具。

您可以从以下链接获取有关此工具的更多信息:</p>

你可以先安装这个工具并使用它,看看它是否满足你的需求,然后参考它的实现进行编码,或者使用它提供的一些库来实现你自己的功能。

exitsnoop例子:

   Trace all process termination
          # exitsnoop

   Trace all process termination, and include timestamps:
          # exitsnoop -t

   Exclude successful exits, only include non-zero exit codes and fatal signals:
          # exitsnoop -x

   Trace PID 181 only:
          # exitsnoop -p 181

   Label each output line with 'EXIT':
          # exitsnoop --label EXIT

另外一个选项

使用 Linux 的 PROC_EVENTS 等待(非子)进程退出

参考项目: https ://github.com/stormc/waitforpid

项目中提到:</p>

使用 Linux 的 PROC_EVENTS 等待(非子)进程退出。由于 waitforpid 二进制文件允许 CAP_NET_ADMIN POSIX 功能,它不需要设置 suid root。您需要一个启用了 CONFIG_PROC_EVENTS 的 Linux 内核。

于 2022-03-04T06:58:50.547 回答