同福

CentOS 7 的系统服务(systemctl service)

类型

oneshot

这一选项适用于只执行一项任务、随后立即退出的服务。可能需要同时设置 RemainAfterExit=yes 使得 systemd 在服务进程退出之后仍然认为服务处于激活状态。

notify

与 Type=simple 相同,但约定服务会在就绪后向 systemd 发送一个信号。这一通知的实现由 libsystemd-daemon.so 提供。

dbus

若以此方式启动,当指定的 BusName 出现在DBus系统总线上时,systemd认为服务就绪。

idle

systemd会等待所有任务处理完成后,才开始执行 idle 类型的单元。其他行为与 Type=simple 类似。

forking

systemd认为当该服务进程fork,且父进程退出后服务启动成功。对于常规的守护进程(daemon),除非你确定此启动方式无法满足需求,使用此类型启动即可。使用此启动类型应同时指定 PIDFile=,以便 systemd 能够跟踪服务的主进程

simple

(默认值) systemd认为该服务将立即启动。服务进程不会 fork 。如果该服务要启动其他服务,不要使用此类型启动,除非该服务是socket 激活型。

配置

文件格式

[Unit]
Description=test service

[Service]
Type=simple
ExecStart=/tmp/test.sh start
ExecStop=/tmp/test.sh stop
ExecReload=/tmp/test.sh reload

[Install]
WantedBy=multi-user.target

示例

我们举一个simple的例子,一个forking的例子。

simple示例

服务程序

建立模拟服务程序 /tmp/test.sh 内容如下:

#!/bin/sh

cmd="$1"

echo "`date`> [$cmd]" >> /tmp/test.log

if [ "start" = "$cmd" ] ; then
while [ 1 ] ; do
sleep 1
echo "`date`> wait..." >> /tmp/test.log
done
fi

配置文件

建立服务配置文件 /lib/systemd/system/test.service 内容如下:

[Unit]
Description=test service

[Service]
Type=simple
ExecStart=/tmp/test.sh start
ExecStop=/tmp/test.sh stop
ExecReload=/tmp/test.sh reload

[Install]
WantedBy=multi-user.target

启动服务

执行如下命令

systemctl start test.service

查看日志文件,可以看到如下内容

Wed Jul 11 14:04:23 UTC 2018> [start]
Wed Jul 11 14:04:24 UTC 2018> wait...
Wed Jul 11 14:04:25 UTC 2018> wait...
Wed Jul 11 14:04:26 UTC 2018> wait...

停止服务

执行如下命令

systemctl stop test.service

查看日志文件,可以看到如下内容

Wed Jul 11 14:04:23 UTC 2018> [start]
Wed Jul 11 14:04:24 UTC 2018> wait...
Wed Jul 11 14:04:25 UTC 2018> wait...
Wed Jul 11 14:04:26 UTC 2018> wait...
Wed Jul 11 14:04:27 UTC 2018> wait...
Wed Jul 11 14:04:40 UTC 2018> [stop]

重启服务

执行如下命令

systemctl restart test.service

查看日志文件,可以看到如下内容

Wed Jul 11 14:14:06 UTC 2018> wait...
Wed Jul 11 14:14:06 UTC 2018> [stop]
Wed Jul 11 14:14:06 UTC 2018> [start]
Wed Jul 11 14:14:07 UTC 2018> wait...

刷新服务

执行如下命令

systemctl reload test.service

查看日志文件,可以看到如下内容

Wed Jul 11 14:14:32 UTC 2018> wait...
Wed Jul 11 14:14:32 UTC 2018> [reload]
Wed Jul 11 14:14:33 UTC 2018> wait...

forking示例

服务程序

建立模拟服务程序 /tmp/test.sh 内容如下:

#!/bin/sh

cmd="$1"

### start major process
if [ "daemon" = "$cmd" ] ; then
echo "`date`> [$cmd]" >> /tmp/test.log
/tmp/test.sh &
exit
fi

### stop major process
if [ "quit" = "$cmd" ] ; then
echo "`date`> [$cmd]" >> /tmp/test.log
pid="`cat /tmp/test.pid`"
if [ "" = "`ps -ax|awk '{print $1}'|grep -e "^${pid}$"`" ] ; then
/bin/rm -f /tmp/test.pid
else
/bin/kill $pid
fi
exit
fi

### reload
if [ ! "" = "$cmd" ] ; then
echo "`date`> [$cmd]" >> /tmp/test.log
exit
fi

### do major process
pid="/tmp/test.pid"

if [ -f "$pid" ] ; then
echo "it's already running with PID `cat /tmp/test.pid`"
exit
fi

echo "$$" > /tmp/test.pid

while [ 1 ] ; do
sleep 1
echo "`date`> wait..." >> /tmp/test.log
done

配置文件

建立服务配置文件 /lib/systemd/system/test.service 内容如下:

[Unit]
Description=test service

[Service]
Type=forking
PIDFile=/tmp/test.pid
ExecStart=/tmp/test.sh daemon
ExecStop=/tmp/test.sh quit
ExecReload=/tmp/test.sh reload

[Install]
WantedBy=multi-user.target

启动服务

执行如下命令

systemctl start test.service

查看日志文件,可以看到如下内容

Wed Jul 11 14:50:29 UTC 2018> [daemon]
Wed Jul 11 14:50:30 UTC 2018> wait...
Wed Jul 11 14:50:31 UTC 2018> wait...
Wed Jul 11 14:50:32 UTC 2018> wait...

停止服务

执行如下命令

systemctl stop test.service

查看日志文件,可以看到如下内容

Wed Jul 11 14:50:29 UTC 2018> [daemon]
Wed Jul 11 14:50:30 UTC 2018> wait...
Wed Jul 11 14:50:31 UTC 2018> wait...
Wed Jul 11 14:50:32 UTC 2018> wait...
Wed Jul 11 14:50:33 UTC 2018> wait...
Wed Jul 11 14:50:37 UTC 2018> [quit]

重启服务

执行如下命令

systemctl restart test.service

查看日志文件,可以看到如下内容

Wed Jul 11 14:50:54 UTC 2018> wait...
Wed Jul 11 14:50:54 UTC 2018> [quit]
Wed Jul 11 14:50:54 UTC 2018> [daemon]
Wed Jul 11 14:50:55 UTC 2018> wait...

刷新服务

执行如下命令

systemctl reload test.service

查看日志文件,可以看到如下内容

Wed Jul 11 14:51:00 UTC 2018> wait...
Wed Jul 11 14:51:01 UTC 2018> [reload]
Wed Jul 11 14:51:01 UTC 2018> wait...

总结

普通服务程序

普通服务程序使用simple类型启动,启动后系统会记录其PID,停止时系统会自动杀死进程。

适用于自己手写的一些脚本程序,这些脚本(long run)会一直执行不会中断。比起放到crontabs里的好处是,我们可以通过stop命令随时停止它。

守护进程服务程序

守护进程服务程序使用forking类型启动,因为启动daemon的逻辑是我们自己实现的,所以我们要告诉系统PID文件放哪里了以证明系统是“活着”的。

停止服务的处理也需要我们自己实现,我们需要自己读取PID文件,并查找进程,回收资源,记录日志,杀死进程等等操作。

适用于server类的程序,比如:httpd、mysqld等等。

常见问题

提示错误 Unit not found

这是放置 xxx.service 文件的位置有问题了。
还有一种情况就是在 /etc/rc.d/rc[2,3,4,5].d/ 下面有 KYYxxx 文件,笔者就遇到了这种奇怪的情况,把这些文件删除了就行了。