编译linux内核(一)

关于linux启动流程

    • 1. 第一阶段:BIOS
      • 1.1 硬件自检
      • 1.2 启动顺序
    • 2. 第二阶段:主引导记录
      • 2.1 主引导记录的结构
      • 2.2 分区表
    • 3. 第三阶段:硬盘启动
      • 3.1 情况A:卷引导记录
      • 3.2 情况B:扩展分区和逻辑分区
      • 3.3 情况C:启动管理器
    • 4. 第四阶段:操作系统
    • 5. Centos7 启动流程
      • 5.1 第一步:启动硬件
      • 5.2 第二步、启动GRUB2引导程序
      • 5.3 第三步、启动内核引导程序(Kernel)
        • 1. 加载驱动
        • 2. 挂载根文件系统(rootfs)
        • 3. initramfs
        • 4. init 程序
      • 5.4 第四步、执行systemed系统初始化

从打开电源到开始操作,计算机的启动是一个非常复杂的过程。

  • boot的含义

先问一个问题,"启动"用英语怎么说?
回答是boot。可是,boot原来的意思是靴子,“启动"与靴子有什么关系呢? 原来,这里的boot是bootstrap(鞋带)的缩写,它来自一句谚语:
  “pull oneself up by one’s bootstraps”
字面意思是"拽着鞋带把自己拉起来”,这当然是不可能的事情。最早的时候,工程师们用它来比喻,计算机启动是一个很矛盾的过程:必须先运行程序,然后计算机才能启动,但是计算机不启动就无法运行程序!

早期真的是这样,必须想尽各种办法,把一小段程序装进内存,然后计算机才能正常运行。所以,工程师们把这个过程叫做"拉鞋带",久而久之就简称为boot了。

计算机的整个启动过程分成四个阶段

1. 第一阶段:BIOS

上个世纪70年代初,“只读内存”(read-only memory,缩写为ROM)发明,开机程序被刷入ROM芯片,计算机通电后,第一件事就是读取它。
编译linux内核(一)_第1张图片
这块芯片里的程序叫做"基本输入输出系统"(Basic Input/Output System),简称为BIOS

1.1 硬件自检

BIOS程序首先检查,计算机硬件能否满足运行的基本条件,这叫做"硬件自检"(Power-On Self-Test),缩写为POST

如果硬件出现问题,主板会发出不同含义的蜂鸣,启动中止。如果没有问题,屏幕就会显示出CPU、内存、硬盘等信息。
编译linux内核(一)_第2张图片

1.2 启动顺序

硬件自检完成后,BIOS把控制权转交给下一阶段的启动程序。

这时,BIOS需要知道,“下一阶段的启动程序"具体存放在哪一个设备。也就是说,BIOS需要有一个外部储存设备的排序,排在前面的设备就是优先转交控制权的设备。这种排序叫做"启动顺序”(Boot Sequence)。

打开BIOS的操作界面,里面有一项就是"设定启动顺序"。
编译linux内核(一)_第3张图片

2. 第二阶段:主引导记录

BIOS按照"启动顺序",把控制权转交给排在第一位的储存设备。

这时,计算机读取该设备的第一个扇区,也就是读取最前面的512个字节。如果这512个字节的最后两个字节是0x55和0xAA,表明这个设备可以用于启动;如果不是,表明设备不能用于启动,控制权于是被转交给"启动顺序"中的下一个设备。

这最前面的512个字节,就叫做"主引导记录"(Master boot record,缩写为MBR)。

2.1 主引导记录的结构

"主引导记录"只有512个字节,放不了太多东西。它的主要作用是,告诉计算机到硬盘的哪一个位置去找操作系统

主引导记录由三个部分组成:

  • 第1-446字节:调用操作系统的机器码【446字节】
  • 第447-510字节:分区表(Partition table)【64字节】
  • 第511-512字节:主引导记录签名(0x55和0xAA)【2字节】

其中,第二部分"分区表"的作用,是将硬盘分成若干个区

2.2 分区表

硬盘分区有很多好处。考虑到每个区可以安装不同的操作系统,"主引导记录"因此必须知道将控制权转交给哪个区。

分区表的长度只有64个字节,里面又分成四项,每项16个字节。所以,一个硬盘最多只能分四个一级分区,又叫做"主分区"。(所以会出现我们使用fdisk分区的时候,只能有至多4个主分区,或者3个主分区+1个扩展分区等等情况)

