前言,本来让我写博客我是拒绝的CentOS系列启动流程和内核原理(5系列,6系列,7系列)_启动流程然而看见大家都在写,还能不能一起友好的玩耍了?

我要是不写怎能和同大神们一起ZB呢?遂开博客!


一、Linux启动内核文件

1.Linux系统组成

动态视角:内核+根文件系统

静态视角:磁盘分区+相关文件

2.Kernel特点

(1)支持某块化:.ko (kernel object)文件

centos7的ko文件:

CentOS系列启动流程和内核原理(5系列,6系列,7系列)_启动流程_02

centos6的ko文件:CentOS系列启动流程和内核原理(5系列,6系列,7系列)_linux_03

注意:

    Linux内核模块文件的命名方式通常为<模块名称.ko>

    centos6系统的内核模块被集放在/lib/modules/'uname -r '/目录下

    centos7系统的内核模块被集放在/usr/lib/modules/'uname -r '/目录下


(2)支持模块运行时动态装载或卸载;

模块的相关命令:

a)加载模块:insmod   modprobe

insmod和modprobe的区别

当a模块与b模块有依赖关系时,假设安装b模块需先安装a模块。

如果用insmod命令那么需要先insmod  a.ko 然后再insmod  b.ko。

如果用modprobe命令那么直接可以modprobeb.ko。

其中/lib/modules/“内核版本号”/modules.dep中记录了模块之间的依赖关系。

通过modprobe加载的内核均在当前的计算机内有效,计算机重新启动后需要重新加载才有效。
如果想要开机后自动挂载内核,需要将modprobe命令写入/etc/rc.sysint文件中

CentOS系列启动流程和内核原理(5系列,6系列,7系列)_启动流程_04

b)卸载模块:rmmod    modprobe -r

rmmod:只需要调用模块名即可,比如安装的时候是inmod  a.ko 安装完毕后该模块存在于内核中的名字为a,那么卸载该模块只需要rmmod  a即可。

[root@openstack01 ~]# modprobe ip_vs  #动态加载ip_vs模块
[root@openstack01 ~]# lsmod |grep ip_vs #查看模块是否加载成功
ip_vs                 125220  0
libcrc32c               1246  1 ip_vs
ipv6                  317340  289 ip_vs
[root@openstack01 ~]# modprobe -r ip_vs #卸载动态模块
[root@openstack01 ~]# lsmod |grep ip_vs #模块已经卸载干净



c)查看模块:lsmod

输出三列信息

分别为模块 占用内存 是否被调用

如果第三列为0则该模块可以随时可以卸载。

CentOS系列启动流程和内核原理(5系列,6系列,7系列)_linux_05


总结:Linux kernel在单内核设计模型上,吸取了多内核设计的优点,使用了模块化设计

    单内核设计:把所有功能集成于同一个程序;如Linux

    微内核设计:每种功能使用一个单独的子系统实现;如Windows, Solaris


3.kernel组成

(1)核心文件

1)/boot/vmlinuz-VERSION-release

注: vmlinuz最后一一个z表示压缩格式的kernel文件

CentOS系列启动流程和内核原理(5系列,6系列,7系列)_内核_06        

2)ramdisk(中间临时文件根系统,动态创建出来的,使用缓冲和缓存来加速对磁盘上的文件访问)

参考:Linux内核Ramdisk(initrd)机制 

安装操作系统后临时生成的文件,能够扫描当前操作系统的硬盘驱动,装载对应的模块

用于实现系统初始化的基于内存的磁盘设备,把内存中的一段空间当内存使用

CentOS 5:/boot/initrd-VERSION-release.img 

工具程序:mkinitrd

CentOS 6,7:/boot/initramfs-VERSION-release.img

工具程序:dracut, mkinitrd

注意:

a)不是必须的,当自编译内核时候知道硬盘接口时候将硬盘驱动编译进kernel,ramdisk就不用了

b)initrd,基于ramdisk的磁盘映像文件;initramfs,基于ramdisk的文件系统

initrd启动该后用free查看memory时候,有一段空间被buffers和cached占用,二次缓存

从2.6内核开始,initrd.img采用cpio压缩,不再是2.4内核使用的ext2格式,无法使用mount -o loop 挂载。需要使用gunzip解压缩,然后再使用cpio解包

