ZetCode

Python os.wait4 函数

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

本综合指南探讨了 Python 的 os.wait4 函数,该函数等待子进程完成并检索资源使用情况。我们将介绍进程管理、资源监控和实际示例。

基本定义

os.wait4 函数等待子进程完成,并返回其终止状态以及资源使用信息。

关键参数:pid (要等待的进程 ID)、options (例如 WNOHANG 的标志)。返回 (pid, status, resource_usage) 的元组。仅限 Unix 系统。

基本进程等待

os.wait4 最简单的用法是等待任何子进程完成。此示例创建一个子进程并等待其终止。

basic_wait.py
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 变为非阻塞。如果没有子进程终止,它会立即返回。这允许在执行其他工作时进行轮询。

non_blocking.py
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 来等待特定的子进程。这在独立管理多个子进程时非常有用。

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

在处理完特定子进程后,它会等待剩余的子进程,以防止出现僵尸进程。

处理资源使用信息

资源使用信息提供了有关子进程执行的详细统计信息。此示例演示如何解释这些值。

resource_usage.py
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 值中提供有关终止原因的信息。

signal_handling.py
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 时非常有用。

any_child.py
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 列表为空)。这种模式在管理多个工作线程的服务器进程中很常见。

安全注意事项

最佳实践

资料来源

作者

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

列出所有 Python 教程