题目与范围
进程退出后数字 PID 可能重用,延迟信号或状态检查会命中错误进程。Linux 提供 pidfdopen() 获取指向进程的文件描述符,pidfdsend_signal() 通过它发送信号。核心能力是系统编程与生命周期正确性,因此分类为 coding。
面试官考察点
应解释描述符身份、poll/epoll 就绪、waitid 集成、close-on-exec、权限和清理。区分 pidfd 与无限权限的进程句柄,处理 fork/exec 竞态,并定义退出、重启、超时和监管器崩溃行为。还要说明内核特性检测和经过测试的回退。
先澄清的问题
- 监管器需要支持哪些内核版本和命名空间?
- 它自己启动子进程,还是附着已有进程?
- 需要观察退出状态、发送信号,还是两者都要?
- 工作进程会 fork 后代吗,后代由谁清理?
- 超时、重启和监管器崩溃保证是什么?
- 不支持 pidfd 的内核是否需要回退?
30 秒答题框架
“为每个受管进程获取 pidfd,保存描述符而非只保存数字 PID。把描述符加入 poll 或 epoll,就绪后用规范等待操作收集状态,用 pidfdsendsignal() 处理超时和关闭。设置 close-on-exec,在每条终止路径关闭描述符,把就绪与状态收集纳入同一状态机。测试 PID 重用、快速退出、权限错误、后代进程、内核检测和监管器重启。”
分步作答
步骤 1:获取并拥有描述符
启动或定位进程后,在支持时调用 pidfd_open(),把描述符放进所有者表。设置 close-on-exec,数字 PID 只用于日志。没有明确所有权转移时,不把描述符交给无关工作器。
步骤 2:观察生命周期事件
把 pidfd 注册到 poll 或 epoll。就绪表示引用的进程已退出;用适当等待操作收集状态,再关闭描述符。不能依赖陈旧 /proc 路径或 PID 整数判断存活。
步骤 3:向正确进程发信号
用 pidfdsendsignal() 执行优雅终止和升级终止,显式处理权限和命名空间错误。pidfd 能指向原进程,即使数字 PID 后来重用,仍不能绕过授权检查。
步骤 4:建模重启与后代
把工作器状态定义为启动、运行、停止、退出或失败。重启时生成新的 pidfd 和代次记录。明确后代属于进程组、cgroup 还是独立所有域,不能假设杀父进程会清理所有子进程。
步骤 5:测试竞态与可移植边界
压测快速退出和 PID 重用、退出并发信号、描述符耗尽、监管器崩溃、命名空间变化和不支持的内核。与直接子进程的 waitpid 等受限回退比较,并记录回退无法提供的保证。
参考答案
“缺陷来自把可重用整数当成进程身份。我会为每个工作器保存 pidfd,加入事件循环,就绪后收集退出状态,并用 pidfdsendsignal() 执行优雅停止和升级终止。描述符所有权、close-on-exec、权限和后代清理成为显式状态机规则。每次重启创建新的代次和 pidfd。测试必须强制快速退出与 PID 重用,覆盖命名空间和内核支持,并记录直接子进程回退较弱的保证。”
常见错误
- 只保存数字 PID → 延迟信号与 PID 重用竞态 → 保留 pidfd。
- 轮询
/proc判断存活 → 观察结果会过期 → 使用 pidfd 就绪和状态收集。 - 认为 pidfd 绕过权限 → 信号仍需授权 → 处理权限错误。
- exec 时泄漏描述符 → 无关程序继承生命周期句柄 → 设置 close-on-exec。
- 只杀父进程 → 后代成为孤儿 → 定义进程组或 cgroup 所有权。
- 把就绪当作完整状态 → 退出码处理不完整 → 收集并保存等待状态。
追问
追问 1:pidfd 能阻止 PID 重用吗?
对于接受 pidfd 的操作,它提供稳定的描述符引用。数字 PID 可以重用,但通过描述符执行的操作仍指向原进程。
追问 2:pidfd 能和 epoll 一起用吗?
可以。pidfd 会在进程退出时变为可轮询,因此能和管道、定时器及控制 socket 一起进入监管器事件循环。
追问 3:内核不支持 pidfd 怎么办?
启动时检测能力,对直接子进程使用文档化回退,并公开较弱的竞态和可观测性保证,不能假装等价。
追问 4:监管器重启后怎么办?
持久化足够的所有权元数据以重新发现或有意放弃工作器,再重建监控状态和 pidfd。不能只信任持久化数字 PID。