如何用Python杀死sudo启动的subprocess子进程?
这个问题我之前也碰到过,核心原因是:当你用subprocess启动sudo sleep 100时,sudo进程在完成权限验证后,可能会fork出一个root权限的sleep子进程,随后sudo自身就退出了——这就导致你拿到的PID是已经死掉的sudo进程ID,而真正在跑的sleep是另一个root权限的进程,普通用户根本没法直接杀它。
下面给你几个靠谱的解决方案,按推荐程度排序:
方案1:让sudo直接替换自身为目标进程(拿到真实PID)
绝大多数现代版本的sudo,在无需交互(比如你已经配置了免密sudo)的情况下,会直接用目标进程替换自己(也就是*exec系统调用*),不会fork子进程。这时候subprocess.Popen返回的PID就是sleep的真实PID。如果你的环境里sudo默认不这么做,可以加-n(非交互模式,适合免密场景)和-E(保留环境变量)选项强制一下:
import subprocess import time # 启动sudo sleep,拿到真实的sleep PID pr = subprocess.Popen(["sudo", "-n", "-E", "sleep", "100"]) time.sleep(5) # 必须用sudo kill,因为sleep是root权限的进程 subprocess.run(["sudo", "kill", str(pr.pid)])
方案2:通过临时文件记录目标进程PID
如果你的sudo确实会fork子进程(比如某些旧版本或特殊配置),可以让sudo启动一个shell,先把目标进程的PID写入临时文件,之后读取这个PID再杀死:
import subprocess import time import os # 用/tmp下的临时文件存PID,避免权限问题 pid_file = "/tmp/sleep_pid.tmp" # 让sudo启动shell,记录自身PID(shell会exec sleep,所以这个PID就是sleep的PID) pr = subprocess.Popen(["sudo", "sh", "-c", f"echo $$ > {pid_file}; sleep 100"]) time.sleep(5) # 读取PID with open(pid_file, "r") as f: sleep_pid = f.read().strip() # 杀死root进程 subprocess.run(["sudo", "kill", sleep_pid]) # 清理临时文件 os.remove(pid_file)
方案3:杀死整个进程组
当sudo启动sleep时,两者会属于同一个进程组(进程组ID等于sudo的PID)。哪怕sudo退出了,sleep依然会留在这个进程组里。你只需要用sudo来杀死整个进程组就行——之前你失败的原因就是没加sudo:
import subprocess import time pr = subprocess.Popen(["sudo", "sleep", "100"]) sudo_pid = pr.pid # 这个就是进程组ID time.sleep(5) # 用sudo杀死进程组,-- -$PGID 表示杀掉PGID为$PGID的所有进程 subprocess.run(["sudo", "kill", "--", f"-{sudo_pid}"])
再复盘下你之前的操作为什么失败:
- 直接
pr.kill():普通用户没有权限给root进程发信号,必然报错权限不足。 sudo kill $PID:这里的$PID是已经退出的sudo进程ID,当然杀不到正在跑的sleep。sudo kill -- -$PID:如果是在sudo退出后执行,进程组ID还是原来的sudoPID,但必须用sudo来执行这个命令——普通用户碰不到root进程的进程组。
内容的提问来源于stack exchange,提问作者John Allard
相关产品推荐
相关产品推荐

