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 教程。