2015 11 06 write sysv service - hanyong/note GitHub Wiki
System V 是个很古老的东西,曾在多个地方看到过,网上能找到的相关资料和教程却很少,找到一些链接,也不太有用。
- http://savannah.nongnu.org/projects/sysvinit
- https://wiki.debian.org/Debate/initsystem/sysvinit
- https://wiki.archlinux.org/index.php/SysVinit
- https://wiki.debian.org/LSBInitScripts
在 /etc/init.d/
目录创建一个文件, 设置可执行标志位, 即为一个 System V 服务, 文件名即为服务名.
此文件通常是一个服务脚本, 实现 start, stop, status 等操作命令.
使用 service
命令即可对该服务进行 start, stop, status 等控制操作,
如 service a.sh status
,
其效果其实与直接调用 /etc/init.d/a.sh status
相同.
这样服务就算创建成功了, 但不会自动启动, 如何自动启动?且看下文。
/etc/
目录下还有一些 rc*.d/
文件夹, 如 rc3.d/
, 每个文件夹对应一个 runlevel,
这些文件夹下包含一些特定命名规则的到服务脚本的软链接, 控制每个 runlevel 下默认起停的服务及顺序.
软链接命名规则如下, 第一个字母为 K 或 S, K 表示停止, S 表示启动,
随后为两位数字, 范围 '00' - '99', 表示起停服务的优先级, 值小的优先, 随后为服务名.
linux 正常启动时 runlevel 通常为 2-5 的其中一个, 并且我们通常对 runlevel 2-5 使用一样的配置.
已知 ubuntu 正常启动时 runlevel 为 2, RHEL 5/6 console 模式为 3, GUI 为 5.
这些软链接不必手动维护, ubuntu 下可使用 update-rc.d
命令维护, 这个命名看起来有点像个文件夹名字, 其实确实是个命令.
常用用法如下:
sudo update-rc.d a.sh defaults 99 1
创建各 runlevel 下的软链接, 默认 2-5 启动, 其他停止. 随后两个参数为启动、停止优先级,最后启动,最先停止.
-
sudo update-rc.d -f a.sh remove
删除所有 runlevel 下的软链接, 但服务脚步本身不会删除,并且,如果服务脚本本身存在的话,不加 "-f" 参数会有一个警告,remove 动作不会执行, 添加 "-f" 参数可强制执行。 -
sudo update-rc.d a.sh enable|disable
在 runlevel 2-5 下允许, 禁止服务。 其效果是将软链接名字在 K, S 间切换, 优先级修改为 (100 - 优先级), 晚启动的先停止. 这个命令需要在 runlevel 下已存在软链接时调用.
在一些古老的系统如 RHEL 5/6 上, 可以使用 chkconfig
管理这些链接.
使用 chkconfig 管理的脚本要求必须包含类似如下两行注释:
# chkconfig: - 99 01
# description: test
chkconfig 这一行指定服务默认启动的 runlevel (通常为 2345, "-" 表示不启动) 和启动、停止优先级,
具体说明可参考 man chkconfig
。
服务脚步放到 /etc/init.d/
目录下,并且包含这两行内容,就可以用 chkconfig 管理.
-
chkconfig --list [name]
, 列出服务在各个 runlevel 下是否启动, 不指定 name 则列出所有服务. -
sudo chkconfig --add <name>
,创建指定服务在各个 runlevel 下的软链接。 -
sudo chkconfig --del <name>
,删除指定服务在各个 runlevel 下的软链接。 -
sudo chkconfig [--level N..] <name> on|off
,在指定 runlevel 下启用/禁用服务,不指定--level
则默认为 2345.
ubuntu 下默认已经没有这个命令,虽然在仓库上可以找到安装,但貌似功能有问题,请不要安装使用.
编写服务脚本时我们通常有一些通用逻辑要处理,如服务已经启动时不要重复启动,停止服务时通过指定 pid 文件停止进程,
可能先尝试优雅退出,不行再强制杀掉。
ubuntu 下有个命令 start-stop-daemon
,可以帮助我们处理这些通用逻辑,
通过 start-stop-daemon
调用我们的应用脚本,我们应用脚本可以省去处理这些逻辑。
start-stop-daemon
有几个选项可以用来匹配服务进程,通常使用 pid 文件集合 exec 路径比较靠谱, 如:
-p a.sh.pid --exec /bin/sleep
- 默认情况 pid 文件需要应用自己创建,
start-stop-daemon
期望应用进程会自动 detach 服务进程, 这时start-stop-daemon
不知道服务进程 pid, pid 文件只能由应用自己创建。 有个缺点就是 pid 文件路径同时在应用本身和start-stop-daemon
参数维护,两者需要保持一致。 - exec 路径通过
/proc/<pid>/exe
采集,对脚本进程会定位到解释器本地程序,这对非编译成本地程序的应用定位不够精确。
启动服务时如果服务已经启动,停止服务时服务未启动,start-stop-daemon
不做任何操作,默认会报错,
添加 --oknodo
可避免 start-stop-daemon
这种情况下报错。
start-stop-daemon
默认会改变当前目录到系统根目录 /
,如果要指定应用目录,可以添加 --chdir
参数, 如:
--chdir $PWD
启动服务语法为 --start -a <命令> [-- 参数列表..]
,
-a
参数指定启动应用程序名字或路径, 默认为 exec 路径, 但对脚本来说这两者是不一样的。
如果我们的应用脚本本身没有 detach 服务进程到后台的功能,
添加 -b, --background
参数还可以帮我们实现这个功能。
但要注意,使用这个功能时,start-stop-daemon
默认将应用 stdin, stdout, stderr 全部重定向到 /dev/null
,
所有 stdout, stderr 输出信息将丢失。
并且 start-stop-daemon
不会检查服务进程有没有启动成功,服务进程没起来或者有任何报错都不会有任何提示,
所以这个功能是不推荐使用的,除非我们的应用也不使用输入输出了,也能平稳启动没问题了,
在 quick and dirty 的场合为了简化工作可以使用。
使用这个功能时,还可以使用 -m, --make-pidfile
参数,让 start-stop-daemon
帮你创建 pid 文件。
这两个参数通常应该一起使用 -bm
。
不过话说回来,不使用输入输出和平稳启动都做了,detach 服务进程和创建 pid 文件也只是一步之遥了。
自己包装一下还能加一些有用的输入输出和检查报错,唯一的缺陷就是 pid 文件路径要在应用和 start-stop-daemon
参数间耦合一下。
需要以指定用户启动进程,可以添加 -c, --chuid username|uid[:group|gid]
参数实现。
这个功能需要 root 权限,否则启动服务进程会失败。
注意, 当使用了 -bm
参数时,即使启动服务进程失败, start-stop-daemon
不会做检查,返回码依然为成功 (即 0)。
System V 服务脚本自动启动时,启动用户即为 root,这个参数很适合在 System V 脚本中使用.
停止服务语法为 --stop
,默认的停止方式是向服务进程发送一个 SIGTERM
信息。
可以使用 --signal
修改发送的信号。
通常我们可以使用功能更强大的 --retry
自定义停止动作, 参数为一系列信号和超时对,如 SIGTERM/30/SIGKILL/1
.
可以使用 --status
参数检查服务进程是否存在。
当我们应用的停止动作需要执行其他命令或动作,而不只是发送信号时,使用 --stop
停止服务是不适用的,
我们可以配合自定义命令和 --status
检查实现停止服务功能。
我们也可以直接在我们的应用脚本中使用 start-stop-daemon
,简化我们编写应用脚本的一些工作。