cpio命令:
cpio - copy files to and from archives
-i, --extract                Extract files from an archive (run in copy-in mode)
将打包文件解压或者将设备上的备份还原到系统。
-v, --verbose              Verbosely list the files processed
显示打包过程中的文件名称。
-o, --create                Create the archive (run in copy-out mode)
将文件拷贝打包成文件或者将文件输出到设备上。
-d, --make-directories     Create leading directories where needed
在cpio还原文件的过程中,自动的建立相应的目录。
-m, --preserve-modification-time Retain previous file modification times when creating files
在创建文件时保留以前的文件修改时间



(2)img文件压缩及解压

centos7的initrd文件解压流程:

CentOS系列启动流程和内核原理(5系列,6系列,7系列)_linux_07

CentOS系列启动流程和内核原理(5系列,6系列,7系列)_linux_08

centos6的initrd文件解压流程:

CentOS系列启动流程和内核原理(5系列,6系列,7系列)_内核_09

CentOS系列启动流程和内核原理(5系列,6系列,7系列)_启动流程_10

修改内核后,自定义打包一个内核initrd文件:

CentOS系列启动流程和内核原理(5系列,6系列,7系列)_启动流程_11


(3)img文件挂载方法

在linux中,对于img文件(例如,虚拟机的img文件),有时候需要将其挂载,以便修改其中的内容。能不能将它像iso文件一样挂载呢?

使用kpartx命令:

kpartx - Create device maps from partition tables
-a     Add partition mapping
-d     Delete partition mappings
-l     List partition mappings that would be added -a
-p     set device name-partition number delimiter
-f     force creation of mappings; overrides ’no_partitions’ feature
-g     force GUID partition table (GPT)
-s     sync mode. Don’t return until the partitions are created
-v     Operate verbosely



(然并卵,我在centos6和centos7失败)

据说这样使用:
kpartx -av xxx.img
mount /dev/mapper/loop0p1 /mnt


