Systemctl stop XXX 时间太长
一、背景假如我们自己开发了一个系统,例如web,想要通过systemd来控制。但使用过程中,出现了问题,systemctl stop XXX 的时间太长了。二、问题原因
一、背景
假如我们自己开发了一个系统,例如web,想要通过systemd来控制。但使用过程中,出现了问题,systemctl stop XXX 的时间太长了。
二、问题原因
先说结论:我们的系统停止的办法采用的是默认的kill -15(SIGTERM)的办法,kill -15(SIGTERM)不会导致程序立刻停止。超时之后才会kill -9(SIGKILL)强制杀死。
接下来是分析。
我们的配置文件是这样的。
[Unit]
Description=sxxxxxx
After=network.target xx.service xx.service xx.service
Before=xx.service
[Service]
ExecStart=xxxxxxxxxxxxxxxxx
WorkingDirectory=/xxxx/xxxx/xxx/
StandardOutput=inherit
StandardError=inherit
Restart=always
User=root
Group=root
[Install]
WantedBy=multi-user.target
里面配置了程序如何启动,却没有配置如何停止,看看官方文档里面的说明。
ExecStop=
这是一个可选的指令, 用于设置当该服务被要求停止时所执行的命令行。 语法规则与
ExecStart=
完全相同。 执行完此处设置的所有命令行之后,该服务将被视为已经停止, 此时,该服务所有剩余的进程将会根据KillMode=
的设置被杀死(参见 systemd.kill(5))。 如果未设置此选项,那么当此服务被停止时, 该服务的所有进程都将会根据KillSignal=
的设置被立即全部杀死。 与ExecReload=
一样, 也有一个特殊的环境变量$MAINPID
可用于表示主进程的PID 。一般来说,不应该仅仅设置一个结束服务的命令而不等待其完成。 因为当此处设置的命令执行完之后, 剩余的进程会被按照
KillMode=
与KillSignal=
的设置立即杀死, 这可能会导致数据丢失。 因此,这里设置的命令必须是同步操作,而不能是异步操作。注意,仅在服务确实启动成功的前提下,才会执行
ExecStop=
中设置的命令。 如果服务从未启动或启动失败(例如,任意一个ExecStart=
,ExecStartPre=
,ExecStartPost=
中无 "-
" 前缀的命令执行失败或超时), 那么ExecStop=
将会被跳过。 如果想要无条件的在服务停止后执行特定的动作,那么应该使用ExecStopPost=
选项。 如果服务启动成功,那么即使主服务进程已经终止(无论是主动退出还是被杀死),也会继续执行停止操作。 因此停止命令必须正确处理这种场景,如果 systemd 发现在调用停止命令时主服务进程已经终止,那么将会撤销 $MAINPID 变量。重启服务的动作被实现为"先停止、再启动"。所以在重启期间,将会执行
ExecStop=
与ExecStopPost=
命令。 推荐将此选项用于那些必须在服务干净退出之前执行的命令(例如还需要继续与主服务进程通信)。当此选项设置的命令被执行的时候,应该假定服务正处于完全正常的运行状态,可以正常的与其通信。 如果想要无条件的在服务停止后"清理尸体",那么应该使用ExecStopPost=
选项。
有这么一句话
如果未设置此选项,那么当此服务被停止时, 该服务的所有进程都将会根据 KillSignal=
的设置被立即全部杀死
再来看另一段有关KillSignal
KillMode=
设置在单元停止时,杀死进程的方法。 取值范围如下:
control-group
,process
,mixed
,none
control-group
表示杀死该单元的 cgroup 内的所有进程(对于 service 单元,还要先执行ExecStop=
动作)。process
表示仅杀死主进程。mixed
表示首先向主进程发送SIGTERM
信号(见下文), 然后再向该单元的 cgroup 内的所有其他进程发送SIGKILL
信号(见下文)。none
表示仅执行ExecStop=
动作, 而不杀死任何进程。 这会导致即使单元已经停止, 但是该单元的 cgroup 依然一直存在, 直到其中的进程 全部死亡。杀死进程的时候, 第一步首先使用
KillSignal=
信号(默认为SIGTERM
) (如果SendSIGHUP=yes
,那么还会立即紧跟一个SIGHUP
信号), 若等候TimeoutStopSec=
时间后, 进程仍然未被杀死, 则继续第二步使用SIGKILL
或FinalKillSignal=
信号(除非SendSIGKILL=no
)强制杀死。 详见 kill(2) 手册。默认值是
control-group
也就是说,默认的方法就是先用kill -15(SIGTERM) 杀,超时杀不掉再用kill -9(SIGKILL)强杀。
信号和数字对应关系可以参考下表。
Signal x86/ARM Alpha/ MIPS PARISC Notes most others SPARC ───────────────────────────────────────────────────────────────── SIGHUP 1 1 1 1 SIGINT 2 2 2 2 SIGQUIT 3 3 3 3 SIGILL 4 4 4 4 SIGTRAP 5 5 5 5 SIGABRT 6 6 6 6 SIGIOT 6 6 6 6 SIGBUS 7 10 10 10 SIGEMT - 7 7 - SIGFPE 8 8 8 8 SIGKILL 9 9 9 9 SIGUSR1 10 30 16 16 SIGSEGV 11 11 11 11 SIGUSR2 12 31 17 17 SIGPIPE 13 13 13 13 SIGALRM 14 14 14 14 SIGTERM 15 15 15 15 SIGSTKFLT 16 - - 7 SIGCHLD 17 20 18 18 SIGCLD - - 18 - SIGCONT 18 19 25 26 SIGSTOP 19 17 23 24 SIGTSTP 20 18 24 25 SIGTTIN 21 21 26 27 SIGTTOU 22 22 27 28 SIGURG 23 16 21 29 SIGXCPU 24 24 30 12 SIGXFSZ 25 25 31 30 SIGVTALRM 26 26 28 20 SIGPROF 27 27 29 21 SIGWINCH 28 28 20 23 SIGIO 29 23 22 22 SIGPOLL Same as SIGIO SIGPWR 30 29/- 19 19 SIGINFO - 29/- - - SIGLOST - -/29 - - SIGSYS 31 12 12 31 SIGUNUSED 31 - - 31
那么 kill -15(SIGTERM) 和kill -9(SIGKILL) 到底有啥区别呢?这里就不展开了。
简而言之。
大部分程序接收到kill -15(SIGTERM)信号后,会先释放自己的资源,然后再停止。
三、解决办法
在配置文件中增加一句
ExecStop=/usr/bin/kill -9 $MAINPID
整个文件的样子大概是
[Unit]
Description=sxxxxxx
After=network.target xx.service xx.service xx.service
Before=xx.service
[Service]
ExecStart=xxxxxxxxxxxxxxxxx
ExecStop=/usr/bin/kill -9 $MAINPID
WorkingDirectory=/xxxx/xxxx/xxx/
StandardOutput=inherit
StandardError=inherit
Restart=always
User=root
Group=root
[Install]
WantedBy=multi-user.target
这样做,就能够导致在收到stop指令的时候,直接发kiil -9信号退出
更多推荐
所有评论(0)