Python脚本启动进程并获取PID:解决等待及客户端连接失败问题
解决子进程启动后客户端无法连接的问题
首先得说,你选subprocess.Popen()其实是完全正确的——因为subprocess.run()会一直等进程跑完才返回,根本没法满足你中途监控进程的需求。现在客户端连不上服务器的问题,大概率是子进程的IO缓冲区被占满,导致服务器进程被阻塞了。
为什么会出现这个问题?
当你用Popen默认参数启动子进程时,它会继承父进程的标准输入、输出、错误流。如果你的服务器程序有日志输出(比如启动日志、连接日志),但父进程(也就是你的监控脚本)没有读取这些输出,当输出缓冲区被填满时,服务器进程就会暂停执行,自然没法处理新的socket连接请求了——这就是为什么你用ps aux能看到进程,但客户端连不上的核心原因。
解决办法:处理子进程的IO流
这里有两种常用的方案,根据你的需求选就行:
方案1:重定向IO到/dev/null或日志文件
如果你不需要看服务器的输出,最简单的办法就是把所有IO都重定向到/dev/null,避免缓冲区阻塞:
import subprocess import time # 启动子进程,重定向所有IO p = subprocess.Popen( ["nameofmyprocess"], # 推荐用列表形式传命令,避免shell注入风险 stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL ) pid = p.pid print(pid) # 循环监控直到进程退出 while p.poll() is None: time.sleep(20) myFunc(pid) myFunc2(pid)
如果需要保留服务器的日志,可以把输出写到文件里:
import subprocess import time # 把stdout和stderr都写到server.log里 with open("server.log", "a", encoding="utf-8") as log_file: p = subprocess.Popen( ["nameofmyprocess"], stdout=log_file, stderr=subprocess.STDOUT, # 把错误输出合并到标准输出 stdin=subprocess.DEVNULL ) pid = p.pid print(pid) while p.poll() is None: time.sleep(20) myFunc(pid) myFunc2(pid)
方案2:用线程实时读取子进程输出
如果需要实时查看服务器的输出(比如调试用),可以启动两个线程分别读取stdout和stderr,避免阻塞子进程:
import subprocess import time import threading def read_stream(stream, stream_name): # 循环读取流内容 for line in iter(stream.readline, ""): print(f"[{stream_name}] {line.strip()}") # 启动子进程,开启管道获取输出 p = subprocess.Popen( ["nameofmyprocess"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.DEVNULL, text=True # 用文本模式,自动处理编码 ) # 启动线程读取stdout和stderr stdout_thread = threading.Thread(target=read_stream, args=(p.stdout, "STDOUT")) stderr_thread = threading.Thread(target=read_stream, args=(p.stderr, "STDERR")) stdout_thread.start() stderr_thread.start() pid = p.pid print(pid) # 监控进程 while p.poll() is None: time.sleep(20) myFunc(pid) myFunc2(pid) # 等待线程结束 stdout_thread.join() stderr_thread.join()
额外提醒
在你的myFunc和myFunc2里,最好加个判断:先检查PID对应的进程是否还存在(虽然循环里判断了p.poll() is None,但极端情况下进程可能在sleep期间退出),避免监控函数报错。比如可以用os.kill(pid, 0)来检查进程是否存在(需要导入os模块):
import os def myFunc(pid): try: os.kill(pid, 0) # 发送0号信号,不会终止进程,只是检查是否存在 # 你的监控逻辑(内存、socket、文件描述符等) except OSError: print(f"Process {pid} no longer exists") return
内容的提问来源于stack exchange,提问作者hexlify




