介绍
本文涉及到进程对信号的响应,关于信号的一些基本知识,可以参考往期文章: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
测试
启动

停止

发表回复