初始化程序
init 程序指的是内核加载到内存中后执行的第一个初始化程序,后续进程的创建与管理都是由 init 程序完成。
- 在 CentOS 5 中使用的 init 程序是 SysV,程序文件为
/sbin/init
,这是真正意义上的 init 程序,进程名称也叫init
,它的所有配置都存放在/etc/inittab
中; - 在 CentOS 6 中使用的 init 程序是 Upstart(由 Ubuntu 研发),被重命名为
init
了,程序文件也为/sbin/init
,它的配置文件存放在/etc/inittab
和/etc/init/*.conf
中; - 在 CentOS 7 中使用的 init 程序是 Systemd,程序文件为
/usr/lib/systemd/systemd
,进程名称为systemd
,它的配置文件存放在/usr/lib/systemd/system
和/etc/systemd/system
中;
下面的流程是以 CentOS 5、6 为例。
运行级别
在 CentOS 5 系统中就有运行级别的概念(类似于 Windows 中的安全模式),它是为了系统的运行或维护等应用目的而设定,目前常用的运行级别有如下 7 个:
- 0 级别,关机系统;
- 1 级别,单用户模式,Single 级别,类似 Windows 的安全模式,无需登陆系统直接以 root 用户自动登录,是一个维护模式;
- 2 级别,多用户模式,也是一种维护模式,需要输入正确的用户名和密码才能登入系统,会启动网络功能,但不会启动网络文件系统(NFS),也是一个维护模式;
- 3 级别,多用户模式,正常模式,提供了文本界面;
- 4 级别,预留级别;
- 5 级别,多用户模式,正常模式,提供了图形界面;
- 6 级别,重启系统;
默认的级别是 3 或 5。
运行级别决定了要启动哪些服务,激活哪些功能。
用户空间的很多初始化工作都是 init 根据其配置文件 /etc/inittab
来确定的,这也就意味着,所谓运行级别的改变、切换都是通过 init 应用程序来进行的。
查看运行级别
查看当前系统的运行级别可使用 runlevel
命令查看:
root@zze ~: runlevel
N 3
第 1 个数字是上一次的运行级别(
N
表示一直使用的是当前运行级别),第 2 个数字是当前的运行级别。
who
命令的-r
选项也可以查看运行级别:
root@zze ~: who -r
run-level 3 2019-12-12 14:35
切换运行级别
要想切换级别,直接使用 init
命令即可,格式如下:
init <级别>
对于不同的级别到底有什么不同取决于什么呢?
所谓不同级别,它们分别要通过配置文件当中所指定的要完成的不同初始化功能来实现。
对 CentOS 5 或 6 来讲,init 的主配置文件是 /etc/inittab
,这个配置文件中每行都可以定义一个动作和与之对应的 process。
CentOS 6 的 /etc/inittab
配置文件中除去注释剩下内容如下:
id:3:initdefault:
CentOS 6 相对 CentOS 5 来说把
/etc/inittab
中的配置切割开了,其中只存放了一行用来设定默认的运行级别,其余的配置都存放在/etc/init/*.conf
中,这些文件遵循 Upstart 语法格式,启动机制基本相同。
它遵循如下格式:
id:runlevel:action:process
id:唯一标识,不重复即可;
runlevel:process 在哪一个级别会启动;
action:以何种模式启动 process;
常用的 action 有如下:
wait:切换至此级别才运行一次;
respawn:重新启动该进程;
initdefault:设定默认运行级别,process 省略;
sysinit:系统初始化,通常依赖于一个脚本,一般指定为 /etc/rc.d/rc.sysinit;
...
process:具体操作的进程;
系统初始化脚本
所以当尝试运行 /sbin/init
程序时,它就要去读取 /etc/inittab
配置文件了,而在上面已经看到 CentOS 6 的配置文件第一行就是设置默认的运行级别,所以 init 程序会读取到自己的运行级别。
在 init 确定了自己的运行级别之后,它就知道了自己要启动哪些服务,运行哪些进程。
在 CentOS 5 上执行完设定默认运行级别这一行后,就会运行下面这行:
si::sysinit:/etc/rc.d/rc.sysinit
这一行中:
runlevel
位置并没有指定运行级别,表示该行内容在任何级别都生效;action
位置指定的是sysinit
,表示执行一个系统初始化操作;process
位置指定的是/etc/rc.d/rc.sysinit
,即通过该脚本完成系统初始化内容;
在 /etc/rc.d/rc.sysinit
这个脚本当中会完成许许多多的系统初始化任务,比如说:
- 设置主机名;
- 设置欢迎信息;
- 激活 udev 和 selinux;
- 挂载
/etc/fstab
中所定义的每一个文件系统; - 检测根文件系统,检测完后以读写方式重新挂载根文件系统(为安全考虑内核初次挂载时是使用的只读挂载);
- 设置系统时钟;
- 读取
/etc/sysctl.conf
文件中的内核参数来设定内核参数; - 激活交换分区(swap);
- 激活 LVM 设备即 software raid 设备;
- 加载各种网络设备、额外驱动程序的加载;
- 清理操作;
整个系统初始化操作都是在其中完成的。
运行指定级别下的服务脚本
在初始化操作执行、设定完毕之后,init 程序就要进行下一步操作——运行指定运行级别下的服务脚本。
这些操作在 /etc/inittab
中的设定大致如下:
l0:0:wait:/etc/rc.d/rc 0
l1:1:wait:/etc/rc.d/rc 1
l2:2:wait:/etc/rc.d/rc 2
l3:3:wait:/etc/rc.d/rc 3
l4:4:wait:/etc/rc.d/rc 4
l5:5:wait:/etc/rc.d/rc 5
l6:6:wait:/etc/rc.d/rc 6
即如果当前的运行级别为 n
,那么就会运行 /etc/rc.d/rc
脚本,且传递一个参数 n
。
给 /etc/rc.d/rc
脚本传递 n
就意味着要去读取 /etc/rc.d/rcn.d/
下的所有文件。
这里的
n
代指0-6
中某个运行级别,可以看到每个级别都对应了一个目录。
而这些文件命名也是有规则的,它们要么是以 K
开头,要么是以 S
开头,以 /etc/rc.d/rc3.d/
下的文件为例:
并且,/etc/rc.d/rc
这个脚本会将这两类脚本分隔开,做以下操作:
- 先去读取所有以
K
开头的脚本文件,并且传递一个stop
参数以执行关闭操作; - 然后读取所有以
S
开头的脚本文件,并且传递一个start
参数以执行启动操作;
即执行所有以 K
开头的脚本终止对应进程,运行所有以 S
开头的启动对应进程。
对应 CentOS 6 下的 /etc/rc.d/rc
中这部分代码:
而在 for
循环中这些脚本是会按照文件名排序执行的,通过查看 /etc/rc.d/rc3.d/
下的文件可以看到:
- 不管是以
S
开头还是以K
开头的文件名的K
或S
后面都紧跟一个数字,这个数字的作用就是用来排序,即这个数字决定了脚本的执行次序; /etc/rc.d/rcn.d
下的文件都是软链接,它们都指向/etc/init.d/
的脚本,并且这些软链接文件名都是在源文件名的前面加上K<序号>
或S<序号>
;
在前面有说到过 0 级别表示关机操作,所以 /etc/rc.d/rc0.d/
下大部分应该都是以 K
开头的脚本文件,因为关机操作需要终止所有进程,查看 /etc/rc.d/rc0.d/
:
果不其然,唯一两个以 S
开头的脚本是 S00killall
和 S01halt
,它们的作用分别是杀掉所有进程、执行关机操作。
执行次序的定义
还有一个问题,/etc/rc.d/rcn.d/
下的各个脚本的序号是在哪儿定义的呢?为什么 NetworkManager
的链接文件序号是 84
而不是 85
或者其它的序号呢?
额,其实每个脚本的序号信息都定义在 /etc/rc.d/init.d/
或 /etc/init.d
目录下的和源脚本(/etc/init.d/
下的脚本)同名的脚本,如:
这里还是以 NetworkManager
为例:
可以看到在注释行中有这样一行:
# chkconfig: - 23 84
-:第 1 个数字;
23:第 2 个数字;
84:第 3 个数字;
其中每个数字的含义如下:
- 第 1 个数字:当我们把一个脚本交给
chkconfig
管理时,经过chkconfig
的处理在每个级别的/etc/rc.d/rcn.d
下都会生成脚本的链接文件,而该数字则表示在初始化时在哪些级别下的链接文件名设置为S
开头,即脚本为启动状态,-
表示在所有级别下默认都不启动,数字可以连续,如23
表示在第 2、3 级别下都为启动状态; - 第 2 个数字:启动序号,表示当脚本的链接名为
S
开头时的序号; - 第 3 个数字:关闭序号,表示当脚本的链接名为
K
开头时的序号;
一般来说一个脚本的启动序号小,那么关闭序号就大;启动序号大,那么关闭序号就小。
- 启动序号小表示先启动,很可能被别的服务所依赖,所以需要晚关闭,关闭序号就大;
- 启动序号大表示后启动,依赖别的服务的几率大,所以需要先关闭,启动序号就小;
后来可能是由于这种定义方式过于简陋,所以 chkconfig
也支持下面注释部分的定义方式:
### BEGIN INIT INFO
# Provides: network_manager $network
# Required-Start: messagebus
# Required-Stop: messagebus
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: start and stop NetworkManager
# Description: NetworkManager is a tool for easily managing network connections
### END INIT INFO
该段内容重点字段含义如下:
Required-Start
:由什么控制启动;Required-Stop
:由什么控制关闭;Default-Start
:定义源脚本在哪些级别下默认是启动状态;Default-Stop
:定义源脚本在哪些级别下默认是关闭状态;Short-Description
:简述;Description
:描述;
该种方式其实是 Upstart 使用的格式,它也可以被
chkconfig
所识别。
chkconfig命令
综上所述,在 /etc/init.d/
下的所有脚本在每一个级别对应的 /etc/rc.d/rcn.d
目录下都有一个链接文件,而这个文件在某级别下启动或终止取决于链接文件名是以 S
开头还是以 K
开头。
如果我们要知道 /etc/init.d/
下的某个脚本在某个级别下是否是启动的,我们可以查看该文件在对应级别下的软链接文件名是以 S
开头还是以 K
开头的,以 S
开头就是启动状态的,以 K
开头就是终止状态的。
查看脚本在各运行级别下的状态
这样判断也行但还是有点麻烦,为了能够直观的看到指定脚本在各个级别下是否执行,我们可以使用 chkconfig
命令查看,例:
以第一行 NetworkManager
为例,它在各个运行级别下的状态如下:
0:off
:级别 0,关闭;1:off
:级别 1,关闭;2:on
:级别 2,启动;3:on
:级别 3,启动;4:on
:级别 4,启动;5:on
:级别 5,启动;6:off
:级别 6,关闭;
也可查看指定服务在各运行级别下的状态,以查看 NetworkManager
服务为例,如下:
当在某级别下为关闭状态时,则该脚本在对应的 /etc/rc.d/rcn.d/
下的链接文件则肯定是以 K
开头的,反之则是以 S
开头的。
修改脚本在各运行级别下的状态
chkconfig
命令还提供了修改指定脚本在指定运行级别下的状态的功能,格式如下:
chkconfig -level <level> <script_name> <status>
level:级别,0~6,省略时为 2345;
script_name:脚本名称;
status:状态,on 开启,off 关闭,reset 重置;
以修改 NetworkManager
在运行级别 4 时为关闭状态为例,执行命令如下:
chkconfig --level 4 NetworkManager off
执行该命令后它在 /etc/rc.d/rc4.d/
下的链接文件肯定也改成了 K
开头的了,查看一下:
是吧~
添加自定义脚本
如果我们要将自己编写的脚本交给 chkconfig
管理,并且定义在各个运行级别下的执行状态,需要遵守如下几个约定:
1、我们编写的脚本要遵循 SysV 或 chkconfig
规范,可参考【上面执行次序的定义】中 NetworkManager
服务脚本的定义;
2、要将其放置在 /etc/rc.d/init.d/
或 /etc/init.d/
目录下;
3、通过 chkconfig
命令生成该脚本在每个运行级别目录(/etc/rc.d/rcn.d
)下的链接文件;
例:在 /etc/init.d
下编写脚本 test_chkconfig.sh
:
#!/bin/bash
#
# chkconfig: 356 66 33
# description: test chkconfig use
echo 'chkconfig test ~~~~';
添加执行权限:
chmod +x /etc/init.d/test_chkconfig
使用 chkconfig --add <script>
生成链接文件:
chkconfig --add /etc/init.d/test_chkconfig
随机检查一个运行级别目录下是否生成了链接文件:
[root@zze ~]# ls /etc/rc.d/rc3.d/*test*
/etc/rc.d/rc3.d/S66test_chkconfig
查看该服务在各运行级别下的状态:
[root@zze ~]# chkconfig --list test_chkconfig
test_chkconfig 0:off 1:off 2:off 3:on 4:off 5:on 6:on
删除脚本
如果我们希望指定的服务脚本在任何运行级别下都不启动,可以修改它在各运行级别下的状态,也可以直接使用 chkconfig --del <script>
删除对应服务脚本。
以删除 test_chkconfig
脚本为例:
chkconfig --del test_chkconfig
执行完该操作后 test_chkconfig
在各运行级别目录下的链接文件也都被删除了。
/etc/rc.local
很多人都知道 /etc/rc.local
文件在系统启动后会被自动执行?但是它为啥会被自动执行呢?
其实在 /etc/rc.d/rcn.d/
目录下的链接文件并不都是指向 /etc/rc.d/init.d/
或 /etc/init.d
中的脚本,如:
可以看到,在 2、3、4、5 这四个运行级别中,/etc/rc.d/rc{2,3,4,5}.d/S99local
链接是指向 /etc/rc.d/rc.local
文件的,即 /etc/rc.d/rc.local
也是会开机后就会被自动执行的,并且该链接文件的执行序号是最大的,所以它是其中最晚执行的一个脚本。
而 /etc/rc.local
文件就是指向 /etc/rc.d/rc.local
的一个软链接,查看:
这就是它在系统启动后会自动执行的原因~~
对于那些不便放在 /etc/rc.d/init.d
中来使用 chkconfig
命令来控制的自定义脚本,但是我们希望它能在系统启动后(用户登陆之前)自动运行,就可以使用 /etc/rc.local
来执行自定义脚本中的操作。
关联登录程序
在上述流程完毕后,用户就可以登录了,而用户要登录的话,就需要启动终端设备,在终端上关联登录程序打印登录提示符给用户,然后用户就可以登录系统了(此时 shell 程序并未启动)。
所以这一步在 /etc/inittab
中的定义类似于:
tty1:2345:respawn:/usr/sbin/mingetty tty1
tty2:2345:respawn:/usr/sbin/mingetty tty2
tty3:2345:respawn:/usr/sbin/mingetty tty3
tty4:2345:respawn:/usr/sbin/mingetty tty4
tty5:2345:respawn:/usr/sbin/mingetty tty5
tty6:2345:respawn:/usr/sbin/mingetty tty6
这 6 行就表示在 CentOS 系统的运行级别为 2、3、4、5 时启动 6 个虚拟终端,在虚拟终端我们执行登出(logout
)操作后,虚拟终端会立即重启并重新打印登录提示符让我们登录,这就是 respawn
的作用。
1 级别是单用户模式,直接启动在物理终端下操作,不需要启动虚拟终端。
登录并不是 mingetty 程序自身的功能,而是 login 程序的,mingetty 会自动调用 login 程序来打印出登录提示符让我们登录,如果我们输入的用户名和密码正确,login 程序就会启动我们默认的 shell 进程,接下来我们就可以在 shell 环境下来完成一系列操作。
如果运行级别为 5 的话,在
/etc/inittab
下还有一行是专门启动图形化桌面程序,这里不再赘述,了解即可。
评论区