Ubuntu 添加开机/关机执行脚本与service的理解

Ubuntu 添加开机/关机执行脚本与service的理解

https://blog.csdn.net/hyklose/article/details/80222070

最近碰到一个需求是需要在ubuntu中插入一个关机脚本,在系统关机之前执行它。在网上浏览了很多资料,但是没有切实可行的方法,大多数的博客都是比较陈旧的方法,可能已经不适用于最新的ubuntu18.04的系统。所以花了大概一整天的时间研究了一下,顺便清楚了关于ubuntu运行状态和service的相关知识,在这里按照当时钻研的思路简单记录一下,不感兴趣的可以直接看最后的用法。

首先,关于关机执行脚本,网上最多的方法应该是下面描述的:

(1)在"/etc/init.d"下面创建脚本:

touch /etc/init.d/myscript

(2)使用update-rc.d工具或者手动创建连接(update-rc.d工具使用方法会在另一片文章中介绍,这里使用手动创建连接的方法,但这样作是为了易于理解,如果会使用的话,建议还是使用update-rc.d工具),如下:

ln -s /etc/init.d/myscript /etc/rc0.d/K99myscript
ln -s /etc/init.d/myscript /etc/rc6.d/K99myscript 

但是按照上面的方法执行完成之后重启,发现脚本并没有执行(所使用的是ubuntu18.04的版本,下同)。

那么现在就需要自己进行研究了,首先清楚上面这个方法的原理。ubuntu有几个运行等级,分别是S 0 1 2 3 4 5 6,代表的意义如下:

 
  1. 0 – Halt,关机模式

  2. 1 – Single,单用户模式

  3. 2 - Full multi-user with display manager (GUI)

  4. 3 - Full multi-user with display manager (GUI)

  5. 4 - Full multi-user with display manager (GUI)

  6. 5 - Full multi-user with display manager (GUI)

  7. 6 – Reboot,重启

 

其中S代表全部,2345是不同的多用户模式,在广义上没有任何区别。而ubuntu在不同级别启动或终止的时候,都回去执行相应的"/etc/rcX.d"下面的脚本。所以我们理解了上面的方法,分别在"/etc/rc0.d"和"/etc/rc6.d"下面创建的我们所要执行的脚本的连接,就是要系统在关机和重启的时候都去执行它。那么问题来了,我们明明创建了软连接,为什么脚本没有执行呢?

这个时候我查看了"/etc/init.d"中的其他一些脚本,发现他们的运行模式都是start 2 3 4 5 stop 0 1 6。就是在2345这四个等级下启动,在016这三个等级下关闭。于是我尝试下面命令,让我的脚本也同样在2345启动016停止,先删除之前创建的连接:

 
  1. rm /etc/rc0.d/K99myscript

  2. rm /etc/rc6.d/K99myscript

在按照下面命令创建连接,让脚本在2345启动016停止:

 
  1. ln -s /etc/init.d/myscript /etc/rc0.d/K99myscript

  2. ln -s /etc/init.d/myscript /etc/rc1.d/K99myscript

  3. ln -s /etc/init.d/myscript /etc/rc2.d/S99myscript

  4. ln -s /etc/init.d/myscript /etc/rc3.d/S99myscript

  5. ln -s /etc/init.d/myscript /etc/rc4.d/S99myscript

  6. ln -s /etc/init.d/myscript /etc/rc5.d/S99myscript

  7. ln -s /etc/init.d/myscript /etc/rc6.d/K99myscript

然后重启,这时发现脚本在开机和关机时被分别执行一次。那么我们到现在为止可以得到,就是"/etc/rcX.d"下面的脚本式会在相应的动作执行时被启动的。而且我们在"/etc/rc0.d"和"/etc/rc6.d"下面的软连接也被执行了。那么到此时,问题转化为了为什么我们单独在0 6下面创建软连接没有被执行,而我们在0 1 2 3 4 5 6 下面都创建软连接时,就被执行了呢?

我们注意到软连接在脚本名字前被加上了S99或K99,这是一种标志,S或K表示动作,S表示运行,K表示停止,99表示这个脚本启动的序列。我们注意到"/etc/init.d"下面的脚本,有很多是需要一直不间断的运行的,所以需要在系统启动时开启,在系统结束时停止。那么对应的他们就要在文件夹2345(即"/etc/rc2.d"、"/etc/rc3.d",下同)下面有一个启动脚本,在016下面有一个终止脚本。所以我们现在可以猜到:系统会在启动时调用2345下面的脚本启动他们,结束时调用016下面的脚本终止他们。然后我此时的思路是,那么我们需要在系统结束时启动我们的脚本,是不是说我们要在"/etc/rc0.d"和"/etc/rc6.d"下面创建S开头的脚本而不是K开头的脚本呢?

于是,先删除我们之前创建的脚本:

