Python os.wait 函数
上次修改时间:2025 年 4 月 11 日
本综合指南探讨 Python 的 os.wait 函数,该函数等待子进程完成。 我们将介绍进程管理、退出状态解释和实际同步示例。
基本定义
os.wait 函数会暂停执行,直到子进程终止。 它返回一个包含进程 ID 和退出状态的元组。
主要特性:阻塞直到子进程退出,回收僵尸进程,返回 (pid, status) 元组。 仅适用于调用者的子进程。
基本进程等待
此示例演示了使用 os.wait 等待单个子进程完成的最简单方法。 父进程 fork 一个休眠的子进程。
import os
import time
pid = os.fork()
if pid == 0: # Child process
print("Child process started")
time.sleep(2)
print("Child process exiting")
os._exit(0)
else: # Parent process
print(f"Parent waiting for child {pid}")
child_pid, status = os.wait()
print(f"Child {child_pid} exited with status {status}")
父进程 fork 一个休眠 2 秒的子进程。 父进程调用 os.wait(),该调用会阻塞直到子进程终止。 打印退出状态。
请注意,子进程中使用 os._exit() 以确保正确的进程终止,而无需运行 Python 的清理处理程序。
处理多个子进程
此示例展示了如何使用循环等待多个子进程。 父进程创建多个子进程并等待每个子进程完成。
import os
import time
import random
children = []
# Create 3 child processes
for i in range(3):
pid = os.fork()
if pid == 0: # Child
sleep_time = random.randint(1, 3)
print(f"Child {os.getpid()} sleeping for {sleep_time}s")
time.sleep(sleep_time)
os._exit(0)
else: # Parent
children.append(pid)
# Wait for all children to complete
while children:
pid, status = os.wait()
print(f"Child {pid} exited with status {status}")
children.remove(pid)
print("All children have exited")
父进程创建 3 个具有随机睡眠持续时间的子进程。 它维护一个子进程 PID 列表,并使用 os.wait() 按顺序等待每个子进程。
完成顺序取决于睡眠时间。 os.wait() 返回任何已终止的子进程,不一定按创建顺序返回。
使用 WNOHANG 进行非阻塞等待
WNOHANG 选项使 os.waitpid 成为非阻塞,允许轮询子进程完成情况。 此示例演示了定期状态检查。
import os
import time
pid = os.fork()
if pid == 0: # Child
print("Child running")
time.sleep(3)
print("Child exiting")
os._exit(42)
else: # Parent
print(f"Parent waiting for child {pid}")
while True:
try:
child_pid, status = os.waitpid(pid, os.WNOHANG)
if child_pid == 0:
print("Child still running...")
time.sleep(1)
else:
print(f"Child exited with status {status}")
break
except ChildProcessError:
print("No such child process")
break
父进程使用带有 WNOHANG 的 os.waitpid 定期检查子进程的状态。 如果子进程尚未退出,waitpid 会立即返回 (0, 0)。
当父进程需要在不阻塞的情况下执行其他工作,同时监视子进程时,此模式很有用。
解释退出状态
os.wait 返回的退出状态包含编码信息。 此示例展示了如何使用 os.WIFEXITED 和相关函数解释它。
import os
import sys
pid = os.fork()
if pid == 0: # Child
print("Child running")
# Exit with different statuses
if len(sys.argv) > 1:
os._exit(int(sys.argv[1]))
else:
os._exit(0)
else: # Parent
pid, status = os.wait()
print(f"Raw status: {status}")
if os.WIFEXITED(status):
print(f"Child exited normally with status {os.WEXITSTATUS(status)}")
elif os.WIFSIGNALED(status):
print(f"Child killed by signal {os.WTERMSIG(status)}")
elif os.WIFSTOPPED(status):
print(f"Child stopped by signal {os.WSTOPSIG(status)}")
子进程以作为参数提供的状态或 0 退出。 父进程使用 os.WIF* 函数解释状态,以确定子进程的退出方式。
这些宏有助于区分正常退出与信号或进程停止导致的终止,从而提供详细的进程终止信息。
等待特定进程
os.waitpid 允许等待特定子进程,而不是任何子进程。 此示例演示了有针对性的进程等待。
import os
import time
# Create two children
child1 = os.fork()
if child1 == 0:
time.sleep(1)
os._exit(10)
child2 = os.fork()
if child2 == 0:
time.sleep(2)
os._exit(20)
# Parent waits for specific child
print(f"Waiting for child {child2}")
pid, status = os.waitpid(child2, 0)
print(f"Child {pid} exited with status {status}")
# Now wait for remaining child
pid, status = os.wait()
print(f"Child {pid} exited with status {status}")
父进程创建两个具有不同睡眠持续时间的子进程。 它首先使用 waitpid 专门等待 child2,然后等待任何剩余的子进程。
当您需要管理具有不同优先级或彼此之间存在依赖关系的多个子进程时,此方法很有用。
处理孤立进程
此示例展示了如何处理子进程可能变为孤立进程的情况。 父进程在等待其子进程之前终止。
import os
import time
import sys
pid = os.fork()
if pid == 0: # Child
print("Child running")
time.sleep(5)
print("Child exiting")
os._exit(0)
else: # Parent
print(f"Parent created child {pid}")
if len(sys.argv) > 1 and sys.argv[1] == "wait":
pid, status = os.wait()
print(f"Parent saw child exit with status {status}")
else:
print("Parent exiting without waiting")
sys.exit(0)
在不带参数运行的情况下,父进程立即退出,使子进程成为孤立进程。 使用 "wait" 参数,父进程会正确等待。 观察进程层次结构。
孤立的子进程由 init (PID 1) 采用,init 最终会回收它们。 正确的进程管理可防止僵尸进程。
错误处理
此示例演示了使用 os.wait 时的正确错误处理,包括没有子进程或中断系统调用的情况。
import os
import time
import errno
# Case 1: No child processes
try:
pid, status = os.wait()
print(f"Child {pid} exited with status {status}")
except ChildProcessError:
print("No child processes to wait for")
# Case 2: Interrupted system call
pid = os.fork()
if pid == 0:
time.sleep(3)
os._exit(0)
else:
try:
# Simulate interrupt (in real code this might happen naturally)
import signal
signal.alarm(1) # SIGALRM in 1 second
pid, status = os.wait()
except InterruptedError:
print("Wait was interrupted")
# Typically you would retry the wait here
pid, status = os.wait()
print(f"Child {pid} exited with status {status}")
第一个案例显示了没有子进程时如何处理。 第二个案例演示了中断处理,这在使用信号处理程序时很常见。 正确的错误恢复。
处理进程管理时,始终要处理潜在的错误,以避免应用程序中出现意外行为或资源泄漏。
安全注意事项
- 僵尸进程: 始终等待子进程以防止僵尸进程
- 信号安全: 请注意,等待可能会被信号中断
- 权限: 子进程继承父进程的权限
- 资源限制: 过多的子进程可能会达到系统限制
- 平台差异: 在 Unix 系统之间,行为可能会有所不同
最佳实践
- 始终等待: 防止僵尸进程累积
- 检查状态: 正确解释退出状态
- 处理错误: 考虑 ECHILD 和 EINTR
- 考虑替代方案: subprocess 模块可能更简单
- 记录行为: 在您的 API 中记录进程管理
资料来源
作者
列出所有 Python 教程。