You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何配置systemd服务以正确停止Python程序并删除锁文件?

解决systemd停止服务时锁文件未删除的问题

咱们来拆解下你遇到的问题:手动按Ctrl+C退出程序时锁文件能正常删除,但用systemd的停止命令(比如systemctl stop your-service)就不行,核心原因是两种场景发送的终止信号不一样,你的Python代码只处理了Ctrl+C对应的信号。

为什么会出现这个差异?

  • 当你按Ctrl+C时,系统给程序发送的是SIGINT信号,Python会把它转换成KeyboardInterrupt异常,你的try-except块捕获到这个异常后,会执行close_program()和锁文件删除逻辑。
  • 而systemd默认停止服务时,给主进程发送的是SIGTERM信号,这个信号不会触发KeyboardInterrupt异常,所以你的程序没进入异常处理分支,锁文件自然没被删除。

下面给你几个可行的解决方案,按需选择:


方案1:修改Python代码,处理多种终止信号

通过signal模块注册信号处理函数,让程序在收到SIGINT(Ctrl+C)和SIGTERM(systemd默认停止信号)时都执行清理逻辑。修改后的代码示例:

import signal
import os
import pathlib
from datetime import datetime
import logging

logger = logging.getLogger(__name__)

def clean_up_and_exit(signum, frame):
    """处理终止信号,执行清理操作并退出"""
    logger.info("Received stop signal, starting cleanup...")
    close_program()  # 关闭你的其他线程
    lock_path = pathlib.Path("program.lock")
    if lock_path.exists():
        os.remove(lock_path)
        logger.info("Lock file removed successfully.")
    exit(0)

# 注册信号处理:同时监听SIGINT和SIGTERM
signal.signal(signal.SIGINT, clean_up_and_exit)
signal.signal(signal.SIGTERM, clean_up_and_exit)

if __name__ == '__main__':
    lock_path = pathlib.Path("program.lock")
    if not lock_path.exists():
        lock_acquired_on = datetime.now()
        with open(lock_path, 'w') as lock:
            lock.write(f'Lock acquired on {lock_acquired_on}')
        logger.info('Added lock file to avoid running the program twice.')
        try:
            while True:
                # 你的业务逻辑代码
                pass
        except Exception as e:
            logger.error(f"Unexpected error: {e}")
            clean_up_and_exit(None, None)
    else:
        with open(lock_path, 'r') as lock:
            lock_acquisition_time = str(lock.readlines()[0])
        logger.info('Programme Maquette Status is already running.')
        logger.info(lock_acquisition_time)

这个方案最健壮,能覆盖所有常见的程序终止场景。


方案2:修改systemd服务配置,发送SIGINT信号

如果你不想改代码,可以让systemd停止服务时发送SIGINT信号(和Ctrl+C一致),这样就能触发你现有的KeyboardInterrupt异常处理。修改你的systemd配置文件,在[Service]段添加一行:

[Service]
WorkingDirectory=/home/pi/Documents/ProductionMaquette
Type=simple
ExecStart=/usr/local/bin/python3.8 /home/pi/Documents/ProductionMaquette/Lo_main.py
StandardInput=tty-force
# 添加这行,让systemd发送SIGINT而不是默认的SIGTERM
KillSignal=SIGINT

修改后重新加载systemd配置并重启服务:

sudo systemctl daemon-reload
sudo systemctl restart your-service-name

这个方案改动最小,适合快速解决问题。


额外建议:利用systemd内置机制避免重复启动

其实systemd本身就有防止服务重复运行的能力,不需要自己手动实现锁文件:

  • systemd会跟踪服务的主进程,默认不会允许同一个服务同时启动多个实例;
  • 如果需要更严格的控制,可以在[Service]段添加PIDFile=/path/to/your/pid/file,让systemd通过PID文件管理进程,但对于Type=simple的服务,这个通常不是必须的。

当然,你自己实现的锁文件可以作为额外的保障,两者不冲突。

内容的提问来源于stack exchange,提问作者Welyweloo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 06:37:40