优雅关闭进程

优雅关闭进程

介绍

本文涉及到进程对信号的响应,关于信号的一些基本知识,可以参考往期文章:https://www.madbull.site/?p=1759

一个进程正常运行的时候,可以通过给进程发送 SIGKILL 9 信号,直接强制关闭进程。进程所持有的一些资源,由操作系统来回收处理,这样确实很省事。但是这种做法也很暴力,会导致一些资源不能及时释放。例如监听端口、需要清理的 PID 文件等等。那么怎么优雅地关闭一个正在运行的进程呢?

这就需要给进程设置信号响应。SIGKILL 信号始终都可以强制关闭进程,这是因为 SIGKILL 信号是不可被捕捉,也就不可以被进程重新设置信号处理函数。但是有另外两个信号 SIGINT 2 SIGTERM 15 是可以被捕捉的,也经常用来优雅地关闭进程。我们可以让进程捕捉这两个信号,然后设置信号处理函数,来主动释放进程所持有的资源,让进程自动退出。

另外 systemctl stop xxx 就是给进程发送 SIGTERM 信号,让进程主动退出。

示例

以下以 python 为例,结合 systemd 启停 python 进程来测试的。其中还涉及 python 的虚拟环境,可以参考往期文章:https://www.madbull.site/?p=1558

当然也可以直接发送 2 和 15 信号让进程自动关闭。执行指令:kill -2 pid 和 kill -15 pid 就可以了。

如果要 C 语言实现,也是同样的道理,捕捉信号,设置信号处理函数,主动退出。

代码 tsys.py

import signal
import time
from pid import PidFile

# 服务运行状态,为 True 就需要主动释放一些资源了
exit_flag = False

def stop_handler(sig, frame) :
    global exit_flag
    # 设置进程为待关闭状态
    exit_flag = True
    print("get "+signal.strsignal(sig)+" "+str(sig), flush=True)


# 启动自己的服务
def run_server() :
    while True:
        # 判断是否接收到中断服务的信号
        if exit_flag :
            ## 等待未完成的服务处理完
            ## 回收资源,关闭文件描述符等等
            break
        ## 在这里运行自己需要的服务,申请资源等等
        print("still running", flush=True)
        time.sleep(1)
        pass

if __name__ == "__main__" :
    # 响应 SIGINT 2 中断信号
    signal.signal( signal.SIGINT, stop_handler )
    # 响应 SIGTERM 15 请求进程终止信号
    signal.signal( signal.SIGTERM, stop_handler )
    with PidFile(pidname="tsys.pid", piddir=".") :
        run_server()

配置 tsys.service,路径 /usr/lib/systemd/system/tsys.service

[Unit]
Description=Test pid file SIGTERM
After=multi-user.target

[Service]
Type=simple
WorkingDirectory=/root/test
ExecStart=bash -c "source /opt/tvenv/bin/activate && exec python tsys.py > tsys.log"
Restart=always

[Install]
WantedBy=multi-user.target

测试

启动

停止

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注