(4)模块文件:/lib/modules/VERSION-release(与内核版本发行号相同的目录)/*

CentOS系列启动流程和内核原理(5系列,6系列,7系列)_启动流程_12

arch :与平台相关的特有代码,专有的汇编级的代码

crypto:加密解密组件

drivers:驱动程序

fs :文件系统

kernel :内核自己追踪用到的文件

lib:库文件

mm:内存管理功能,memory manage

net:网络功能

sound:和声音相关的驱动程序,单独放出来,因为有很多与声音相关的×××


二、CentOS系列PC架构MBR主机启动流程

POST --> Boot Sequence(BIOS) --> Boot Loader (MBR) --> Kernel(ramdisk) --> rootfs --> switchroot --> /sbin/init -->(/etc/inittab, /etc/init/*.conf) --> 设定运行级别 --> 系统初始化脚本 --> 关闭或启动对应级别下的服务 --> 启动终端

1.POST:加电自检,检查硬件设备是否存在

用于实现POST的代码在主板上ROM(CMOS)芯片上

BIOS:Basic Input and Output System 基本上输入输出系统,固化在ROM芯片上

POST(PowerOnSelfTest)首先对每一个设备进行检查。完成后会寻找存有引导记录的设备,找到后读入操作系统引导记录,然后将系统控制权交给引导记录,并由引导记录来完成系统的顺利启动。


2.Boot Sequence:

按次序查找各引导设备,第一个有引导程序的设备即为本次启动要用到的设备;  

CentOS系列启动流程和内核原理(5系列,6系列,7系列)_内核_13

3.MBR引导,bootloader:引导加载器,程序;

MBR(Master Boot Record) MBR记录一般是在磁盘 0 磁道 1 扇区,共512个字节。前446个字节是BootLoder,后 4*16 的 64 个字节是存放分区信息的,最后 2 个字节是校验信息,一般是 55AA。

提供一个菜单,允许用户选择要启动的系统或不同的内核版本; 把用户选定的内核装载到RAM中的特定空间中,解压、展开,而后把系统控制权移交给内核;

(1)Windows上引导加载器:ntloader

(2)Linux上引导加载器:

1)LILO:短小精悍的linux加载器,1024柱面之后无法加载,现在多用于安卓手机启动    

2)GRUB:Grand Uniform Bootloader统一引导加载器

GRUB 0.X(CentOS 5/6):Grub Legacy

GRUB 1.X(CentOS 7):Grub2,完全重写,设计理念上很大改变

CentOS系列启动流程和内核原理(5系列,6系列,7系列)_linux_14

GRUB(GRand Unified Bootloader)加载内核,就是MBR中的前 446 个字节,是BooTLoader的一种,它的作用是要选择要启动的内核。

1)GRUB程序的组成:

centos6:

CentOS系列启动流程和内核原理(5系列,6系列,7系列)_启动流程_15

主要是由device.map,menulst,stage1,stage2,以及一系列的stage1_5组成。对于这些部分我的理解是这样:

device.map:存放的是内核文件的根分区

menu.lis:是grub.conf的链接文件,但是这个名字我觉得更与它的功能接近,就是菜单列表。里卖弄设置了可以选择的内核菜单。存放于stage2中。

stage:用于grub引导程序过大,所以分2段引导,第一段存放在MBR中,第二段存放于内核文件系统中,第一段引导完成后可以找到 第二段。 但是,第二段是存放于内核文件系统中的,此时还没有格式化文件系统,如何可以访问到第二段的 menu.lst 呢??就需要借助于中间层 stage1_5,有它来协助 stage1 段来访问 stage2 段。stage1_5通常位于 stage1 字段后的 63 个扇区。 由于stage2 在内存中存放可以使用的文件系统不确定,所以这就是有多个 stage1_5 的原因了。

2)grub.conf 文件参数意义

CentOS系列启动流程和内核原理(5系列,6系列,7系列)_启动流程_16

default=0    # 默认启动的内核title, 0 表示是第一个  
timeout=5    # 默认等待时间  
splashp_w_picpath=(hd0,0)/grub/splash.xpm.gz    # 指定菜单的背景图片的路径。为xpm格式,采用gzip压缩,只能为14bits色  
hiddenmenu    # 隐藏菜单  
title CentOS (2.6.32-431.el6.x86_64)    # 标题名,用户可自定义  
    root (hd0,0)    # 指定 grub 的根位置  
    # 指定 kernel 文件的位置,还要指出 root(系统启动后) 的位置,挂载方式 ro,这项很关键。  
    # 加载后会启动 init 进程。   
    kernel /vmlinuz-2.6.32-431.el6.x86_64 ro root=UUID=17df3f60-a2a2-4de3-bdeb-b6fb4950d848 rd_NO_LUKS  KEYBOARDTYPE=pc KEYTABLE=us
LANG=en_US.UTF-8 rd_NO_MD SYSFONT=latarcyrheb-sun16 rd_NO_LVM crashkernel=auto rhgb quiet rd_NO_DM rhgb quiet
    # 在内核启动过程中装载根文件系统时有用
    initrd /initramfs-2.6.32-431.el6.x86_64.img
# initramfs 是以 gzip 压缩的 cpio 格式的文件。内核启动时将他作为一个临时的根文件系统。 # grub 的 stage2 将initrd加载到内存里,让后将其中的内容释放到内容中, 
# 内核便去执行init脚本,这时内核将控制权交给了init文件处理。  
# init 它也主要是加载各种存储介质相关的设备驱动程序。当所需的驱动程序加载完后,  
# 会创建一个根设备,然后将根文件系统rootfs以只读的方式挂载。  
# 这一步结束后,释放未使用的内存,转换到真正的根文件系统上面去,同时运行/sbin/init程序,  
# 执行系统的1号进程。此后系统的控制权就全权交给/sbin/init进程了。

Linux内核在初始化之后会执行init进程,而init进程会挂载我们的根文件系统,但由于init程序也是在根文件系统上的,所以这就有了悖论。

Linux采用两步走的方法来解决这个问题。

Linux2.6 版以前的方法是:除了内核vmlinuz之外还有一个独立的initrd.img映像文件,其实它就是一个文件系统映像,linux内核在初始化后会 mount initrd.img作为一个临时的根文件系统,而init进程就是在initrd.img里的,然后init进程会挂载真正的根文件系统,然后 umount initrd.img。

但Linux2.6内核的实现方式却不太一样,虽然完成的功能是一样的。Linux2.6采用initramfs。 initramfs:init ram filesystem,它是一个cpio格式的内存文件系统,制作的方法有两个,一个是http://blog.csdn.net/htttw/article/details/7215858介绍的,但这样做出来的initramfs是和内核vmlinuz分开的,因此我们需要在grub里写上initramfs的路径。

而另 一种方法是把内核和initramfs制作在一起成为一个文件,方法是在linux源码make menuconfig,然后General setup-->选择Initial RAM filesystem and RAM disk (initramfs/initrd) support,然后在Initramfs source file(s)里输入我们的initramfs目录,然后make bzImage。这种方法做出来的内核就只有一个文件,不需要指定initramfs了。

从2.6内核以后真正的内核文件是initramfs开头的!!initrd开头的注意后面的结尾dump,可能是原始备份的


centos7:

CentOS系列启动流程和内核原理(5系列,6系列,7系列)_linux_17


3)grub的功能

(1)提供菜单,并提供交互式接口

(2)选择要启动的内核或系统

允许传递引导参数给内核

选择界面可隐藏

# 可以自启动是通过 grub 像内核传递参数。  
# 应用之一是:修改 root 密码(忘记密码),使用 e 选项,传递单用户指令。

centos7:

CentOS系列启动流程和内核原理(5系列,6系列,7系列)_linux_18

CentOS系列启动流程和内核原理(5系列,6系列,7系列)_内核_19

centos6:

CentOS系列启动流程和内核原理(5系列,6系列,7系列)_linux_20

CentOS系列启动流程和内核原理(5系列,6系列,7系列)_内核_21

CentOS系列启动流程和内核原理(5系列,6系列,7系列)_内核_22

(3)为编辑功能提供保护机制

启用内核文件

选择运行指定的内核得先输入密码

传递参数

使用e命令得先输入密码

[root@openstack01 ~]# grub-md5-crypt   
Password:   
Retype password:   
$1$32hfn$HcmdhoMIJvGir.2hNKz8W0
# 上面是生成的加密字符串  
# 然后将信息加入到 grub.conf 文件中,格式如下:  
password --md5 $1$32hfn$HcmdhoMIJvGir.2hNKz8W0
# 当然加入 grub.conf 文件的位置不同,加密效果也不一样。  
# 加入到 title 之前的话,会加密整个菜单。  
# 加入到 title 指内的话,会加密对应的操作系统的入口。

 

4.Kernel实现功能

kernel自身初始化,实现功能

 ---> 探测可识别到的所有硬件设备;

---> 加载硬件驱动程序;(有可能会借助于ramdisk加载驱动)

---> 以只读方式挂载根文件系统;

 ---> 运行用户空间的第一个应用程序:/sbin/init

      

5./sbin/init管理用户空间服务进程

init程序的在不同CentOS版本上类型:

CentOS 5及以前:SysV init     配置文件:/etc/inittab

CentOS 6:Ubantu研发的Upstart       配置文件:/etc/inittab   /etc/init/*.conf

CentOS 7:Systemd      配置文件:/usr/lib/systemd/system/, /etc/systemd/system/


(1)CentOS 5:

SysV init配置文件:/etc/inittab

[root@openstack01 ~]# cat  /etc/inittab
id:3:initdefault:    此处 表示默认启动级别为3文本界面,不能为0级别
/etc/inittab文件:每行定义一种action 以及与之对应的process
格式:id:runlevels:action:process
id:一个任务的标识符;
runlevels:在哪些级别启动此任务;#,###,若此处为空则表示所有级别;
action:在什么条件下启动此任务;
wait:等待切换至此任务所在的级别时执行一次;
respawn:再次发起;此任务终止,就自动重新启动;
initdefault:设定默认运行级别;此时process会省略,不是设定任务,而是默认启动级别;
sysinit:设定系统初始化方式,此处一般为指定/etc/rc.d/rc.sysinit脚本(CentOS5和6用到,7无);
process:任务;


(2)CentOS 6:
init程序:upstart,但依然为/sbin/init,
其配置文件: /etc/init/*.conf, /etc/inittab(仅用于定义默认运行级别)
注意:*.conf为upstart风格的配置文件;各功能切割成片段

CentOS系列启动流程和内核原理(5系列,6系列,7系列)_内核_23CentOS系列启动流程和内核原理(5系列,6系列,7系列)_启动流程_24init-system-dbus.conf:主要在哪儿启动服务的配置文件

rcS.conf:  系统初始化脚本

start-ttys.conf:启动时的终端数量

rc.conf:启动服务的配置文件


系统初始化首先从/etc/init/rcS.conf开始

end script
exec /etc/rc.d/rc.sysinit

rcS.conf里面有这样行,表明这个rcS.conf执行完后才开始执行rc.sysinit配置文件CentOS系列启动流程和内核原理(5系列,6系列,7系列)_linux_25


更改默认tty数量文件:

CentOS系列启动流程和内核原理(5系列,6系列,7系列)_启动流程_26


(3)CentOS 7:不需要任何启动脚本

init程序:systemd,配置文件:/usr/lib/systemd/system/*,  /etc/systemd/system/*

完全兼容SysV脚本机制;因此,service命令依然可用;不过,建议使用systemctl命令来控制服务;

 # systemctl  {start|stop|restart|status}  name[.service]


6.启动运行级别初始化控制:/etc/rc.d/rc#.d
(1)系统运行级别:为了系统的运行或维护等目的而设定的机制;
0-6:共7个级别;
0:关机, shutdown
1:单用户模式(single user),root用户,无须认证;维护模式;
2:多用户模式(multi user),会启动网络功能,但不会启动NFS;维护模式;
3:多用户模式(mutli user),完全功能模式;文本界面;
4:预留级别:目前无特别使用目的,但习惯以同3级别功能使用;
5:多用户模式(multi user), 完全功能模式,图形界面;
6:重启,reboot
1) 默认级别:3, 5
2) 级别切换:init #
3) 级别查看命令:who -r ;  runlevel
(2)/etc/rc.d目录
1)rc #脚本:接受一个运行级别数字为参数;当级别切换时启动或关闭服务
K*:要停止的服务;
K##*,优先级,数字越小,越是优先关闭;依赖的服务先关闭,而后关闭被依赖的;
S*:要启动的服务;
S##*,优先级,数字越小,越是优先启动;被依赖的服务先启动,而依赖的服务后启动;
注意:按照glob通配,数字越小排在前面

CentOS系列启动流程和内核原理(5系列,6系列,7系列)_linux_27

注意:开机时启动的服务,越早开启,关闭的时候越靠后

CentOS系列启动流程和内核原理(5系列,6系列,7系列)_内核_28

2)/etc/rc.d/rc脚本框架(vim /etc/rc.d/rc)

关闭服务脚本:

for i in /etc/rc$runlevel.d/K* ; do

        # Check if the subsystem is already up.
        subsys=${i#/etc/rc$runlevel.d/K??}
        [ -f /var/lock/subsys/$subsys -o -f /var/lock/subsys/$subsys.init ] || continue
        check_runlevel "$i" || continue

        # Bring the subsystem down.
        [ -n "$UPSTART" ] && initctl emit --quiet stopping JOB=$subsys
        $i stop
        [ -n "$UPSTART" ] && initctl emit --quiet stopped JOB=$subsys
done

开启服务脚本:

for i in /etc/rc$runlevel.d/S* ; do

        # Check if the subsystem is already up.
        subsys=${i#/etc/rc$runlevel.d/S??}
        [ -f /var/lock/subsys/$subsys ] && continue
        [ -f /var/lock/subsys/$subsys.init ] && continue
        check_runlevel "$i" || continue

        # If we're in confirmation mode, get user confirmation
        if [ "$do_confirm" = "yes" ]; then
                confirm $subsys
                rc=$?
                if [ "$rc" = "1" ]; then
                        continue
                elif [ "$rc" = "2" ]; then
                        do_confirm="no"
                fi
        fi

        update_boot_stage "$subsys"
        # Bring the subsystem up.
        [ -n "$UPSTART" ] && initctl emit --quiet starting JOB=$subsys
        if [ "$subsys" = "halt" -o "$subsys" = "reboot" ]; then
                export LC_ALL=C
                exec $i start
        fi
        $i start
        [ -n "$UPSTART" ] && initctl emit --quiet started JOB=$subsys
done

CentOS系列启动流程和内核原理(5系列,6系列,7系列)_linux_29

开启的时候执行最后一个启动脚本时,执行/etc/rc.d/rc.local脚本,此脚本是启动过程中最后启动的一个脚本。

S99local做了一个软链接给rc.local


(3)/etc/init.d/* (/etc/rc.d/init.d/*)脚本执行方式:

# /etc/init.d/SRV_SCRIPT  {start|stop|restart|status}

# service  SRV_SCRIPT   {start|stop|restart|status}


1.CentOS 6:

chkconfig命令:

管理控制/etc/init.d/每个服务脚本在各级别下的启动或关闭状态;

1) 查看:chkconfig  --list   [name]

CentOS系列启动流程和内核原理(5系列,6系列,7系列)_启动流程_30

2) 添加:chkconfig  --add  name

3) 删除:chkconfig  --del  name

4) 修改指定的链接类型:

chkconfig  [--level  LEVELS]  name  <on|off|reset>

--level LEVELS:指定要控制的级别;默认为2345;

5) 能被添加的服务的脚本定义格式:

#!/bin/bash
#
# chkconfig: ###  ## ##       ======注释:运行级别、启动优先级、关闭优先级======
#
description:



2.CentOS 7:(待续)


(4)/etc/rc.d/rc.local脚本:开机自动读取此文件中命令

正常级别下,最后启动的一个服务S99local没有链接至/etc/init.d下的某脚本

而是链接至了/etc/rc.d/rc.local (/etc/rc.local)脚本;

因此,不便或不需写为服务脚本的程序期望能开机自动运行时,直接放置于此脚本文件中即可。

CentOS系列启动流程和内核原理(5系列,6系列,7系列)_内核_31


7.系统初始化脚本:/etc/rc.d/rc.sysinit

(1) 设置主机名;

(2) 设置欢迎信息;

(3) 激活udev和selinux;

(4) 挂载/etc/fstab文件中定义的所有文件系统;

(5) 检测根文件系统,并以读写方式重新挂载根文件系统;

(6) 设置系统时钟;

(7) 根据/etc/sysctl.conf文件来设置内核参数;

(8) 激活lvm及软raid设备;

(9) 激活swap设备;

(10) 加载额外设备的驱动程序;

(11) 清理操作;


8.启动终端

tty1:2345:respawn:/usr/sbin/mingetty tty1

... ...

tty6:2345:respawn:/usr/sbin/mingetty tty6

(1)mingetty会调用login程序;

(2)打开虚拟终端的程序除了mingetty之外,还有诸如getty等;



启动开机流程总结:

内核级别:

1.POST做开机启动时候的硬件检测功能

2.BootSequence(BIOS)启动加载主引导分区MBR中的引导加载器程序BootLoader

   在LInux现行的BootLoader是三段划分(打破446字节限制)的GRUB程序,

    第1段写在BootLoader中

    第1.5段在其后扇区用于文件系统的引导

    第2段在boot/grub中提供国土部接口和调用系统内核kernel

3.Kernel识别硬件、加载驱动、只读挂载根文件系统、同时交付给用户空间第一个程序/sbin/init

此处特别要注意,系统发行商为了适应多种硬件接口驱动调用,会在第一次安装系统时候,自动识别硬件接口,并调用唯一驱动程序来生成ramdisk文件,以内存当磁盘做虚根,驱动接口后会切换到真实的根文件系统上

CentOS 5系列是initrd,当磁盘映像文件会造成二次缓存缓冲

CentOS 6/7系列改进为initramfs,以文件系统形式可以不二次占用缓存和缓冲

 

用户空间级别

4./sbin/init接管后更具其配置文件来初始化

5.根据/sbin/init中的配置会设置默认运行级别,以及一些在/etc/init.d/设置的开机服务

6./etc/rc.d/rc.sysinit运行系统初始化脚本,完成系统初始化

7.关闭对应级别下需要停止的服务,启动对应级别下需要开启的服务

8.设置登录终端 [--> 启动图形终端]