ZetCode

Python os.waitpid 函数

上次修改时间:2025 年 4 月 11 日

这份全面的指南探讨了 Python 的 os.waitpid 函数,该函数等待子进程完成并检索其退出状态。我们将涵盖进程管理、状态解释和实际示例。

基本定义

os.waitpid 函数等待由进程 ID (pid) 指定的子进程完成,并返回其退出状态信息。

关键参数:pid(要等待的进程 ID),options(如 WNOHANG 的标志)。返回一个 (pid, status) 元组。Status 包含退出代码和信号信息。

基本进程等待

此示例演示了 os.waitpid 的最简单用法,即等待子进程完成。父进程无限期地等待。

basic_wait.py
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 成为非阻塞的,如果子进程尚未退出,则立即返回。这允许轮询。

non_blocking.py
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 会等待任何子进程。当管理多个子进程并且您不知道哪个进程会先完成时,这很有用。

wait_any.py
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 返回的状态信息来处理被信号终止的子进程。

signal_handling.py
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 会等待指定进程组中的任何子进程。这对于管理相关进程组很有用。

process_group.py
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 状态值的全面状态解释。

status_interpretation.py
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)。

orphaned.py
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

父进程立即退出,使子进程孤立并继续运行。原始父进程无法再等待此子进程,因为它已被重新父级化。

这演示了一个重要的限制 - 您只能等待直接子进程,而不能等待孙子进程或孤立进程。

安全注意事项

最佳实践

资料来源

作者

我叫 Jan Bodnar,是一名充满激情的程序员,拥有丰富的编程经验。 我自 2007 年以来一直在撰写编程文章。 迄今为止,我已撰写了 1,400 多篇文章和 8 本电子书。 我拥有超过十年的编程教学经验。

列出所有 Python 教程