Python 生成进程的脚本无法在 systemd 下启动

Python script that spawns processes fails to start under systemd

我有一个 python 脚本,它定期扫描文件夹以供 ffmpeg 处理。我决定把它变成一个系统服务。脚本 运行 在命令行上运行良好,但当我尝试将其作为服务 运行 时抛出 BlockingIOError。为了弄清楚这个问题,我将脚本简化为一个几乎是 hello world 的例子,但我仍然得到相同的结果。这是我拥有的:

foobar.py

import subprocess 

result = subprocess.run(['/usr/bin/ffmpeg', '-h'])

print(result)

foobar.service

[Unit]
Description=foobar

[Service]
Type=simple
TasksMax=1
User=root
Environment="PATH=/usr/bin:/root/24-7"
ExecStart=/usr/bin/python3.8 /root/24-7/foobar.py

异常

Aug 26 12:47:55 Ubuntu-1804-bionic-64-minimal systemd[1]: Started foobar.
Aug 26 12:48:15 Ubuntu-1804-bionic-64-minimal python3.8[4539]: Traceback (most recent call last):
Aug 26 12:48:15 Ubuntu-1804-bionic-64-minimal python3.8[4539]:   File "/root/24-7/foobar.py", line 6, in <module>
Aug 26 12:48:15 Ubuntu-1804-bionic-64-minimal python3.8[4539]:     result = subprocess.run(['/usr/bin/ffmpeg', '-h'])
Aug 26 12:48:15 Ubuntu-1804-bionic-64-minimal python3.8[4539]:   File "/usr/lib/python3.8/subprocess.py", line 489, in run
Aug 26 12:48:15 Ubuntu-1804-bionic-64-minimal python3.8[4539]:     with Popen(*popenargs, **kwargs) as process:
Aug 26 12:48:15 Ubuntu-1804-bionic-64-minimal python3.8[4539]:   File "/usr/lib/python3.8/subprocess.py", line 854, in __init__
Aug 26 12:48:15 Ubuntu-1804-bionic-64-minimal python3.8[4539]:     self._execute_child(args, executable, preexec_fn, close_fds,
Aug 26 12:48:15 Ubuntu-1804-bionic-64-minimal python3.8[4539]:   File "/usr/lib/python3.8/subprocess.py", line 1637, in _execute_child
Aug 26 12:48:15 Ubuntu-1804-bionic-64-minimal python3.8[4539]:     self.pid = _posixsubprocess.fork_exec(
Aug 26 12:48:15 Ubuntu-1804-bionic-64-minimal python3.8[4539]: BlockingIOError: [Errno 11] Resource temporarily unavailable
Aug 26 12:48:15 Ubuntu-1804-bionic-64-minimal systemd[1]: foobar.service: Main process exited, code=exited, status=1/FAILURE
Aug 26 12:48:15 Ubuntu-1804-bionic-64-minimal systemd[1]: foobar.service: Failed with result 'exit-code'.

不确定还可以尝试什么。我什至尝试用 sleep() 延迟调用,看看是否可能等待几秒钟会打开任何阻塞的东西。我不知道该尝试什么。有人知道吗?

问题是你的TasksMax=1。这防止了子进程的分叉,并且效果并不特定于 subprocess.run——任何诸如 subprocess.Popenos.systemos.fork 甚至 non-Python 应用程序尝试分叉会遇到类似的问题。

这是 os.fork 在受到 TasksMax=1 影响时显示类似的症状:

foobar.py

import os

pid = os.fork()
if pid == 0:
    print("child")
    os._exit(0)
else:
    print(os.waitpid(pid, 0))
Aug 26 12:25:57 moon systemd[1]: Started foobar.
Aug 26 12:25:57 moon python3.8[9477]: Traceback (most recent call last):
Aug 26 12:25:57 moon python3.8[9477]:   File "/root/24-7/foobar.py", line 3, in <module>
Aug 26 12:25:57 moon python3.8[9477]:     pid = os.fork()
Aug 26 12:25:57 moon python3.8[9477]: BlockingIOError: [Errno 11] Resource temporarily unavailable
Aug 26 12:25:57 moon systemd[1]: foobar.service: Main process exited, code=exited, status=1/FAILURE
Aug 26 12:25:57 moon systemd[1]: foobar.service: Failed with result 'exit-code'.

如果您删除该行或增加限制,它将起作用。

即使只是将它增加到 2 也适用于你简单的 ffmpeg -h 示例,尽管使用你想要的真实选项 运行,你可能想要增加它一点,只是如果 ffmpeg 需要 fork 自己的子进程。