Linux运行级别和启动顺序
本文主要介绍Sysvinit初始化系统,牵涉到一部分upstart的知识。参考Linux的三种Init机制详细的了解Linux系统的初始化机制。
运行级别 | 说明 |
0 | 停机,机器关闭 |
1 | 单用户模式 |
2 | 没有用到/可以用户自定义 |
3 | 完全多用户模式,没有图形界面 |
4 | 没有用到/可以用户自定义 |
5 | 完全多用户模式,有图形界面 |
6 | 重新启动 |
运行级别 | 说明 |
0 | 停机,机器关闭 |
1 | 单用户模式 |
2 | 完全多用户模式,有图形界面 |
3 | 完全多用户模式,有图形界面 |
4 | 完全多用户模式,有图形界面 |
5 | 完全多用户模式,有图形界面 |
6 | 重新启动 |
1 2 3 4 5 6 7 8 9 10 11 |
#查看运行级别命令 runlevel #修改运行级别 sudo vi /etc/inittab #添加以下内容,则默认运行级别修改3 id:3:initdefault: #重启后,再使用命令runlevel查看,会发现运行级别变成了3 #下面是个非主流重启操作 sudo init 6 |
不同运行级别,会执行不同的脚本,这些脚本是在:/etc/rc0.d 到 /etc/rc5.d这些目录中定义的,目录名中的数字和运行级别对应。
这些脚本都是指向/etc/init.d/下文件的符号连接。如果你留意的话,发现这些脚本均是以如下方式命名:
前缀 | 两位数字 | 脚本名称 |
可以是K或者S | 表示脚本被执行的顺序 | /etc/init.d中对应脚本的名称 |
例如:S91apache2、K09apache2等。
前缀S或者K表示启动或者停止某个服务。分别会调用init.d下脚本的do_start()、do_stop()函数。
如果要在运行级别N下禁用服务myservice,则只需要删除rcN.d中删除符号连接,例如:
1 2 3 4 |
#进入运行级别2的目录 cd /etc/rc2.d #修改前缀S为K rm S11myservice |
Linux下的 service 命令可以用于启动或者停止一个服务,该命令会忽略大部分环境变量,并且设置工作目录为 / 。
该命令的本质执行以下二者之一:
- 执行位于/etc/init.d/下的System V init script
- 执行位于/etc/init/下的启动任务(upstart job)
因此,我们只需要编写符合规范的脚本,并按照上一节描述的命名规则放置到对应位置,即可自己定义“服务”,下面是Ubuntu 14的System V init脚本的模板:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
#! /bin/sh ### BEGIN INIT INFO # Provides: Sample daemon # Required-Start: $remote_fs $syslog # Required-Stop: $remote_fs $syslog # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: System V init script example # Description: System V init script example ### END INIT INFO #注意,上面的INIT INFO会被chkconfig命令读取,自动完成/etc/rc*.d中符号链接的更新 # Author: Alex Wang # 如当前服务在mountnfs.sh后运行,PATH只应当包含/usr/*下的内容 PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin DESC="System V init script example" NAME=smpld # 守护程序的路径 DAEMON=/usr/local/bin/$NAME DAEMON_ARGS="--msg HelloWorld" #写入进程ID的文件 PIDFILE=/var/run/$NAME.pid SCRIPTNAME=/etc/init.d/$NAME # 如果守护程序不可执行,则退出 [ -x "$DAEMON" ] || exit 0 # Read configuration variable file if it is present [ -r /etc/default/$NAME ] && . /etc/default/$NAME # 加载rcS变量 . /lib/init/vars.sh # 执行此文件以定义LSB log_*函数 . /lib/lsb/init-functions # # 用于启动(S)守护程序/服务的函数 # do_start() { # 返回值说明: # 0 守护程序启动成功 # 1 守护程序(在此函数执行之前)已经启动 # 2 守护程序无法启动 # 一般使用start-stop-daemon来进行守护程序的启动与关闭 # 测试守护程序是否已经被启动 # start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null || return 1 # 尝试启动守护程序 # start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- $DAEMON_ARGS || return 2 # 添加额外的代码,例如等待进程准备好提供服务,或者仅仅是等待一段时间。等待以便依赖于此守护程序的其它Daemon能够启动 $DAEMON $DAEMON_ARGS } # # 用于停止(K)守护程序/服务的函数 # do_stop() { # 返回值说明: # 0 守护程序停止成功 # 1 守护程序(在此函数执行之前)已经停止 # 2 守护程序无法停止 # 其它值:有错误发生 # 一般使用start-stop-daemon来进行守护程序的启动与关闭 # start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME # RETVAL="$?" # [ "$RETVAL" = 2 ] && return 2 # # 等待守护程序fork出的子进程停止: # start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON # [ "$?" = 2 ] && return 2 # 某些守护程序不会删除其PID文件,需要手工删除 # rm -f $PIDFILE # return "$RETVAL" } # # 用户发送SIGHUP信号给守护程序的函数 # do_reload() { # start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME return 0 } #根据第一个参数是start、stop、reload……,执行不同的函数 case "$1" in start) [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" do_start case "$?" in 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; esac ;; stop) [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" do_stop case "$?" in 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; esac ;; status) status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $? ;; reload|force-reload) log_daemon_msg "Reloading $DESC" "$NAME" do_reload log_end_msg $? ;; restart|force-reload) log_daemon_msg "Restarting $DESC" "$NAME" do_stop case "$?" in 0|1) do_start case "$?" in 0) log_end_msg 0 ;; 1) log_end_msg 1 ;; # 旧的进程仍然在运行 *) log_end_msg 1 ;; # 启动失败 esac ;; *) # Failed to stop log_end_msg 1 ;; esac ;; *) echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2 exit 3 ;; esac #空命令,返回0 : |
下面是实际中的一个具体例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
#! /bin/sh ### BEGIN INIT INFO # Provides: tomcat7d # Required-Start: $syslog # Required-Stop: $syslog # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Gmem tomcat7d daemon # Description: Gmem tomcat7d daemon ### END INIT INFO # Author: Alex Wang export PDT_DIR=/usr/local/Tomcat7 . $PDT_DIR/setenv.sh LOG_FILE=$LOG_DIR/tomcat7d.log touch $LOG_FILE do_log() { echo `date "+%Y-%m-%d %H:%M:%S"` $1 >> $LOG_FILE echo $1 } do_start() { ps -fC "mysqld_safe" | grep "$PDT_DIR/db/my.cnf" > /dev/null if [[ $? -eq 0 ]]; then do_log "$PDT_NAME database service is already up." else do_log "Starting $PDT_NAME database service..." $PDT_DIR/startup-db.sh fi ps -fC "java" | grep "$PDT_DIR/jre/bin/java" > /dev/null if [[ $? -eq 0 ]]; then do_log "$PDT_NAME application service is already up." else do_log "Starting $PDT_NAME application service..." $PDT_DIR/startup-app.sh fi return 0 } do_stop() { ps -fC "mysqld_safe" | grep "$PDT_DIR/db/my.cnf" > /dev/null if [[ $? -eq 0 ]]; then do_log "Stopping $PDT_NAME database service..." $PDT_DIR/db/bin/mysqladmin -uroot -hlocalhost --protocol=tcp shutdown do_log "$PDT_NAME database service stopped." else do_log "$PDT_NAME database service is already down." fi ps -fC "java" | grep "$PDT_DIR/jre/bin/java" > /dev/null if [[ $? -eq 0 ]]; then do_log "Stopping $PDT_NAME application service..." $PDT_DIR/bin/shutdown.sh do_log "$PDT_NAME application service stopped." else do_log "$PDT_NAME application service is already down." fi return 0 } case "$1" in start) do_start ;; stop) do_stop ;; restart) do_stop do_start ;; esac : #该脚本放置于/etc/init.d下 #执行命令: chkconfig --add tomcat7d 即可注册服务 |
命令 chkconfig 用于配置系统启动时服务的行为,新近的Ubuntu版本已经不支持该命令,使用 update-rc.d 命令代替之:
1 |
sudo update-rc.d myservice defaults |
在Ubuntu下,你可能会注意到rc2.d - rc5.d里面有一个数字最大的S99rc.local文件,查看其链接目标/etc/init.d/rc.local ,可以看到类似如下脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
#! /bin/sh ### BEGIN INIT INFO # Provides: rc.local # Required-Start: $all # Required-Stop: # Default-Start: 2 3 4 5 # Default-Stop: # Short-Description: Run /etc/rc.local if it exist ### END INIT INFO PATH=/sbin:/usr/sbin:/bin:/usr/bin . /lib/init/vars.sh . /lib/lsb/init-functions do_start() { if [ -x /etc/rc.local ]; then [ "$VERBOSE" != no ] && log_begin_msg "Running local boot scripts (/etc/rc.local)" /etc/rc.local ES=$? [ "$VERBOSE" != no ] && log_end_msg $ES return $ES fi } case "$1" in start) do_start ;; restart|reload|force-reload) echo "Error: argument '$1' not supported" >&2 exit 3 ;; stop) ;; *) echo "Usage: $0 start|stop" >&2 exit 3 ;; esac |
在执行S操作的时候,/etc/init.d/rc.local 会调用脚本/etc/rc.local,我们常常用这个脚本来执行一些操作:这些操作在所有系统服务启动完毕后执行。
脚本执行由 init 进程发起,以Ubuntu为例,步骤如下:
-
/etc/init/ 目录下的所有*.conf文件被执行,这些文件可能具有类似如下的内容:
123456789description "deferred execution scheduler"#启动的时机,可能是运行级别(runlevel)、文件系统(filesystem)等start on runlevel [2345]stop on runlevel [!2345]expect forkrespawnexec atd - /etc/rc*.d/ 目录下的脚本,按照顺序执行
- /etc/rc.local 脚本被执行
Leave a Reply