每个主分区的16个字节,由6个部分组成

  • 第1个字节:如果为0x80,就表示该主分区是激活分区,控制权要转交给这个分区。四个主分区里面只能有一个是激活的。
  • 第2-4个字节:主分区第一个扇区的物理位置(柱面、磁头、扇区号等等)。
  • 第5个字节:主分区类型。
  • 第6-8个字节:主分区最后一个扇区的物理位置。
  • 第9-12字节:该主分区第一个扇区的逻辑地址。
  • 第13-16字节:主分区的扇区总数。
  • 物理地址是内存的实际地址
  • 基地址是每个段的起始地址,又称段地址
  • 程序将基地址与 CPU 生成的地址相加得到逻辑地址,即逻辑地址 = 基地址 + CPU 生成地址

最后的四个字节(“主分区的扇区总数”),决定了这个主分区的长度。也就是说,一个主分区的扇区总数最多不超过2的32次方。

如果每个扇区为512个字节,就意味着单个分区最大不超过2TB。再考虑到扇区的逻辑地址也是32位,所以单个硬盘可利用的空间最大也不超过2TB。如果想使用更大的硬盘,只有2个方法:一是提高每个扇区的字节数,二是增加扇区总数。

3. 第三阶段:硬盘启动

这时,计算机的控制权就要转交给硬盘的某个分区了,这里又分成三种情况

3.1 情况A:卷引导记录

上一节提到,四个主分区里面,只有一个是激活的。计算机会读取激活分区的第一个扇区,叫做"卷引导记录"(Volume boot record,缩写为VBR)。

"卷引导记录"的主要作用是,告诉计算机,操作系统在这个分区里的位置。然后,计算机就会加载操作系统了。

3.2 情况B:扩展分区和逻辑分区

随着硬盘越来越大,四个主分区已经不够了,需要更多的分区。但是,分区表只有四项,因此规定有且仅有一个区可以被定义成"扩展分区"(Extended partition)。

所谓"扩展分区",就是指这个区里面又分成多个区。这种分区里面的分区,就叫做"逻辑分区"(logical partition)。

计算机先读取扩展分区的第一个扇区,叫做"扩展引导记录"(Extended boot record,缩写为EBR)。它里面也包含一张64字节的分区表,但是最多只有两项(也就是两个逻辑分区)。

计算机接着读取第二个逻辑分区的第一个扇区,再从里面的分区表中找到第三个逻辑分区的位置,以此类推,直到某个逻辑分区的分区表只包含它自身为止(即只有一个分区项)。因此,扩展分区可以包含无数个逻辑分区。

但是,似乎很少通过这种方式启动操作系统。如果操作系统确实安装在扩展分区,一般采用下一种方式启动。

3.3 情况C:启动管理器

在这种情况下,计算机读取"主引导记录"前面446字节的机器码之后,不再把控制权转交给某一个分区,而是运行事先安装的"启动管理器"(boot loader),由用户选择启动哪一个操作系统。

Linux环境中,目前最流行的启动管理器是grub
编译linux内核(一)_第4张图片

4. 第四阶段:操作系统

控制权转交给操作系统后,操作系统的内核首先被载入内存。

以Linux系统为例,先载入/boot目录下面的kernel。内核加载成功后,第一个运行的程序是/sbin/init。它根据配置文件(Debian系统是/etc/initab)产生init进程。这是Linux启动后的第一个进程,pid进程编号为1,其他进程都是它的后代。

然后,init线程加载系统的各个模块,比如窗口程序和网络程序,直至执行/bin/login程序,跳出登录界面,等待用户输入用户名和密码。

至此,全部启动过程完成。

5. Centos7 启动流程

编译linux内核(一)_第5张图片
Centos6和Centos7启动区别

CentOS 6 的服务管理机制是 systemv,采用 service 命令来管理所有的服务。
从 CentOS 7 开始,服务从原来的由 systemv 管理机制升级到了systemd,统一采用 systemctl 命令来管理所有的服务。

本文将介绍Centos7的启动流程

5.1 第一步:启动硬件

这部分前面已经介绍过了,为了叙述的完整性,此处快速带过

