如何终止通过Python启动的无头X服务器?

2 投票
1 回答
4289 浏览
提问于 2025-04-15 16:05

我想用Python获取网页的截图。为此,我使用的是http://github.com/AdamN/python-webkit2png/

    newArgs = ["xvfb-run", "--server-args=-screen 0, 640x480x24", sys.argv[0]]
    for i in range(1, len(sys.argv)):
        if sys.argv[i] not in ["-x", "--xvfb"]:
            newArgs.append(sys.argv[i])
    logging.debug("Executing %s" % " ".join(newArgs))
    os.execvp(newArgs[0], newArgs)

这个工具基本上是用正确的参数调用了xvfb-run。但是man xvfb上说:

注意,上面例子中使用的演示X客户端不会自己退出,所以在xvfb-run退出之前,必须先杀掉它们。

这就意味着,如果整个过程在一个循环中(为了获取多个截图),这个脚本会<????>,除非杀掉X服务器。我该怎么做呢?

1 个回答

5

关于 os.execvp 的文档中提到:

这些函数会执行一个新的程序,替换掉当前的进程;它们不会返回。[..]

所以在调用 os.execvp 之后,程序中的其他语句就不会再执行了。你可能想用 subprocess.Popen 来代替:

subprocess 模块可以让你启动新的进程,连接它们的输入/输出/错误管道,并获取它们的返回代码。这个模块的目的是替代一些旧的模块和函数,比如:

使用 subprocess.Popen,在虚拟帧缓冲区 X 服务器中运行 xlogo 的代码变成了:

import subprocess
xvfb_args = ['xvfb-run', '--server-args=-screen 0, 640x480x24', 'xlogo']
process = subprocess.Popen(xvfb_args)

现在的问题是 xvfb-run 会在后台启动 Xvfb 进程。调用 process.kill() 并不会杀掉 Xvfb(至少在我的机器上是这样……)。我尝试了几种方法,到目前为止,唯一有效的办法是:

import os
import signal
import subprocess

SERVER_NUM = 99  # 99 is the default used by xvfb-run; you can leave this out.

xvfb_args = ['xvfb-run', '--server-num=%d' % SERVER_NUM,
             '--server-args=-screen 0, 640x480x24', 'xlogo']
subprocess.Popen(xvfb_args)

# ... do whatever you want to do here...

pid = int(open('/tmp/.X%s-lock' % SERVER_NUM).read().strip())
os.kill(pid, signal.SIGINT)

所以这段代码从 /tmp/.X99-lock 中读取 Xvfb 的进程 ID,并向这个进程发送一个中断信号。这个方法有效,但偶尔会出现错误信息(不过我想你可以忽略它)。希望其他人能提供一个更优雅的解决方案。谢谢。

撰写回答