如何通过subprocess.Popen恢复进程?
我有一个用Python写的程序,它使用 subprocess.Popen
来启动另一个进程(可以是Python进程或其他进程)。启动后,我会把这个子进程的PID(进程ID)保存到一个文件里。假设父进程突然崩溃了(可能是因为出现了异常或者其他原因)。有没有办法再次访问 Popen
返回的那个对象呢?
我的基本想法是,首先读取那个文件,如果文件存在并且里面有一个PID,那么就以某种方式访问这个进程,以便知道它的返回代码或者其他信息。如果文件里没有PID,那就用 Popen
再启动一个新的进程。
非常感谢!!
3 个回答
看起来你想写一个守护进程,而且它需要支持pid文件。使用 python-daemon 是个不错的选择。
比如说:
import daemon
import lockfile
import os
with daemon.DaemonContext(pidfile=lockfile.FileLock('/var/run/spam.pid')):
os.execl('/path/to/prog', args…)
如果一个进程死掉了,它打开的所有文件句柄都会被关闭。这包括通过 popen()
创建的任何无名管道。所以,不,单靠一个进程ID(PID)是无法恢复 Popen
对象的。操作系统甚至不会把你新创建的进程视为父进程,因此你也收不到 SIGCHLD
信号(不过 waitpid()
可能还是能用)。
我也不确定子进程是否一定能存活,因为如果向一个没有读者的管道写入数据(也就是子进程重定向的 stdout
),会导致子进程因为 SIGPIPE
信号而被杀掉。
如果你想让父进程从子进程停止的地方继续工作,你需要让子进程把数据写入一个文件,通常是在 /tmp
或 /var/log
目录下,并让它记录自己的PID(通常的位置是 /var/run
)。如果让它写入一个命名管道,就有可能像上面说的那样因为 SIGPIPE
被杀掉。如果你在文件名后面加上PID,那么管理进程就能很容易地搞清楚哪个文件属于哪个守护进程。
Popen对象其实就是一个用来管理子进程的小工具,它包含了子进程的PID(进程标识符)、输入、输出和错误输出的通道,还有一些方便使用这些通道的功能。
那么,为什么你需要访问Popen对象呢?是想和子进程沟通,结束它,还是检查它是否还在运行?
无论如何,对于已经在运行的进程,你是无法再获取到Popen对象的。
正确的做法是像Tobu建议的那样,把子进程当作守护进程来启动。把一个进程变成守护进程的步骤之一是关闭输入和输出通道,这样你就不能通过这些通道和子进程交流了。大多数守护进程会使用管道或套接字,让其他程序可以连接上来并发送消息。
与子进程沟通最简单的方法是,在子进程中打开一个命名管道,比如说在/etc/my_pipe,然后在父进程或控制进程中打开这个命名管道,进行读写操作。
我快速看了一下python-daemon,感觉它可以帮助你把子进程变成守护进程,这个过程比较复杂,但它并不提供消息传递的功能。
不过,就像我说的,我觉得在我们能进一步帮助你之前,你需要告诉我们你为什么需要子进程的Popen对象。