打开电源;POST自检;BIOS逐一排查设备启动顺序;如果是硬盘启动,读取硬盘的MBR的BootLoader。(这里默认MBR分区,暂不考虑GPT分区)
这里主要有三个需要了解的地方:BIOS启动顺序、MBR和BootLoader。

  • BIOS的启动顺序:

    硬盘 -> 光盘 -> U盘 -> 软盘

  • MBR
     MBR(Main Boot Record),是硬盘的0柱面,0磁道、1扇区(第一个扇区),称为主引导扇区,也称为主引导记录。它由三部分组成:主引导程序(BootLoader)、硬盘分区表DPT(Disk Partition table)和硬盘有效标志(55AA)。
      注:硬盘默认一个扇区大小为512字节。

    第一部分,主引导程序(BootLoader)占446个字节,负责从活动分区中装载,并运行系统引导程序。
    第二部分,硬盘分区表DPT占64个字节,有4个分区表项,每个分区表项占16个字节,硬盘中分区有多少以及每一个分区的大小都记录在其中。
    第三部分,硬盘有效标志,占2个字节,固定为55AA。如果这个标志位0xAA55,就认为这个是MBR。

  • BootLoader
    不同的系统有不同的主引导程序(BootLoader)。Windows使用的是NTLDR(NT Loader,Windows NT系列操作系统)、Bootmgr(Boot Manager,Windows Vista,7,8,10),Linux一般使用的是grub(也叫grub legacy)和grub2。

      CentOS6一般使用的是grub。GRUB(GRand Unified Bootloader)是一个来自GNU项目的多操作系统启动程序
    

5.2 第二步、启动GRUB2引导程序

CentOS7的主引导程序使用的是grub2,而CentOS6的主要引导程序是grub。

GRUB 是什么?
GRUB 是一个用于加载和管理系统启动的完整程序。它是 Linux 发行版中最常见的引导程序(bootloader)。引导程序是计算机启动时运行的第一个软件。它加载操作系统的内核,然后再由内核初始化操作系统的其他部分(包括 Shell、显示管理器、桌面环境 等等)。。GRUB2引导加载器的主要作用是加载Linux内核映像(vmlinuz)到内存,并执行内核映像

GRUB2在执行时会依次读取以下几个文件:

  • grub.cfg(主配置文件)
  • 设备映像(通常为/boot目录下的initramfs或initrd.img文件)
  • 内核映像(vmlinuz)

GRUB2的启动流程

  1. BIOS/UEFI加载MBR到内存
  2. MBR寻找/boot/grub目录下的core.img文件,将其加载到内存
  3. core.img读取/boot/grub/grub.cfg文件,解析出menuentries并加载
  4. menuentries中包含了指向设备映像和内核映像的引导配置信息
  5. 设备映像的启动参数和内核映像的启动参数被合并成最终的内核启动参数
  6. 内核映像被加载到内存中

GRUB的启动分为三个阶段stage1,stage1.5和stage2,这三个阶段也被分为三个文件(在某些情况下,可以没有stage1和stage1.5)

  • Stage1可以理解为已写入MBR的BootLoader中的小程序部分。因为MBR空间有限,所以MBR当中安装最小程序。其主要作用是装载Stage2,即加载剩余的GRUB程序。

    stage1可以嵌入到MBR中,即MBR的头446个字节(后面为分区表64字节,0x55和0xAA两个校验字节),也可以存储在活动分区的第一个扇区512字节, 然后由MBR来加载。所以stage1最多为512字节,如果存储在MBR中,则只能最大为446字节。stage1中保存了stage1.5的地址,并负责加载stage1.5的前512字节。之所以stage1只能加载512字节,是为了遵循MBR的规则。

  • Stage1.5是MBR后面的分区,并不处在MBR中。它是Stage1和Stage2的桥梁,能够识别启动分区文件系统,GRUB访问/boot分区grub目录下的Stage2文件,将Stage2载入内存并执行。

    进入stage1.5,由于只加载了前512字节,所以stage1.5首先要负责把剩余部分代码,由自己加载到内存中。对于stage1.5来说,它可以识别和支持文件系统。可以查看/boot/grub目录下,有多个后缀为stage1.5文件,其前缀即为支持的文件系统,也就是说要支持一个文件系统,就有一个对应的stage1.5文件。至于加载哪个文件,已经硬编码在stage1中。这个文件系统为stage2所在的文件系统。stage2文件是真正保存在文件系统中的。这样通过对应的stage1.5文件,就可以正确加载stage2文件。为什么会有stage1.5这个阶段呢?主要是当stage2不连续或者需要在stage2前,对文件系统做些特殊处理。如果没有这样的需求,完全可以避免stage1.5。

  • Stage2运行后,开始解析GRUB的配置文件/boot分区下/grub/grub.conf。然后显示操作系统启动菜单加载内核镜像到内存,通过/boot/initrd开头文件建立虚拟RAM DISK虚拟内存文件系统(initrd),最后将控制权转交给内核程序。

    stage2文件为最主要的加载代码,这时由于已经stage1.5已经支持文件系统了,所以stage2可以比较大。stage2来实现GRUB的各种功能,这里就不列举了,感兴趣的同学可以自己查看GRUB的手册。stage2首先需要找到GRUB的配置文件,来决定如何加载操作系统。

