ZetCode

Python os.kill 函数

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

本综合指南探讨 Python 的 os.kill 函数,该函数用于向进程发送信号。我们将涵盖信号类型、进程 ID 和实际的进程管理示例。

基本定义

os.kill 函数向由其进程 ID (PID) 标识的进程发送指定的信号。它是 Unix kill 系统调用的包装器。

关键参数:pid(要发送信号的进程 ID),sig(要发送的信号编号)。常见信号包括 SIGTERM (15)、SIGKILL (9) 和 SIGHUP (1)。

向进程发送 SIGTERM

SIGTERM(信号 15)是默认的终止信号。它允许进程在退出前执行清理操作。这是请求终止的礼貌方式。

terminate_process.py
import os
import signal
import time
import subprocess

# Start a long-running process
process = subprocess.Popen(["python", "-c", "while True: pass"])

print(f"Process started with PID: {process.pid}")

# Wait a bit then send SIGTERM
time.sleep(2)
os.kill(process.pid, signal.SIGTERM)

# Wait for process to terminate
process.wait()
print(f"Process terminated with return code: {process.returncode}")

此示例启动一个无限期运行的 Python 进程,然后发送 SIGTERM 以请求终止。该进程可以捕获和处理 SIGTERM。

subprocess.Popen.wait() 确保我们在发送信号后等待进程实际终止。

使用 SIGKILL 强制终止进程

SIGKILL(信号 9)立即终止进程,无需清理。该进程无法捕获或忽略此信号。将其用作最后的手段。

kill_process.py
import os
import signal
import subprocess
import time

# Start a process that ignores SIGTERM
cmd = '''
import signal, time
signal.signal(signal.SIGTERM, signal.SIG_IGN)
while True: time.sleep(1)
'''
process = subprocess.Popen(["python", "-c", cmd])

print(f"Process started with PID: {process.pid}")

# First try SIGTERM (will be ignored)
os.kill(process.pid, signal.SIGTERM)
time.sleep(1)

# Process still running, use SIGKILL
if process.poll() is None:
    print("SIGTERM failed, sending SIGKILL")
    os.kill(process.pid, signal.SIGKILL)
    process.wait()

print(f"Process terminated with return code: {process.returncode}")

这展示了 SIGKILL 如何终止忽略 SIGTERM 的进程。process.poll() 检查进程在发送 SIGTERM 后是否仍在运行。

应谨慎使用 SIGKILL,因为它不允许正确清理诸如打开的文件或网络连接之类的资源。

发送 SIGHUP 以重新加载配置

SIGHUP(信号 1)传统上表示终端断开连接,但通常用于重新加载配置。许多守护进程会在 SIGHUP 上重新加载配置。

reload_config.py
import os
import signal
import subprocess
import time

# Start a process that handles SIGHUP
cmd = '''
import signal, time

def handle_hup(signum, frame):
    print("Received SIGHUP, reloading config")
    
signal.signal(signal.SIGHUP, handle_hup)
while True: time.sleep(1)
'''
process = subprocess.Popen(["python", "-c", cmd], 
                          stdout=subprocess.PIPE,
                          universal_newlines=True)

print(f"Process started with PID: {process.pid}")

# Send SIGHUP to trigger config reload
time.sleep(1)
os.kill(process.pid, signal.SIGHUP)

# Read process output
print(process.stdout.readline())

# Clean up
os.kill(process.pid, signal.SIGTERM)
process.wait()

这演示了一个捕获 SIGHUP 以重新加载配置的进程。子进程捕获 stdout,以便我们可以看到重新加载消息。

许多服务器应用程序(如 nginx 和 Apache)使用 SIGHUP 来实现优雅的配置重新加载,而无需重新启动。

向进程组发送信号

负 PID 会将信号发送到整个进程组。这对于终止所有子进程及其父进程非常有用。

process_group.py
import os
import signal
import subprocess
import time

# Start a process that creates children
cmd = '''
import os, time, subprocess
print(f"Parent PID: {os.getpid()}")
children = [subprocess.Popen(["sleep", "60"]) for _ in range(3)]
[print(f"Child PID: {c.pid}") for c in children]
time.sleep(60)
'''
process = subprocess.Popen(["python", "-c", cmd], 
                         stdout=subprocess.PIPE,
                         universal_newlines=True)

# Read PIDs from output
parent_pid = int(process.stdout.readline().split(":")[1])
child_pids = [int(process.stdout.readline().split(":")[1]) for _ in range(3)]

print(f"Parent PID: {parent_pid}")
print(f"Child PIDs: {child_pids}")

# Send SIGTERM to entire process group
time.sleep(1)
os.kill(-parent_pid, signal.SIGTERM)

# Verify all processes terminated
for pid in [parent_pid] + child_pids:
    try:
        os.kill(pid, 0)  # Check if process exists
        print(f"Process {pid} still running!")
    except ProcessLookupError:
        print(f"Process {pid} terminated")

这创建了一个具有三个子进程的父进程。将 SIGTERM 发送到负父 PID 会终止整个进程组。

进程组确保可以一起管理所有相关进程,这对于 shell 作业控制和服务管理特别有用。

使用信号 0 检查进程是否存在

信号 0 实际上并不发送信号,而是检查是否可以发送信号。这可以验证进程是否存在以及您是否有权向其发送信号。

check_process.py
import os
import subprocess
import time

# Start a temporary process
process = subprocess.Popen(["sleep", "10"])
pid = process.pid

print(f"Checking process with PID: {pid}")

# Check if process exists
try:
    os.kill(pid, 0)
    print(f"Process {pid} exists and can be signaled")
except ProcessLookupError:
    print(f"Process {pid} does not exist")
except PermissionError:
    print(f"No permission to signal process {pid}")

# Wait for process to finish and check again
process.wait()
try:
    os.kill(pid, 0)
except ProcessLookupError:
    print(f"Process {pid} no longer exists after completion")

这演示了使用信号 0 检查进程是否存在。第一次检查在进程运行时成功,第二次检查在完成后失败。

此技术对于进程监视和健康检查非常有用,而无需实际中断目标进程。

在 Python 中处理信号

Python 进程可以使用 signal 模块捕获和处理信号。此示例显示了一个可以优雅地处理终止的 Python 进程。

signal_handler.py
import os
import signal
import sys
import time

def handle_term(signum, frame):
    print(f"Received signal {signum}, cleaning up...")
    # Perform cleanup here
    time.sleep(1)  # Simulate cleanup
    print("Cleanup complete, exiting")
    sys.exit(0)

# Register signal handlers
signal.signal(signal.SIGTERM, handle_term)
signal.signal(signal.SIGHUP, handle_term)

print(f"Running with PID: {os.getpid()}")
print("Send SIGTERM or SIGHUP to trigger handler")

# Main loop
while True:
    time.sleep(1)
    print("Working...")

此脚本注册 SIGTERM 和 SIGHUP 的处理程序。收到任一信号时,将在退出前运行 cleanup 函数。

信号处理程序允许进程优雅地关闭,在终止时保存状态并正确释放资源。

Windows 注意事项

在 Windows 上,与 Unix 相比,os.kill 的功能有限

安全注意事项

最佳实践

资料来源

作者

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

列出所有 Python 教程