Centos系统启动流程及相关
一、库相关内容
1. 基本概念
- 库就是函数(function)的集合,可以把函数理解为一种功能,它内部是由很多代码段组成的,因此任何一个程序调用函数其实就是调用功能,为了方便调用会给每个函数都起个名字,通常都是通过函数名完成调用的。站在运行的角度看库,其实它也是二进制程序,唯一跟
/bin
目录下的程序不同的就在于库文件没有自我独立的执行入口,也就是说库要想运行起来必须要别的程序调用才行,因此无法单独执行,但可以作为有单独执行入口程序的代码片段,与别的程序一块存活,用户空间的库以文件.so
结尾,shared object - 头文件:为了让函数拥有更灵活的功能,通常还要让函数能接受参数,不同的函数能接受参数类型与个数都不同。因此,每一个库当中有多少个函数,每一个函数能接受多少个参数,每一个参数都是什么类型等等,都应该有一个文件对其加以描述,而这个文件就叫头文件
2. 根据执行结果对库的分类
- function:函数调用,调用完以后有返回值,这个返回值不是执行状态返回值,而是结果
- procedure:过程调用,没有返回值的代码片段,这种代码片段其实并不是没做任何事,而只是完成后就结束了
二、centos系统启动流程
1. 第一步:加电自检(Post)
启动系统时,ROM会传递给CPU第一条叫醒指令,指引着CPU装载指定位置的指令完成CPU自举,接着CPU根据这个指令去完成一些自检等操作
2. 第二步:BOOT Sequence
完成加电自检后,根据BIOS中设置的引导加载次序查找各引导设备,第一个有引导程序(bootloader)的设备即为本次启动用到的设备
3. 第三步:kernel初始化
当用户选定内核后,内核就会被加载到内存中做解压、展开操作并获得控制权,接着内核开始完成自身初始化,比如探测各硬件、装载各硬件的驱动程序、挂载根文件系统、完成根文件系统切换、启动/sbin/init
程序,需要注意的是,内核在开始挂载根的时候是以只读方式挂载的
ramdisk
在内核初始化过程中,会用到此文件,但这个文件不是必需的。因为内核引导完毕后需要去硬盘上寻找根文件系统,但是寻找根文件系统的前提条件是需要能驱动硬盘,不同用户使用的硬盘千差万别,我们为了保证内核轻量化又不能将各类硬盘的驱动做进内核,所以需要一个外部的辅助机制来帮助内核可以正确的识别硬盘,解决方式就是在安装完操作系统后,由一个命令工具生成这个文件用来提供硬盘驱动,这个文件会在启动系统时直接装入内存,假装一块硬盘使用,给内核模拟了一个精小的根文件系统,接着内核就借助这个微型的根文件系统去装载硬盘驱动,最后再将根文件系统从微型系统中切换到真正的根文件系统中。所以,如果内核中有你使用的硬盘驱动,那么就不需要这个文件。这个文件名称要与内核版本号完全匹配
- centos 5系统中:文件
/boot/initrd-VERSION-release.img
,生成此文件的工具程序是mkinitrd
,模拟成硬盘使用 - centos 6、7:文件
/boot/initramfs-VERSION-release.img
,生成此文件的工具程序是:dracut
,模拟成文件系统
Centos 5上将ramdisk模拟成硬盘,而Centos 6上是模拟成文件系统叫做ramfs, 这个改进是因为内核有个特性可以使用缓冲和缓存加速磁盘上文件的访问,所以,在系统启动时,ramdisk被加载至内存中,因它是模拟成硬盘使用的,而内核认为硬盘都比较慢,所以内核会将ramdisk中的文件再次加载至内核内存的缓冲区当中,等于多了一个步骤。如果模拟成文件系统就没问题了,因为文件系统自身可以完成缓冲和缓存管理的,所以Centos 6以后就变成了ramfs
制作ramdisk文件:~]# mkinitrd /boot/initramfs-$(uname -r).img $(uname -r)
或~]# dracut /boot/initramfs-$(uname -r).img $(uname -r)
4. 第四步:init程序初始化
当内核初始化完成后就会启动init程序,Centos 5、Centos 6、Centos 7用到的init程序各不相同,但是主要工作基本都按照配置文件中规定的步骤去完成相应的初始化工作,如启动默认运行级别、按照运行级别启动或关闭对应的服务、设定主机名称、设定时钟、启动SELinux、完成根文件系统重挂、启动登录程序等等。涉及概念较多,因此详细各步骤请查看本文后续详解
5. 第五步:用户登录
当init程序初始化完成后,会启动用户登录程序,在这个程序中完成用户认证并准许登录
运行级别相关的命令
- 切换运行级别的命令:
init #
- 查看运行级别的命令:
who -r
、runlevel
,输出的信息为两个字符,分别表示上一次运行的级别和当前运行的级别,如果上一次运行的级别为N,表示Null,意思是直接启动系统就是3级别
三、grub相关概念
1. grub版本
在系统启动流程的第二步中(Boot Sequence),根据引导次序去依次寻找各个设备的boot loader。而常见的Boot loader有两种,其一为Windows的NTloader,其二为linux的Grub
linux的引导加载器有两种,早期时候用的是LILO(Linux Loader),现在用于安卓。现使用Grub(GRand Unified Bootloader),大统一引导程序
- grub:
grub 0.x
的版本,这种一般称为grub传统版(grub legacy) - grub2:
grub 1.x
,这种一般称为grub2
2. grub legacy的三个加载阶段
grub为了突破mbr446字节的限制,采用分段启动加载的方式。当启动系统时读取磁盘的mbr,因此可以加载到stage 1,stage 1加载完,它会尝试去加载随后扇区中的stage 1_5,当1_5阶段读取完后,从而就能驱动真正第二阶段的磁盘分区了。其实这个磁盘分区上不光有stage 2文件,还有内核文件及其ramdisk等等都在这个分区上放置着,其配置文位于/etc/grub/grub.conf
,并有一个链接文件位于/etc/grub.conf
1st stage
位于MBR中,第一阶段的主要目的找到硬盘上的2st
1_5st stage
主板Bios必须能识别硬盘,才能够去加载硬盘中的BootLoader,不过硬盘上的BootLoader自身加载完以后也仅仅能够识别当前主机能识别到的硬盘设备,因为硬盘设备能识别并不意味着硬盘设备中的文件系统能识别,文件系统是额外附加的一层软件组织的文件结构,所以要想对接某种文件系统,通常必须要用到文件系统驱动,帮助对应的应用程序(这里指grub 1st stage)能识别和理解相应的文件系统才可以,这里的第1_5 stage也就是提供了让grub 1 stage能识别2阶段的文件系统驱动,从而就能访问对应的第二阶段和内核所在的分区了。通常grub第二阶段、ramdisk和内核文件都会放置在一个最基本分区上,毕竟grub 1_5 stage也不会做的特别复杂。grub会在系统安装时,将系统使用的文件系统驱动等必须组件做成1_5 stage文件,在启动时装载到mbr随后的一段空间中,从而使得第一阶段可以访问第二阶段的文件系统
2st stage
提供一个菜单和一个交互式接口、加载用户选择的内核且允许用户向内核传递参数、隐藏菜单、菜单编辑保护认证、为启用的内核添加认证、其文件存放在存放在/boot/grub
第二阶段的功用:
- 提供一个菜单、交互接口,在这个交互接口中可以使用
e
键进入编辑模式,使用c
键进入命令模式,在命令模式中,可以传递给grub一参数,从而使得grub不用读取配置文件就可以完成启动 - 加载用户选则的内核或操作系统,并且允许用户通过编辑菜单传递参数,必要时还可以隐藏启动菜单
- 为菜单提供了认证机制。第一,给编辑菜单提供认证,第二,启动内核前提供认证
3. grub legacy的配置文件内容
|
4. grub 命令行接口配置
在grub选择菜单中键入c
键可进入命令行接口,常用命令如下
help [key_word]
:获取帮助列表root [DEVICE [HDBIAS]]
:设置当前使用的根设备,对grub来讲,根设备不是用户进入系统后看到的根,而是第二阶段所在的磁盘分区才叫根
对于grub legacy来讲磁盘设备都是使用hd来标识,后面跟上两个#号,用来标识第几块磁盘的第几个分区,用逗号分隔且用括号包含起来,如(hd0,0)
find DEVICE/FILE_NAME
:通过find命令去查找文件,需要先指定硬件设备和分区kernel /PATH
:指明内核文件路径,额外和可以指定很多内核参数,如init=/path
、selinux=0
等等initrd /PATH
:设定为选定内核的ramdiskboot
:启动选定的内核
5. 进入单用户模式
如需进入grub编辑菜单,在需编辑的内核上键入e
键进入二级菜单,接着在kernel
选项上键入e
键,进入三级菜单,补上进入单用户模式的参数1或s或S或single
,键入完毕后敲回车键确定,最后在kernel
选项上敲b
键启动系统
6. grub 安装及修复
- 情况一:重装本机的grub,适用于grub被破坏但未重启系统
|
- 情况二:使用紧急救援模式重装grub,适用于grub被破坏,并且已经重启系统了
|
|
|
- 情况三:向一个新的磁盘中安装grub用于引导其他机器上的系统,这里为了便于演示,都放置于一块硬盘
|
解释下grub的根为何如此诡异:这个根的设置取决于/boot
目录是否被单独分区了,分别来说
- 情况一:
/boot
目录被单独分区
当用户选定内核时,grub通过1_5stage去寻找第二阶段,这时,如果/boot
目录被单独分区了,那么1_5 stage就没必要去访问真正的根文件系统了,因为/boot
目录被单独拿出来了,直接访问grub 2 stage所在的分区就可以了,这样的话,1_5stage就会把grub 2 stage所在的目录当成根,所以在这种情况下设置的路径应该使用/grub/grub.conf
或者/vmlinuz
这样的方式去指定第二阶段 - 情况二:
/boot
没有被单独分区
在这种情况下,/boot
目录是和根放在一起的,stage1_5stage去访问stage 2的时候,就不可能绕过文件系统的根,所以在这种情况下,路径设置就应该使用/boot/grub/grub.conf
或/boot/vmlinux
这种方式
所以,/boot
目录有没有被单独分区决定了第二阶段的访问路径,grub命令行接口的root命令,就是用来指明这个根分区位置的,一般都可以在系统上看到/boot
目录被单独分区了,但这并不意味着/boot
目录不可以和根文件系统放置在同一个分区上
四、init程序的相关概念
1. init程序的类别
SysV
:传统的init程序,Centos 5上用到的就是这个程序,由贝尔实验室研发,Sys为system的简写,而V是版本号(第五版),其配置文件/etc/inittab
,这个init程序存在一个缺点,因其初始化操作都是用脚本实现的,初始化过程中会大量的启动进程、销毁进程,并且进程启动存在依赖关系,比如B进程需等待A进程启动后才能启动,这将极大降低启动速度Upstart
:Centos 6上用到的init程序,由Ubuntu研发。为改进SysV的缺点,Centos 6所以换成了Upstart,它可以基于总线的方式让进程之间互相通信,不用等待进程启动完成,只要命令一初始化就可以把自己的状态通知给其他进程,接近于并行的方式完成初始化,但是Upstart在设计上尽可能的兼容了SysV,所以在Centos5上可以使用的机制在Centos6上也依然可以使用,更重要的是Cenots6也并没有充分发挥Upstart的作用,依然通过脚本启动服务,配置文件/etc/inittab
、/etc/init/*.conf
Systemd
:Centos 7用到的init程序,受启发于MacOS的启动灵感,仿照着做了Systemd,它自己就一个强大的解释器,不需要其他任何程序来启动服务,自行完成程序加载并提交给内核,并且它在init始化时不真正初始化任何服务,当用户第一次访问某个服务时,它才会真正启动,所以这就使得Centos 7 开机可以在秒级别完成,配置文件:/usr/lib/systemd/system/
,/etc/systemd/system/
2. init程序的运行级别
类似Windows系统的安全模式、VAG模式,不同的模式意为着启动了不同的服务、加载了不同的驱动,所以使得我们能在故障出现时,可以提供给用户一个基本环境实现系统维护工作,Centos 5、6也一样也有运行模式的概念,只不过在Centos 5、6上不叫做模式,而称之为运行级别。但在Centos 7以后级别已经失去了它原本的意义了,仅仅是为了能兼容这个理念而采用的,并且配置方式已经有了巨大的变化
- 级别0:关机
- 级别1:单用户模式,也称为single级别,是一个维护模式,类似windows中的安全模式,单用户级别无需登录,直接以root用户切入,比如忘记管理员密码就直接进入1级别,就能够重置管理员密码,当然1级别不仅仅是能够重置密码的,在这个级别系统中大多数的服务都不会启动,它仅仅启动系统基本的运行环境,让我们进行维护工作
- 级别2:多用户模式,也是一种维护模式,这个模式用户需要使用账号名、密码登录,此模式会启动网络功能,但不会启动网络文件系统(NFS),其实1级别模式也可以启动网络功能,但需要用户手动选定才可以
- 级别3:多用户模式(正常模式),只不过使用文本界面
- 级别4:4级别也是多用户模式,但事实上这个级别没定义,是个预留级别,可同3级别
- 级别5:多用户模式,此级别是一个可以正常使用的级别,但在这个级别下将启动图形界面
- 级别6:一旦将运行级别设置为6,就表示重启系统
|
3. init的配置文件及意义及初始化时完成的操作
为了能完成系统初始化,init程序要指挥着很多进程在用户空间启动很多服务,到底启动哪些,取决于配置文件定义的不同初始化工作
(1)Centos 5:SysV
Centos 5的init的主配文件位于/etc/inittab
,这个文件每行定义都遵循特定格式,都是由冒号隔开的四个字段组成
|
当内核启动/sbin/init
开始初始化工作时,init程序就会基于配置文件/etc/inittab
中的设定开始
- 第一步:读取默认运行级别设定
id:3:initdefault:
,这个文件中第一行就是设定默认运行级别的,因为只有运行级别确定了,才能知道该启动哪些服务、运行哪些进程,在id:3:initdefault:
中,id就是这行的标识,3标识运行级别,initdefault这个action就是设定默认运行级别的关键字 - 第二步:
si::sysinit:/etc/rc.d/rc.sysinit
,这一行对应的运行级别段为空表示所有级别都需要运行,当默认运行级别确定后,init程序就会执行标识为si的设定,运行/etc/rc.d/rc.sysinit
这个脚本,这个脚本当中会完成许多的初始化任务比如挂载/etc/fstab
中所定义的每一个文件系统、读取/etc/sysctl
文件中的内核参数对内核进行设定、激活交换分区、激活LVM设备、各种设备的额外驱动程序加载、设定系统时钟、设置主机名称、设置欢迎信息、激活udev和selinux、检测根文件系统并以读写方式重挂、 - 第三步:当
/etc/rc.d/rc.sysinit
这些设定完成后,就会运行对应级别下的指定服务脚本,如l0:0:wait:/etc/rc.d/rc 0
、l1:1:wait:/etc/rc.d/rc 1
…l6:6:wait:/etc/rc.d/rc 6
,从配置文件可以看出在每个rc后面都有一个数字,它们分别对应着/etc/rc.d/rc#.d
这样的目录,数字是几就运行哪个目录下的脚本。假如默认设定的运行级别为3级别,那就会去执行l3:3:wait:/etc/rc.d/rc 3
这一行对应的/etc/rc.d/rc3.d/
目录下的脚本,而不管是数字几对应的目录,其中的文件都是脚本链接。并且它们都遵循统一的命名法则,要么以字母K##开头,要么以字母S##开头(##表示一个两位数字),接着通过脚本给对应目录下所有以K##开头的文件传递一个stop参数,给所有以S##开头的文件传递一个start参数,见下方脚本示例
|
- 接上述第三步内容。在每个级别下,都会先执行K开头的文件,而不管是K开头还是S开头的文件,名称后面的数字至关重要,它决定了在脚本中的执行次序,所以数字小的一定会被先执行。反之,在关闭时,被依赖的反而要后关闭。将来如果想在级别3实现某个服务开机自动运行,就在
/etc/rc.d/rc3.d/
为这个服务创建一个以S##开头的链接文件即可,不过手动创建链接文件或删除链接文件这种方式确实挺低效的,有一个命令可以高效的实现这个操作chkconfig
命令。无论是K开头还是S开头的脚本(LSB脚本),它后面的两位数字都源于脚本中的chkconfig提示信息,比如你应该曾经在某些脚本中看到过chkconfig: - 23 84
这样的配置参数,这个参数就是用来设定S或K后面数字的 - 第三步的补充说明:在正常级别下有一个特殊的服务,也是最后一个启动的服务
/etc/rc.d/rc3.d/S99local
,这个链接文件不同其它文件一样指向/etc/rc.d/init.d/
目录中,而是指向了/etc/rc.d/rc.local
,此文件中定义的命令执行时间是系统启动完成了,用户登录前。它还有个链接文件是/etc/rc.local
。文件的意义是对于那些不便受chkconfig控制写成脚本放在/etc/rc.d/init.d/
目录下,但是需要开机运行的命令,就可以写在这个文件中 - 第四步:当将对应运行级别
l3:3:wait:/etc/rc.d/rc 3
规定服务初始化工作完成,接下来就该运行tty3:2345:respawn:/user/sbin/mingetty tty3
这类操作了,类似的定义一共有6行,每行都在2、3、4、5级别下启动一个tty终端,所以会启动6个终端,而其中的action对应的respawn参数,表示每当这个终端关闭时,就自动重新启动一次。而在每一行process中对的/sbin/mingetty
会自动调用login
程序,login
程序负责验证用户键入的账号、密码,当验证通过则启动默认shell进程,以此类推,如果默认运行级别是5级别,则会多启动一个图形界面。这里启动的虚拟终端可以同时按下Ctrl + Alt + F#
调出,#对应一个数字
(2)Centos 6:Upstart
Centos 6的Upstart程序是向前兼容Centos 5的,但它依然将这个程序命名成init,其配置文件为/etc/inittab
,但是这个配置文件是为了方便用户修改默认运行级别有意留在这里的,其实Upstart程序是不会用这个配置文件的,它用的配置文件在/etc/init/*.conf
,Centos 6只不过是将Centos 5上的文件切分了。需要注意的是,Centos 6的启动初始化配置文件需要使用Upstart配置文件语法格式
(3)Centos 7:systemd
systemd引入了一个新的核心概念用于完成服务控制,我们称之为Unit(单元),由其相关的配置文件进行标识、配置。文件中主要包含了系统服务,监听的socket,保存的快照以及其他与init相关的信息
① systemd的新特性
- 系统引导时实现服务并行启动
- 按需激活进程:在启动之前让进程处于半活动状态。比如httpd服务,它会先帮httpd的端口注册且不启动进程,当第一次访问时再启动之
- 系统状态快照:自我保存当前用户空间进程的运行状态快照,将来可以迅速恢复到过去某个状态
- 基于依赖关系定义服务控制逻辑
- 基于socket的激活机制:意为着socket与服务程序分离,socket由systemd待为监听,程序不用启动,当有进程访问套接字的时候,才临时的按需激活服务
- 基于bus的激活机制:激活设备时还可以基于总线激活,如果这个总线上存在对某个服务的访问,那么就基于总线的请求将设备激活
- 基于device的激活机制:时刻监控中硬件信息,一旦某个设备加入进来,先给它创建设备文件,在将其自动挂载至某路径,挂载点不存在还能自动创建
- 基于path的激活机制:系统可以监控这某个目录或文件存在与否,如果路径变得可用会立刻激活某个服务
- 系统快照:将所有unit当前状态临时保存在持久设备设备中,完成状态回滚
- 向后兼容sysv init脚本:意为着放置于
/etc/init.d/
目录中的服务脚本也能靠systemd启动、控制
② 不兼容的特性
- systemctl命令固定不变:此前的service脚本是用户可以自行定义的,比如支持start、stop、restart等等,如果想多增一个参数只需自定义即可。但对于systemctl就不可以,这些所有的unit文件都是由systemctl控制的,而systemclt命令对每个unit最多支持的参数是固定不可变的,除非去重新去编写systemd或systemctl这个程序
- 非由systemd启动的服务,systemctl无法控制:比如直接通过绝对路径启动的服务,systemctl就无法与之通信,也就意味着systemctl就无法控制,不过可以通过自行编写unit脚本去控制
③ systemd配置文件的位置
/usr/lib/systemd/system
/run/systemd/system
/etc/systemd/system
:设定默认运行级别和每个运行级别启动的及其依赖关系
④ 常见的unit文件类型类型
- Service unit:服务unit,文件扩展名为
.service
,用于定义系统服务,替代了原/etc/init.d/
下的服务脚本 - Target unit:目标unit,文件扩展名为
.target
,用于模拟实现运行级别。其实对systemd来说没有运行级别,因为所有服务默认都是不启动的,只有第一次访问时才启动 - Device unit:设备unit,文件扩展名为
.device
,用于定义内核识别的设备。对于Centos 6来讲,/dev
目录下的设备文件是由udev
根据内核探测输出到/sys
目录的信息创建的,而Centos 7的系统设备文件是由udev
和systemd
联合创建的 - Mount unit:挂载unit,文件扩展名为
.mount
,用于定义文件系统挂载点 - Socket unit:套接字unit,文件扩展名为
.socket
,用于标识进程间通信用的socket文件 - Snapshot unit:快照unit,文件扩展名为
.snapshot
,管理系统快照 - Swap unit:交换unit,文件扩展名为
.swap
,用于标识、管理swap设备 - Automount unit:文件扩展名为
.automount
,用于管理文件系统自动挂载点设置,比如用户插入U盘,要不要自动将U盘挂载上来 - Path unit:文件扩展名为
.path
,用于定义文件系统中的一个文件或目录,它能够监控用户指定的一个特定文件或目录,如果不存在,systemd可以自动创建
⑤ unit文件的组织结构
以/usr/lib/systemd/system/httpd.service
为例,这个配置文件由三个配置段组成:Unit、Service、Install,其中第二个配置段是和文件类型相关的,我们这里使用的httpd的service unit文件,所以第二段为Service,那么如果查看的是target unit文件,第二段及为target。
|
- Unit配置段主要用来定义当前unit的描述信息、unit行为及依赖关系等。其中,
|
- Service配置段主要用来定义与此类型文件相关的专用选项,这里以Service为例所以名称为Service,如果打开的是target unit文件,这里的名称即为Target。所以第二个配置段是定义与此类型unit相关的专用选项,这里说明Service的常用选项
|
- install配置段,定义由
systemctl enable
以及systemctl disable
命令实现服务开机自启或禁用时用到的一些选项
|
对于新创建或修改了Unit文件,必须要通知Systemd重载此配置文件,使用命令systemctl daemon-reload
完成重载
⑥ systemctl命令的用法
对于Centos 7,系统服务主要就是靠service类型的unit文件来实现管控的,它可以兼容/etc/init.d/
下的各类服务脚本
命令格式:systemctl commond NAME.service
|
4. chkconfig命令相关
在init程序初始化中,曾提到过chkconfig提示信息,本部分对chkcofnig相关概念加以说明
(1)chkconfig提示信息
chkconfig提示信息你应该曾经在某些脚本中看到过,如chkconfig: - 23 84这样的配置参数,这个参数就是用来设定init初始化工作中S或K后面数字的,下方为chkconfig提示信息示例
- chkconfig提示信息示例1:
|
- chkconfig提示信息示例2:
|
上述的两种chkconfig提示信息示例中,格式1是早期chkconfig命令所使用的格式,但它有点简陋。于是就出现了格式2,格式2是Upstart所使用的格式,不过这两种都可以被chkconfig所使用
(2)chkconfig命令 [–list] [name]命令
命令格式:chkconfig [option] [name]
|
(3)自定义chkconfig初始化服务脚本
- 编辑一个文件,放到
/etc/init.d/
下,如/etc/init.d/testsrv
,文件内容如下
|
-
给定执行权限并添加至chkconfig命令管控
~]# chmod +x /etc/init.d/testsrv
~]# chkconfig --add /etc/init.d/testsrv
-
查看结果
~]# chkconfig --list testsrv
~]# ll /etc/init.d/rc0.d/K22testsrv
~]# ll /etc/init.d/rc3.d/S88testsrv