编译linux内核(一)_第6张图片

grub.cfg配置文件是比较复杂了,但不用担心,CentOS7中一般是使用命令进行配置,而不直接去修改配置文件,grub.cfg配置文件开头注释部分说明了它是由/etc/grub.d/目录下文件和/etc/default/grub文件组成的。
  
一般修改好配置后都需要使用命令grub2-mkconfig -o /boot/grub2/grub.cfg,将配置文件重新生成。

5.3 第三步、启动内核引导程序(Kernel)

内核引导的步骤:

  1. 探测可识别到的所有硬件设备
  2. 加载硬件驱动程序(有可能会借助于initramfs加载驱动)
  3. 以只读方式挂载根文件系统(rootfs,确保没问题再改为读写方式)
  4. 运行用户空间的第一个应用程序:/sbin/init
      
    注意:相比于6系统,7系统执行的初始化程序变成了/usr/lib/systemd/systemd
1. 加载驱动

内核是如何加载驱动的,有些是编译到内核里面,有些是编译成ko,让系统自动加载。总的说来,在Linux下可以通过两种方式加载驱动程序:静态加载和动态加载

  • 静态加载就是把驱动程序直接编译进内核,系统启动后可以直接调用。
  • 动态加载利用了Linux的module特性,可以在系统启动后用insmod命令添加模块(.ko),在不需要的时候用rmmod命令卸载模块。
  • 静态加载过程
    将模块的程序编译到Linux内核中,也就是在编译内核时选择Y的模块,静态由do_initcall函数加载。先来看看initcall在哪里

    	kernel 3.10.108
    
    start_kernel(void) // init/main.c 内核启动的入口, 负责初始化调度,中断和内存, 最后启动1号进程和2号进程
      -> rest_init(void)
        -> kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
                             |
                             V
                       kernel_init() // linux 1号进程, top一下系统就能找到1号进程了
                         -> kernel_init_freeable()
                           -> do_basic_setup()
                             -> do_initcalls()
    

    do_initcalls 中会定义的各个模块加载顺序,加载顺序分为16个等级,加载时按照16个等级依次加载内核驱动。关于每个等级的定义参考 include/linux/init.h

    举个例子 device使用的是 arch_initcall,而driver使用的是 module_init,因为 arch_initcall的优先级大于module_init,所以设备驱动的device先于driver在总线上添加。

  • 动态加载过程
    将模块的程序编译成.ko,也就是在编译内核时选择M的模块,通过insmodmodprobe 或者udev动态加载到内核中。

    insmod 绝对路径/xx.ko,而modprobe xx即可,不用加.ko或.o后缀,也不用加路径。最重要的一点是:modprobe同时会加载当前模块所依赖的其它模块。

2. 挂载根文件系统(rootfs)

根文件系统至少包括以下目录:

/etc/:存储重要的配置文件。
/bin/:存储常用且开机时必须用到的执行文件。
/sbin/:存储着开机过程中所需的系统执行文件。
/lib/:存储/bin/及/sbin/的执行文件所需的链接库,以及Linux的内核模块。
/dev/:存储设备文件。

注:五大目录必须存储在根文件系统上,缺一不可。

以只读的方式挂载根文件系统,之所以采用只读的方式挂载根文件系统是因为:此时Linux内核仍在启动阶段,还不是很稳定,如果采用可读可写的方式挂载根文件系统,万一Linux不小心宕机了,一来可能破坏根文件系统上的数据,再者Linux下次开机时得花上很长的时间来检查并修复根文件系统。

3. initramfs

Linux允许将一部分内存作为块设备(RAM block device support)。这通常见于完全运行在内存上的Linux的live发行版。Linux的live发行版会卸载光盘并接着加载到内存中,所以在尝试一个新的操作系统或者修复另一个系统时不会伤害到已安装的系统。

initrd是一个内存中的磁盘结构(ramdisk),它把(initrd)文件系统当做可启动文件系统。用于在内核把控制权交给根文件系统上的init应用程序之前挂载所需的文件系统initrd。Linux内核在此根文件系统上执行脚本(通常称为linuxrc),此脚本的工作是准备系统,切换到真正的根文件系统,然后调用init

initramfs即initram file system,翻译成中文的意思就是初始化ram文件系统,基于tmpfs,是一种大小灵活,直接作用在内存中的文件系统。initramfs包含的工具和脚本,在正式的根文件系统初始化启动之前,就被挂载了。initramfs是可选的,内核编译选项默认开启initramfs。

initramfs的重要作用之一就是允许内核将保存根文件系统的存储设备的驱动不再编译进内核

