Python os.wait3 函数
上次修改时间:2025 年 4 月 11 日
本综合指南探讨 Python 的 os.wait3 函数,该函数等待子进程完成并返回资源使用情况数据。我们将介绍进程管理、状态解释和实践示例。
基本定义
os.wait3 函数等待任何子进程完成,并返回其进程 ID、退出状态和资源使用信息。
主要参数:options (WNOHANG, WUNTRACED)。返回 (pid, status, resource_usage) 的元组。仅在类 Unix 系统上可用。
基本进程等待
os.wait3 的最简单用法是等待任何子进程终止。此示例创建一个子进程并等待其完成。
import os
import sys
pid = os.fork()
if pid == 0: # Child process
print("Child process running")
sys.exit(42)
else: # Parent process
print(f"Parent waiting for child {pid}")
pid, status, rusage = os.wait3(0)
print(f"Child {pid} exited with status {status}")
print(f"Exit code: {os.WEXITSTATUS(status)}")
print(f"Resource usage: {rusage}")
此示例 fork 一个退出代码为 42 的子进程。父进程等待子进程并打印其退出状态和资源使用信息。
os.WEXITSTATUS 宏从 wait3 返回的状态值中提取退出代码。资源使用情况包括 CPU 时间和内存统计信息。
使用 WNOHANG 进行非阻塞等待
WNOHANG 选项使 os.wait3 成为非阻塞的。如果没有子进程终止,它会立即返回。这允许轮询。
import os
import time
pid = os.fork()
if pid == 0: # Child
print("Child sleeping for 2 seconds")
time.sleep(2)
os._exit(0)
else: # Parent
print("Parent polling for child completion")
while True:
result = os.wait3(os.WNOHANG)
if result[0] != 0: # Child exited
print(f"Child {result[0]} exited")
break
print("Child still running...")
time.sleep(0.5)
父进程使用 WNOHANG 每 0.5 秒轮询一次子进程完成情况。这可以防止在等待子进程完成睡眠时发生阻塞。
当没有子进程退出时,wait3 返回 (0, 0, 0)。仅当子进程终止时才返回实际的子进程 PID。
处理多个子进程
os.wait3 可以管理多个子进程。此示例创建多个子进程,并在它们终止时按顺序等待它们。
import os
import time
import random
children = []
for i in range(3):
pid = os.fork()
if pid == 0: # Child
sleep_time = random.randint(1, 3)
print(f"Child {i} sleeping for {sleep_time}s")
time.sleep(sleep_time)
os._exit(i)
else: # Parent
children.append(pid)
print(f"Parent waiting for children {children}")
while children:
pid, status, _ = os.wait3(0)
exit_code = os.WEXITSTATUS(status)
print(f"Child {pid} exited with code {exit_code}")
children.remove(pid)
创建了三个具有随机睡眠持续时间的子进程。父进程按照它们完成的顺序等待每个子进程,而不是按照它们创建的顺序。
children 列表跟踪活动的子进程 PID。 随着每个子进程退出,它会从列表中删除,直到所有子进程都被收回。
捕获资源使用情况
os.wait3 中的资源使用情况元组提供了有关 CPU 时间、内存使用情况和其他系统资源的详细信息。
import os
import time
def child_work():
# Simulate CPU and memory usage
start = time.time()
data = []
while time.time() - start < 1: # Run for 1 second
data.append("x" * 1000) # Allocate memory
return len(data)
pid = os.fork()
if pid == 0: # Child
count = child_work()
os._exit(count)
else: # Parent
pid, status, rusage = os.wait3(0)
print(f"Child user time: {rusage.ru_utime:.3f}s")
print(f"Child system time: {rusage.ru_stime:.3f}s")
print(f"Max RSS: {rusage.ru_maxrss} KB")
print(f"Page faults: {rusage.ru_majflt}")
print(f"Blocks in: {rusage.ru_inblock}")
print(f"Blocks out: {rusage.ru_oublock}")
子进程执行 CPU 密集型工作和内存分配。 父进程在子进程退出后捕获详细的资源使用情况统计信息。
主要指标包括用户 CPU 时间、系统 CPU 时间、最大常驻集大小、页面错误和块 I/O 操作。
处理停止的进程
使用 WUNTRACED 选项,os.wait3 可以检测子进程何时被停止(例如,通过 SIGSTOP),而不是被终止。
import os
import signal
import time
pid = os.fork()
if pid == 0: # Child
print("Child running, will be stopped")
time.sleep(10) # Give time to send signal
os._exit(0)
else: # Parent
print(f"Parent waiting for child {pid}")
time.sleep(1) # Wait a moment before stopping
os.kill(pid, signal.SIGSTOP)
pid, status, _ = os.wait3(os.WUNTRACED)
if os.WIFSTOPPED(status):
print(f"Child {pid} stopped by signal {os.WSTOPSIG(status)}")
# Continue the child
os.kill(pid, signal.SIGCONT)
pid, status, _ = os.wait3(0)
print(f"Child {pid} exited with status {status}")
父进程向子进程发送 SIGSTOP,然后使用 WUNTRACED 检测停止状态。 然后,它使用 SIGCONT 继续子进程,并等待其终止。
os.WIFSTOPPED 检查子进程是否已停止,而 os.WSTOPSIG 获取停止信号编号。
错误处理
os.wait3 在某些情况下可能会引发 OSError。 此示例演示了等待子进程时的正确错误处理。
import os
import errno
try:
# Try to wait when no children exist
pid, status, rusage = os.wait3(os.WNOHANG)
print("This won't be reached")
except OSError as e:
if e.errno == errno.ECHILD:
print("No child processes to wait for")
else:
print(f"Unexpected error: {e}")
# Create and immediately wait for child
pid = os.fork()
if pid == 0:
os._exit(0)
else:
try:
pid, status, rusage = os.wait3(0)
print(f"Child {pid} exited normally")
except OSError as e:
print(f"Error waiting for child: {e}")
第一次尝试等待失败,并出现 ECHILD(没有子进程)。 第二部分显示了创建子进程后成功等待的情况。
正确的错误处理至关重要,因为系统条件可能会在进程创建和等待操作之间发生变化。
高级进程组
os.wait3 可以监视特定进程组中的进程。 此示例演示了管理子进程的进程组。
import os
import time
# Create new process group
os.setpgrp()
children = []
for i in range(3):
pid = os.fork()
if pid == 0: # Child
# Children in same process group
print(f"Child {i} in group {os.getpgrp()}")
time.sleep(i + 1)
os._exit(0)
else:
children.append(pid)
print(f"Parent waiting for children in group {os.getpgrp()}")
while children:
try:
pid, status, _ = os.wait3(0)
if pid > 0:
print(f"Child {pid} exited")
children.remove(pid)
except OSError as e:
if e.errno == errno.ECHILD:
break
raise
print("All children exited")
这会创建一个新的进程组和几个子进程。 父进程等待其进程组中的所有子进程退出,并适当处理错误。
进程组允许一起管理相关进程,并且对于 shell 作业控制实现特别有用。
安全注意事项
- 竞争条件: 子进程状态可能在检查之间发生变化
- 信号处理: SIGCHLD 可能会干扰 wait3
- 资源限制: 进程过多可能导致失败
- 权限: 需要权限才能等待子进程
- 平台限制: 行为在 Unix 系统之间有所不同
最佳实践
- 始终等待: 防止僵尸进程积累
- 处理错误: 检查 ECHILD 和其他条件
- 使用 WNOHANG: 用于在需要时进行非阻塞操作
- 检查状态: 使用 WIFEXITED/WIFSIGNALED 宏
- 监视资源: 分析 rusage 以获取性能数据
资料来源
作者
列出所有 Python 教程。