Python os.wait4 函数
上次修改时间:2025 年 4 月 11 日
本综合指南探讨了 Python 的 os.wait4
函数,该函数等待子进程完成并检索资源使用情况。我们将介绍进程管理、资源监控和实际示例。
基本定义
os.wait4
函数等待子进程完成,并返回其终止状态以及资源使用信息。
关键参数:pid (要等待的进程 ID)、options (例如 WNOHANG 的标志)。返回 (pid, status, resource_usage) 的元组。仅限 Unix 系统。
基本进程等待
os.wait4
最简单的用法是等待任何子进程完成。此示例创建一个子进程并等待其终止。
import os import time pid = os.fork() if pid == 0: # Child process print("Child process running") time.sleep(2) print("Child process exiting") os._exit(0) else: # Parent process print(f"Parent waiting for child {pid}") pid, status, rusage = os.wait4(pid, 0) print(f"Child {pid} exited with status {status}") print(f"Resource usage: {rusage}")
这会创建一个休眠 2 秒的子进程。父进程使用 os.wait4 等待其完成。资源使用信息包括 CPU 时间和内存使用情况。
status 包含退出代码和信号信息(如果进程因信号而终止)。
使用 WNOHANG 进行非阻塞等待
WNOHANG 选项使 os.wait4 变为非阻塞。如果没有子进程终止,它会立即返回。这允许在执行其他工作时进行轮询。
import os import time pid = os.fork() if pid == 0: # Child time.sleep(3) os._exit(42) else: # Parent print("Parent checking child status...") while True: result = os.wait4(pid, os.WNOHANG) if result[0] == 0: print("Child still running...") time.sleep(1) else: pid, status, rusage = result print(f"Child exited with status {status >> 8}") break
父进程定期检查子进程的状态而不阻塞。当使用 WNOHANG 并且子进程尚未退出时,wait4 返回 (0, 0, 0)。
退出状态编码在 status 值中。右移 8 位提取实际退出代码(在本例中为 42)。
等待特定进程
您可以通过提供其 PID 来等待特定的子进程。这在独立管理多个子进程时非常有用。
import os import time children = [] for i in range(3): pid = os.fork() if pid == 0: # Child time.sleep(i + 1) os._exit(i) else: children.append(pid) print(f"Started child {pid}") # Wait for middle child (second in list) target_pid = children[1] pid, status, rusage = os.wait4(target_pid, 0) print(f"Child {pid} exited with status {status >> 8}") # Wait for remaining children for pid in children: if pid != target_pid: os.wait4(pid, 0)
这会创建三个具有不同睡眠时长的子进程。父进程首先专门等待第二个子进程(children[1] 中的 PID)。
在处理完特定子进程后,它会等待剩余的子进程,以防止出现僵尸进程。
处理资源使用信息
资源使用信息提供了有关子进程执行的详细统计信息。此示例演示如何解释这些值。
import os import time import resource pid = os.fork() if pid == 0: # Child # Use some CPU and memory start = time.time() while time.time() - start < 1: # Burn CPU for 1 second pass data = "x" * 10_000_000 # Allocate memory time.sleep(0.5) os._exit(0) else: # Parent pid, status, rusage = os.wait4(pid, 0) print("Resource usage statistics:") print(f"User CPU time: {rusage.ru_utime:.3f} seconds") print(f"System CPU time: {rusage.ru_stime:.3f} seconds") print(f"Max RSS: {rusage.ru_maxrss} KB") print(f"Page faults: {rusage.ru_majflt} major, {rusage.ru_minflt} minor")
子进程执行 CPU 密集型工作并分配内存。子进程退出后,父进程打印详细的资源使用情况统计信息。
关键指标包括 CPU 时间(用户和系统)、最大常驻集大小(内存)和页面错误计数。
处理已终止的子进程
当子进程异常终止(例如,通过信号)时,os.wait4 会在 status 值中提供有关终止原因的信息。
import os import signal import time pid = os.fork() if pid == 0: # Child print("Child running") time.sleep(5) print("Child exiting normally") os._exit(0) else: # Parent time.sleep(1) os.kill(pid, signal.SIGTERM) pid, status, rusage = os.wait4(pid, 0) if os.WIFEXITED(status): print(f"Child exited normally with status {os.WEXITSTATUS(status)}") elif os.WIFSIGNALED(status): print(f"Child terminated by signal {os.WTERMSIG(status)}") elif os.WIFSTOPPED(status): print(f"Child stopped by signal {os.WSTOPSIG(status)}")
父进程在 1 秒后向子进程发送 SIGTERM。然后,wait4 调用返回有关此信号终止的信息。
WIF... 函数有助于解释 status 值,以确定进程的终止方式(正常、通过信号或已停止)。
等待任何子进程
将 pid=-1 与 os.wait4 一起使用会等待任何子进程终止。这在管理多个子进程而无需跟踪特定 PID 时非常有用。
import os import time import random # Start several child processes children = [] for i in range(5): pid = os.fork() if pid == 0: # Child sleep_time = random.randint(1, 5) time.sleep(sleep_time) os._exit(sleep_time) else: children.append(pid) print(f"Started child {pid} (will sleep {i+1}s)") # Wait for any child to finish while children: pid, status, rusage = os.wait4(-1, 0) exit_code = os.WEXITSTATUS(status) print(f"Child {pid} exited after {exit_code} seconds") children.remove(pid) print("All children exited")
这会创建 5 个具有随机睡眠时长的子进程。父进程使用 pid=-1 等待任何子进程完成,并按终止顺序处理它们。
循环继续,直到所有子进程都退出(children 列表为空)。这种模式在管理多个工作线程的服务器进程中很常见。
安全注意事项
- PID 重用: 请注意 wait 调用之间潜在的 PID 重用
- 僵尸进程: 始终等待子进程以防止出现僵尸进程
- 信号处理: 信号可能会中断 wait 调用(使用 EINTR 处理)
- 权限: 需要适当的权限才能等待进程
- 平台限制: 行为可能因类 Unix 系统而异
最佳实践
- 错误处理: 检查并处理 EINTR 错误
- 资源监控: 使用 rusage 进行性能跟踪
- 信号感知: 适当处理 WIFSIGNALED 情况
- 子进程跟踪: 维护多个子进程的 PID 列表
- 超时模式: 将 WNOHANG 与超时结合使用以提高鲁棒性
资料来源
作者
列出所有 Python 教程。