最近碰到一个需求是需要在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 大功告成!
相关主题 |