云网牛站
所在位置:首页 > Linux教程 > Ubuntu添加开机/关机执行脚本与service的理解

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

2018-05-07 21:54:36作者:克洛泽稿源:linux站

最近碰到一个需求是需要在ubuntu18.04操作系统中插入一个关机脚本,在操作系统关机之前执行它。在网上浏览了很多资料,但是没有切实可行的方法,大多数的都是比较陈旧的方法,可能已经不适用于最新的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,代表的意义如下:

0 – Halt,关机模式  

1 – Single,单用户模式  

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

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

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

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

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停止,先删除之前创建的连接:

rm /etc/rc0.d/K99myscript

rm /etc/rc6.d/K99myscript

 

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

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

 

然后重启,这时发现脚本在开机和关机时被分别执行一次。那么我们到现在为止可以得到,就是"/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)然后在后面添加执行逻辑,开机逻辑和关机逻辑只需要一种时,另一处可以什么也不添:

case "$1" in  

start)  

#开机需要执行的逻辑  

;;  

stop)  

#关机需要执行的逻辑  

;;  

*)  

;;  

esac

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

update-rc.d myscript defaults

大功告成!

 

相关主题

更改Ubuntu或Linux Mint开机启动画面

精选文章
热门文章