systemctl使用reload及踩坑

时间:2025-04-24 10:35:18

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,这个 MAINPIDMAINPID其实是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进程。