CentOS 5: initrd
		工具程序:mkinitrd
CentOS 6,7: initramfs
		工具程序:dracut, mkinitrd

一个initramfs至少包含一个文件,即systemd,内核将这个文件执行起来的进程设为main init进程,pid=1。内核挂载initramfs时,文件系统的根分区并没有挂载,所以无法访问文件系统中的文件。多数的嵌入式设备需要一个shell,那么也会在initramfs打包进一个shell。如果还需要其他工具或脚本,也可以打包到initramfs。

4. init 程序

init 程序类型包括:

CentOS 5-:SysV init
	配置文件:/etc/inittab
	
CentOS 6:Upstart
	配置文件:/etc/inittab
		    /etc/init/*.conf			
	
CentOS 7:Systemd
	配置文件:/usr/lib/systemd/system/, /etc/systemd/system/

至此,系统初始化流程(内核级别): POST → BootSequence(BIOS) → BootLoader(MBR) → Kernel(ramdisk) → rootfs(readonly) → /sbin/init

5.4 第四步、执行systemed系统初始化

init进程按顺序启动进程,前一个启动后一个才启动,启动时间长。该进程是一个由内核启动的用户级进程,init始终是第一个进程,进程编号为1,如果内核无法找到init并且运行/bin/sh文件也失败那么系统将无法启动

systemd是centos 7系统中的初始化进程,也就是1号进程。

编译linux内核(一)_第7张图片

初始化执行步骤:

  • 执行默认target配置文件/etc/systemd/system/default.target
  • 执行sysinit.target初始化系统
  • 执行basic.target准备操作系统
  • 启动multi-user.target下的本机与服务器服务
  • 检查/etc/rc.d/rc.local文件是否有用户自定义脚本需要启动
  • 执行multi-user下的getty.target及登录服务,检查default.target是否有其他的服务需要启动。

备注:执行的配置文件/etc/systemd/system/default.target,这是一个软链接,它与默认运行级别有关

Centos7运行级别:

0	系统关机级别,系统默认的运行级别不能设置为0,否则不能正常启动。
	init 0 进行关机。
	poweroff.target
1	单用户模式级别。root 权限,用于系统维护(或修复系统,或重置系统密码信息),禁止远程登录。
	该运行级别是没有网咯的。
	rescue.target
2	多用户模式级别。该级别下没有NFS和网咯的支持。
	multi-user.target
3	完整的多用户文本模式级别。登录后进入到控制台命令行模式。
	multi-user.target
4	预留级别。该级别系统未使用。
	multi-user.target
5	图形化模式级别。登录后进入图形GUI模式。
	graphical.target
6	系统重启级别。默认运行级别不能设置为6,否则系统不能正常启动。
	init 6 系统会重启。
	reboot.target

Linux常见运行级别如下:

运行级别0(关机):系统停机状态,系统默认运行级别不能设为0,否则不能正常启动
运行级别1(单用户root):单用户工作状态,root权限,用于系统维护,禁止远程登陆
运行级别2:多用户状态(没有NFS)
运行级别3(命令行):完全的多用户状态(有NFS),登陆后进入控制台命令行模式
运行级别4(保留):系统未使用,保留
运行级别5(图形):X11控制台,登陆后进入图形GUI模式
运行级别6(重启):系统正常关闭并重启,默认运行级别不能设为6,否则不能正常启动。
        默认级别:3,5。
        切换级别:init #
        查看级别:runlevel ; who -r

启动运行级别程序
根据之前读取的运行级别,操作系统会运行rc0.d到rc6.d中的相应的脚本程序,来完成相应的初始化工作和启动相应的服务。其中以S开头表示系统即将启动的程序,如果以K开头,则代表停止该服务。S和K后紧跟的数字为启动顺序编号。

Centos6、7启动区别

  • CentOS 6:采用了INIT技术,整个开机过程是自检BIOS——MBR引导——GRUB加载——加载内核——启动INIT进程——读取INITTAB配置文件,根据配置文件指定的模式按顺序来启动进程服务。INIT技术让启动流程很清晰,依赖SHELL脚本。因为启动进程时是按顺序一个一个启动,所以速度慢,会因为某个服务卡住而影响系统的启动。
  • CentOS 7:采用了systemd技术。这是替代INIT的新技术,采用了并行方式来启动进程,所以启动速度更快,并且兼容INIT的命令以降低迁移成本。

启动完成后,进入login界面进行登录

编译linux内核(一)_第8张图片

参考《Linux引导启动过程详细分析》

你可能感兴趣的:(#,linux,系统内核,linux,运维,服务器)