You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现非父子进程间的进程终止事件驱动通知?

问题概述

需要监控一个非父子关系的进程,当该进程无论正常终止还是崩溃(如被kill -9)时,监控进程能收到事件通知,且要求采用事件驱动方式,避免轮询/proc/<PID>目录。

已尝试方案的问题复盘
  • 对/proc/<PID>目录使用epoll():epoll无法直接监听目录,此方案不可行。
  • 考虑inotify:误以为其不支持系统文件/目录,但实际可监控/proc下的资源(后续会说明),但之前未尝试。
  • 临时文件机制:进程崩溃时无法主动清理文件,无法触发终止通知。
  • 子进程等待机制(如waitpid()):仅适用于父子进程关系,不符合场景。
  • Python NamedTemporaryFile():进程终止后文件未自动删除,无法依赖此判断进程状态。
  • 监听/proc/<PID>下可打开文件的epoll事件:会产生大量无效事件,不符合需求。
可行事件驱动方案

方案1:使用pidfd_open() + epoll(推荐,Linux 5.3+)

这是最直接且高效的方案,Linux 5.3及以上版本提供了pidfd_open()系统调用,可创建一个与目标PID绑定的文件描述符(PID FD),之后通过epoll监听该FD的EPOLLIN事件:

  • 当被监控进程终止时,PID FD会触发EPOLLIN事件,监控进程可通过read()读取退出状态(或直接感知事件)。
  • 无论进程是正常退出、崩溃还是被强制杀死,都会触发事件,完全满足需求。

示例代码(C语言)

#include <sys/epoll.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

#define PIDFD_OPEN 438  // 对应系统调用号,不同架构可能有差异

int main(int argc, char *argv[]) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <pid>\n", argv[0]);
        exit(1);
    }
    pid_t target_pid = atoi(argv[1]);

    // 创建PID文件描述符
    int pidfd = syscall(PIDFD_OPEN, target_pid, 0);
    if (pidfd == -1) {
        perror("pidfd_open failed");
        exit(1);
    }

    // 初始化epoll
    int epfd = epoll_create1(0);
    if (epfd == -1) {
        perror("epoll_create1 failed");
        close(pidfd);
        exit(1);
    }

    struct epoll_event ev;
    ev.events = EPOLLIN;
    ev.data.fd = pidfd;
    if (epoll_ctl(epfd, EPOLL_CTL_ADD, pidfd, &ev) == -1) {
        perror("epoll_ctl failed");
        close(pidfd);
        close(epfd);
        exit(1);
    }

    printf("Monitoring PID %d...\n", target_pid);
    struct epoll_event events[1];
    int n = epoll_wait(epfd, events, 1, -1);
    if (n == -1) {
        perror("epoll_wait failed");
    } else {
        printf("Process %d has terminated\n", target_pid);
        // 可选:读取退出状态
        siginfo_t info;
        if (syscall(SYS_pidfd_getfd, pidfd, &info, 0) == 0) {
            printf("Exit status: %d\n", info.si_status);
        }
    }

    close(pidfd);
    close(epfd);
    return 0;
}

Python 实现思路

可通过ctypes调用pidfd_open系统调用,结合select或asyncio监听事件,示例框架:

import ctypes
import select

libc = ctypes.CDLL("libc.so.6")
PIDFD_OPEN = 438

def pidfd_open(pid):
    return libc.syscall(PIDFD_OPEN, pid, 0)

target_pid = 12345
pidfd = pidfd_open(target_pid)

epoll = select.epoll()
epoll.register(pidfd, select.EPOLLIN)

print(f"Monitoring PID {target_pid}...")
events = epoll.poll()
for fd, event in events:
    if fd == pidfd and event & select.EPOLLIN:
        print(f"Process {target_pid} terminated")

epoll.unregister(pidfd)
epoll.close()
libc.close(pidfd)

方案2:使用inotify监控/proc/<PID>目录(兼容旧Linux版本)

虽然/proc是虚拟文件系统,但inotify可以监控其下的目录删除事件:

  1. 创建inotify实例,添加对/proc/<PID>目录的IN_DELETE_SELF事件监听。
  2. 当被监控进程终止时,/proc/<PID>目录会被删除,触发IN_DELETE_SELF事件,监控进程即可收到通知。

注意事项

  • 需要确保监控时/proc/<PID>目录存在(可先检查一次),避免添加监听失败。
  • 部分系统中inotify对/proc的支持可能有差异,但大多数主流发行版(如Ubuntu、CentOS)都支持。

示例代码(C语言)

#include <sys/inotify.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define BUF_LEN 1024

int main(int argc, char *argv[]) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <pid>\n", argv[0]);
        exit(1);
    }
    char proc_path[256];
    snprintf(proc_path, sizeof(proc_path), "/proc/%s", argv[1]);

    int fd = inotify_init();
    if (fd == -1) {
        perror("inotify_init failed");
        exit(1);
    }

    int wd = inotify_add_watch(fd, proc_path, IN_DELETE_SELF);
    if (wd == -1) {
        perror("inotify_add_watch failed");
        close(fd);
        exit(1);
    }

    printf("Monitoring %s...\n", proc_path);
    char buf[BUF_LEN];
    ssize_t len = read(fd, buf, BUF_LEN);
    if (len == -1) {
        perror("read failed");
    } else {
        struct inotify_event *event = (struct inotify_event *)buf;
        if (event->mask & IN_DELETE_SELF) {
            printf("Process %s has terminated\n", argv[1]);
        }
    }

    inotify_rm_watch(fd, wd);
    close(fd);
    return 0;
}

方案3:使用ptrace(不推荐,侵入性强)

通过ptrace附加到目标进程,当进程终止时会触发SIGCHLD信号给监控进程。但此方案存在明显缺点:

  • 会暂停目标进程,需要调用ptrace(PTRACE_CONT)恢复,影响进程运行。
  • 需要额外权限(如CAP_SYS_PTRACE),且可能与其他监控工具冲突。
  • 侵入性强,仅在上述方案不可用时考虑。
总结
  • 优先选择**pidfd_open() + epoll**方案,高效且无侵入性,是Linux内核提供的原生解决方案。
  • 若系统版本较低,可采用**inotify监控/proc/<PID>**目录的方式,实现事件驱动的进程终止通知。

内容的提问来源于stack exchange,提问作者dbeberman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 04:55:01