systemctl使用reload及踩坑
文件
[Unit]
Description=demo - demo server
Documentation=/demo
After=
[Service]
Type=sample
User=root
PIDFile=/run/
ExecStart=/home/codes/test/src/demo/demo/demo
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s QUIT $MAINPID
Restart=0
LimitNOFILE=65536
[Install]
WantedBy=
[Unit]主要是描述和规定启动前后的顺序依赖关系
[Service]主要是核心的控制语句
[Install]主要是定义服务启动相关
2.程序代码
这里使用go做一个简单的信号量的捕获,
func main() {
go signalHandle() // 信号量处理函数
http.ListenAndServe("localhost:8080", nil) // 用于将进程挂起,避免退出
}
func signalHandle() {
ch := make(chan os.Signal)
signal.Notify(ch, syscall.SIGHUP)
sig := <-ch
for {
fmt.Printf("Signal received: %v", sig)
if sig == syscall.SIGHUP {
fmt.Println("")
writeFile(" received")
return
}
}
}
func writeFile(wiriteString string) {
var filename = "/home/codes/test/src/demo/demo/"
var f *os.File
f, err := os.Create(filename)
if err != nil {
fmt.Println(err)
return
}
l, err := f.WriteString(wiriteString)
if err != nil {
fmt.Println(err)
f.Close()
return
}
fmt.Println(l, "bytes written successfully")
err = f.Close()
if err != nil {
fmt.Println(err)
return
}
}
3.测试
情况一:
实验步骤:
[root@bogon demo]# cat
[root@bogon demo]# systemctl start
[root@bogon demo]# ps -ef|grep demo
root 41159 1 0 14:30 ? 00:00:00 /home/codes/test/src/demo/demo/demo
root 41168 37883 0 14:30 pts/3 00:00:00 grep --color=auto demo
[root@bogon demo]# systemctl reload
[root@bogon demo]# cat
received[root@bogon demo]#
systemctl start可以启动,进程存在,执行reload,进程可以捕获到SIGHUP信号量,执行了写文件操作,文件中输出 received。
情况二:
有时候进程在启动时去读取配置文件,然后初始化log配置,及log输出路径后才会实现日志记录,在此之前或者有些没有打印日志的位置,怕程序跑飞,往往会用重定向来记录控制台日志,例如go的fmt。
service文件中ExecStart、ExecReload、ExecStop是不支持> 、>>等重定向符号的,这里可以用/bin/sh -c来实现。例如:
ExecStart=/bin/sh -c '/home/codes/test/src/demo/demo/demo >>/home/codes/test/src/demo/demo/ 2>&1'
实验步骤:
[root@bogon system]# systemctl daemon-reload
[root@bogon system]# systemctl start
[root@bogon system]# systemctl reload
Job for failed because the control process exited with error code. See "systemctl status " and "journalctl -xe" for details.
[root@bogon system]# systemctl status
● - demo - demo server
Loaded: loaded (/usr/lib/systemd/system/; disabled; vendor preset: disabled)
Active: failed (Result: exit-code) since Thu 2019-07-11 14:42:33 CST; 1min 13s ago
Docs: /database/jdcloud-rds
Process: 41395 ExecStop=/bin/kill -s QUIT $MAINPID (code=exited, status=1/FAILURE)
Process: 41394 ExecReload=/bin/kill -s HUP $MAINPID (code=exited, status=0/SUCCESS)
Process: 41372 ExecStart=/bin/sh -c /home/codes/test/src/demo/demo/demo >>/home/codes/test/src/demo/demo/ 2>&1 (code=killed, signal=HUP)
Main PID: 41372 (code=killed, signal=HUP)
Jul 11 14:42:33 bogon kill[41395]: -p, --pid print pids without signaling them
Jul 11 14:42:33 bogon kill[41395]: -l, --list [=<signal>] list signal names, or convert one to a name
Jul 11 14:42:33 bogon kill[41395]: -L, --table list signal names and numbers
Jul 11 14:42:33 bogon kill[41395]: -h, --help display this help and exit
Jul 11 14:42:33 bogon kill[41395]: -V, --version output version information and exit
Jul 11 14:42:33 bogon kill[41395]: For more details see kill(1).
Jul 11 14:42:33 bogon systemd[1]: : control process exited, code=exited status=1
Jul 11 14:42:33 bogon systemd[1]: Reload failed for demo - demo server.
Jul 11 14:42:33 bogon systemd[1]: Unit entered failed state.
Jul 11 14:42:33 bogon systemd[1]: failed.
发现reload是报错的,查看status,发现ExecStop=/bin/kill -s QUIT $MAINPID (code=exited, status=1/FAILURE)失败。此时进程已经被杀掉了,执行systemctl strat将服务启动:
[root@bogon system]# ps -ef|grep demo
root 41435 1 0 14:45 ? 00:00:00 /bin/sh -c /home/codes/test/src/demo/demo/demo >>/home/codes/test/src/demo/demo/ 2>&1
root 41436 41435 0 14:45 ? 00:00:00 /home/codes/test/src/demo/demo/demo
root 41444 37883 0 14:45 pts/3 00:00:00 grep --color=auto demo
可以看到,这次/bin/sh -c命令循环fork出了两个子进程,41435是该进程组的首进程,负责与终端tty交互,41436才是真正的demo进程,当执行reload时,实际上执行了ExecReload=/bin/kill -s HUP M A I N P I D , 这 个 MAINPID,这个 MAINPID,这个MAINPID其实是41435,此时的reload信号量是发给了首进程(41435),进程如果没做信号量的捕获,默认是执行中断操作,与此同时会给子进程发送SIGTREM信号杀掉子进程,所以systemctl命令报了错误。
如果在这种情况下还想实现reload,可以换一种实现方法,就是查到demo进程的真实pid,修改如下:
ExecReload=/bin/sh -c '/bin/kill -HUP $(pidof /home/codes/test/src/demo/demo/demo)'
通过pidof /home/codes/test/src/demo/demo/demo获取进程的pid,执行kill -HUP。
[root@bogon system]# systemctl start
[root@bogon system]# ps -ef|grep demo
root 42728 1 0 15:26 ? 00:00:00 /bin/sh -c /home/codes/test/src/demo/demo/demo >/home/codes/test/src/demo/demo/ 2>&1
root 42729 42728 0 15:26 ? 00:00:00 /home/codes/test/src/demo/demo/demo
root 42736 37883 0 15:26 pts/3 00:00:00 grep --color=auto demo
[root@bogon system]# systemctl reload
[root@bogon system]# cat /home/codes/test/src/demo/demo/
Signal received:
23 bytes written successfully
这次可以看到reload没有报错,进程成功进到HUP信号量,cat 文件可以看到成功写入文件。
补充:为什么加入重定向就会循环fork出两个进程?
/bin/sh -c的原理就是fork+exec来产生一个进程,次进程是无法与tty(控制台)交互的后台进程,重定向需要与tty(控制台)交互,所以先fork出来一个重定向进程,再fork出一个真正的demo进程。