Python os.waitpid 函数
上次修改时间:2025 年 4 月 11 日
这份全面的指南探讨了 Python 的 os.waitpid 函数,该函数等待子进程完成并检索其退出状态。我们将涵盖进程管理、状态解释和实际示例。
基本定义
os.waitpid 函数等待由进程 ID (pid) 指定的子进程完成,并返回其退出状态信息。
关键参数:pid(要等待的进程 ID),options(如 WNOHANG 的标志)。返回一个 (pid, status) 元组。Status 包含退出代码和信号信息。
基本进程等待
此示例演示了 os.waitpid 的最简单用法,即等待子进程完成。父进程无限期地等待。
import os
import sys
# Fork a child process
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}")
child_pid, status = os.waitpid(pid, 0)
print(f"Child {child_pid} exited with status {status}")
if os.WIFEXITED(status):
print(f"Exit code: {os.WEXITSTATUS(status)}")
父进程 fork 一个以代码 42 退出子进程。父进程使用 waitpid 等待子进程完成。WIFEXITED 检查是否正常退出。
该状态包含比退出代码更多的信息,包括如果进程被信号终止的信号信息。
使用 WNOHANG 进行非阻塞等待
WNOHANG 选项使 os.waitpid 成为非阻塞的,如果子进程尚未退出,则立即返回。这允许轮询。
import os
import time
pid = os.fork()
if pid == 0:
# Child process runs for 3 seconds
time.sleep(3)
os._exit(0)
# Parent polls child status
while True:
child_pid, status = os.waitpid(pid, os.WNOHANG)
if child_pid == 0:
print("Child still running...")
time.sleep(1)
else:
print(f"Child {child_pid} exited")
break
父进程每秒检查一次子进程状态,而不阻塞。当子进程在 3 秒后退出时,waitpid 返回子进程的 PID 和状态。
当您需要在继续在父进程中执行其他任务的同时监视进程时,此模式很有用。
等待任何子进程
使用 pid -1 会等待任何子进程。当管理多个子进程并且您不知道哪个进程会先完成时,这很有用。
import os
import time
import random
# Create 3 child processes
children = []
for i in range(3):
pid = os.fork()
if pid == 0:
# Child sleeps for random time and exits
sleep_time = random.randint(1, 5)
time.sleep(sleep_time)
os._exit(sleep_time)
else:
children.append(pid)
# Wait for any child to complete
while children:
pid, status = os.waitpid(-1, 0)
if os.WIFEXITED(status):
print(f"Child {pid} exited after {os.WEXITSTATUS(status)} seconds")
children.remove(pid)
print("All children exited")
父进程创建 3 个休眠随机时长的子进程。使用 pid=-1,它等待任何一个子进程先退出,并将其从跟踪列表中删除。
这种方法在同时处理多个客户端进程的服务器中很常见。
处理已终止的子进程
此示例演示如何使用 os.waitpid 返回的状态信息来处理被信号终止的子进程。
import os
import signal
import time
pid = os.fork()
if pid == 0:
# Child process waits to be killed
print("Child running (PID: {})".format(os.getpid()))
while True:
time.sleep(1)
else:
# Parent waits briefly then kills child
time.sleep(2)
os.kill(pid, signal.SIGTERM)
# Wait for child and check status
child_pid, status = os.waitpid(pid, 0)
if os.WIFSIGNALED(status):
sig = os.WTERMSIG(status)
print(f"Child {child_pid} killed by signal {sig} ({signal.Signals(sig).name})")
elif os.WIFEXITED(status):
print(f"Child exited normally with status {os.WEXITSTATUS(status)}")
父进程在 2 秒后向子进程发送 SIGTERM。waitpid 返回状态信息,我们使用 WIFSIGNALED 和 WTERMSIG 对其进行解码。
这演示了在管理子进程时如何区分正常退出和信号引起的终止。
等待进程组
使用小于 -1 的 pid 会等待指定进程组中的任何子进程。这对于管理相关进程组很有用。
import os
import time
# Create a new process group
os.setpgrp()
# Create 3 child processes in the same group
for i in range(3):
pid = os.fork()
if pid == 0:
# Children sleep and exit
time.sleep(i + 1)
os._exit(0)
# Parent waits for any child in our process group
# Negative PID means wait for any in that process group
group_pid = -os.getpgrp()
exited = 0
while exited < 3:
child_pid, status = os.waitpid(group_pid, 0)
exited += 1
print(f"Child {child_pid} exited (total exited: {exited})")
父进程创建一个新的进程组并派生 3 个子进程。使用等于组 ID 负数的负 pid,它等待组中的任何子进程。
当您想要一起管理进程的所有后代时,此技术特别有用。
高级状态解释
此示例演示了使用所有可用的宏来检查 waitpid 状态值的全面状态解释。
import os
import signal
import random
def analyze_status(pid, status):
print(f"\nAnalysis for process {pid}:")
if os.WIFEXITED(status):
print(f" Exited normally with status {os.WEXITSTATUS(status)}")
if os.WIFSIGNALED(status):
print(f" Killed by signal {os.WTERMSIG(status)}")
print(f" Core dumped: {os.WCOREDUMP(status)}")
if os.WIFSTOPPED(status):
print(f" Stopped by signal {os.WSTOPSIG(status)}")
if os.WIFCONTINUED(status):
print(" Continued")
# Create child that might exit normally, be killed, or stopped
pid = os.fork()
if pid == 0:
# Child process
fate = random.choice(['exit', 'segfault', 'stop'])
if fate == 'exit':
os._exit(42)
elif fate == 'segfault':
os.kill(os.getpid(), signal.SIGSEGV)
else:
os.kill(os.getpid(), signal.SIGSTOP)
os._exit(0)
else:
# Parent waits and analyzes
child_pid, status = os.waitpid(pid, 0)
analyze_status(child_pid, status)
子进程随机选择正常退出、段错误或停止。父进程使用各种宏来解释 waitpid 返回的状态。
这演示了在 Python 中使用子进程时状态解释的所有可能性。
等待孤立进程
此示例演示了当原始父进程退出时,os.waitpid 如何处理已重新父级化的孤立进程(PID 1)。
import os
import time
import sys
pid = os.fork()
if pid == 0:
# Child becomes orphan
print(f"Child PID: {os.getpid()}")
print("Child sleeping for 10 seconds (will be orphaned)")
time.sleep(10)
print("Orphan child exiting")
sys.exit(0)
else:
# Parent exits immediately
print(f"Parent exiting, child will be orphaned")
sys.exit(0)
# This code only runs in the original parent
# The orphaned child continues running but can't be waited for
# by this process as it has been reparented to init
父进程立即退出,使子进程孤立并继续运行。原始父进程无法再等待此子进程,因为它已被重新父级化。
这演示了一个重要的限制 - 您只能等待直接子进程,而不能等待孙子进程或孤立进程。
安全注意事项
- PID 重用: 请注意 wait 调用之间潜在的 PID 重用
- 僵尸进程: 始终等待子进程以避免僵尸进程
- 竞争条件: 子进程可能在调用 waitpid 之前退出
- 权限问题: 需要权限才能等待特定进程
- 信号干扰: 信号可以中断 waitpid 调用
最佳实践
- 始终等待: 通过始终等待来防止僵尸进程
- 检查状态: 正确解释退出状态
- 处理错误: 考虑 ECHILD 和 EINTR 错误
- 谨慎使用 WNOHANG: 避免使用 WNOHANG 进行忙等待
- 考虑子进程模块: 对于新代码,请考虑子进程模块
资料来源
作者
列出所有 Python 教程。