rm /etc/rc0.d/K99myscript
rm /etc/rc1.d/K99myscript
rm /etc/rc2.d/S99myscript
rm /etc/rc3.d/S99myscript
rm /etc/rc4.d/S99myscript
rm /etc/rc5.d/S99myscript
rm /etc/rc6.d/K99myscript

然后在016这三个文件夹下面创建S开头的软连接:

ln -s /etc/init.d/myscript /etc/rc0.d/S99myscript
ln -s /etc/init.d/myscript /etc/rc1.d/S99myscript
ln -s /etc/init.d/myscript /etc/rc6.d/S99myscript 

然后继续重启,发现脚本依旧没有执行。我们注意到,当我们在0123456都添加了脚本的软连接时,脚本是在开机和关机都被执行了一次的。所以虽然脚本没有执行,但是根据我的经验,我依然对上面标红色的猜测坚定不移。那么下面的问题就是,为什么脚本没有被执行呢?此时我的猜测是:系统不会机械的根据0123456文件夹下面的脚本来执行,而是会自己记录,只有在启动时系统启动过这个脚本,在结束时系统才会去终止这个脚本。那么基于这个猜测,我们的问题进一步转化,脚本都是一样的,系统是如何区别启动和终止的的呢?这时我们不妨再查看一下"/etc/init.d"下面的其他脚本,结果发现很多脚本都会对$1入参做一个case语句,分别对应着start和stop,就拿我们都知道的ssh举例:

case "$1" in
  start)
        check_privsep_dir
        check_for_no_start
        check_dev_null
        log_daemon_msg "Starting OpenBSD Secure Shell server" "sshd" || true
        if start-stop-daemon --start --quiet --oknodo --pidfile /run/sshd.pid --exec /usr/sbin/sshd -- $SSHD_OPTS; then
            log_end_msg 0 || true
        else
            log_end_msg 1 || true
        fi
        ;;
  stop)
        log_daemon_msg "Stopping OpenBSD Secure Shell server" "sshd" || true
        if start-stop-daemon --stop --quiet --oknodo --pidfile /run/sshd.pid; then
            log_end_msg 0 || true
        else
            log_end_msg 1 || true
        fi
        ;;

所以基于此,我们可以猜测:系统在启动时执行脚本,会自动在脚本后面传入一个start的入参,在结束时终止脚本,也会调用他,不过在他后面加上一个stop的入参。

这是我们只要修改一下我们的脚本,加入上面的case语句,让他在start和stop时做不同的事情,然后在重新链接一次:

rm /etc/rc0.d/S99myscript
rm /etc/rc1.d/S99myscript
rm /etc/rc6.d/S99myscript
ln -s /etc/init.d/myscript /etc/rc0.d/K99myscript
ln -s /etc/init.d/myscript /etc/rc1.d/K99myscript
ln -s /etc/init.d/myscript /etc/rc2.d/S99myscript
ln -s /etc/init.d/myscript /etc/rc3.d/S99myscript
ln -s /etc/init.d/myscript /etc/rc4.d/S99myscript
ln -s /etc/init.d/myscript /etc/rc5.d/S99myscript
ln -s /etc/init.d/myscript /etc/rc6.d/K99myscript 

然后此时我们再次重启,这时发现写在start参数和stop参数下面的语句分别被执行了。由此验证了我们上面所有红字的猜测,都是正确的。

所以假如我们想写一个关机执行的脚本,只要在上面start参数下面什么也不做就可以了。

说到这里,我们可以从更深层次介绍一下,其实写在"/etc/init.d"下面的就是linux系统的service。service都回在开机时启动,在关机时结束,而启动和结束的方式就是通过传入start或stop参数。比如我们自己写的脚本,还可以在后面加个status的参数,可以看到这service的状态。所以在这里说一下,如果你在关机之前手动调用你的脚本,并且给他传入了stop参数,那么系统关机时将也不再次执行你的脚本。

好了说了这么多,我们在最后总结一下在一个ubuntu系统添加一个开机和关机脚本的详细步骤:

(1)在"/etc/init.d"下面创建你要执行的脚本,名字可不同:

touch /etc/init.d/myscript

(2)在开头添加以下内容:

#!/bin/bash

### BEGIN INIT INFO
# Provides:
# Required-Start:
# Required-Stop:
# Default-Start:    2 3 4 5
# Default-Stop:     0 1 6
# Short-Description:
# Description:
### END INIT INFO

(3)然后在后面添加执行逻辑,开机逻辑和关机逻辑只需要一种时,另一处可以什么也不添:

 
  1. case "$1" in

  2. start)

  3. #开机需要执行的逻辑

  4. ;;

  5. stop)

  6. #关机需要执行的逻辑

  7. ;;

  8. *)

  9. ;;

  10. esac

(4)保存文件,然后执行:

update-rc.d myscript defaults

大功告成!

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值