Llinux启动流程

部分引用他人总结,如有侵权,请告知本人删除
一、引导过程
    内核引导过程依次涉及的文件为
    src/arch/i386/boot/bootsect.S
    src/arch/i386/boot/setup.S
    src/arch/i386/boot/compressed/head.S
    src/arch/i386/boot/compressed/misc.c
    src/arch/i386/boot/kernel/head.S
    src/init/main.c

1、傀儡引导扇区
    旧的内核,使用BIOS的启动后加载启动存储介质的第一个扇区到0x7c00这个位置并交出执行权限的机制来引导内核。因此这个扇区也称为引导扇区,现在这个机制已弃之不用了,一般使用其他引导器引导,流行的有LILO(Linux Loader)和Grub,引导器通过在主引导记录或分区引导记录里插入代码来获得CPU控制权,进而引导内核。
    傀儡引导扇区的代码由文件src/arch/i386/boot/bootsect.S生成,一般情况下不会被执行,现在的功能仅为输出以下信息
    "Direct booting from floppy is no longer supported"
    "Please use a boot loader program instead"
    "Remove disk and press any key to reboot"
和存储少量由引导器设置给内核的参数。

2、引导器
    引导器依据引导协议将bootsect装载到地址0x9000:0000的位置,然后在bootsect中设置相关参数,由内核识别和使用。关于引导协议,参见内核文档src/Documention/i386/boot.txt描述,引导器还将向内核传递其它信息,在后文介绍

3、探测系统资源
    探测系统可用资源的任务由src/arch/i386/boot/setup.S完成,引导器完成内核镜像的装载后,系统控制权转移至该文件生成的二进制代码。由于bootsect长512字节,该二进制代码被加载至0x9020:0000的位置。
    此部分对系统的影响主要为探测系统配置信息,并将探测到的信息存到相应的内存单元中,设置系统的全局描述符GDT和中断描述符IDT,系统进入保护模式。
    
    源代码依据作用可以分为几个模块
    1、预留内存空间
        根据引导协议,这里预留一部分空间用于内核和引导器交换信息。
    2、重置硬盘控制器
    3、完整性验证
        用于判断bzImage中的setup是否完整装入,老的引导器默认setup占用4个扇区4*512字节,对于大于4个扇区的setup可能不能完全装入,所以setup执行后会立即进行完整性验证。内核构建程序最后阶段调用build程序生成内核镜像bzImage,该程序在setup的实际大小超过4扇区时修改bootsect中的参数记录setup的真实大小,以使新的引导器能完整装入setup。
    4、引导能力判断
        判断引导程序是否有能力正确装入内核镜像,通过引导协议规定传递的参数,让内核镜像知道引导程序是否有能力正确装载自己。与上一条不同,内核镜像发展出不同的形式,zimage构建生成的是小内核镜像,被装载到0x100,bzimage生成的为大内核镜像,被装载到0x100000。
    5、系统内存探测
        三种bios中断都用一遍,预防部分主板不支持某些探测中断
    6、探测其他信息
        包括硬盘信息,处理VOYAGER架构,检测ps/2鼠标
    7、设置跳转地址
        跳转地址依据内核镜像的类型而定,bzimage生成的大内核的跳转地址为0x100000
    8、开启A20地址线
        但仍不能访问1MB以上的空间
    9、设置GDT、IDT
        内核会重设
    10、开启保护机制
    11、控制权跳转
    
4、解压缩内核
    此部分代码在src/arch/i386/boot/compressed/head.S中,由setup跳入。
    经过少量检测后解压缩内核代码到临时空间,然后判断是否需要合并和搬移内核,此处为大内核(参见附录内核构建过程),需要搬移,所以先将搬移和合并内核的代码先复制到0x10000处,然后跳转到0x10000,该处代码完成内核的合并和搬移到0x100000处,最后跳转到该位置。
    
5、进入内核核心初始化
    此部分代码在src/arch/i386/boot/kernel/head.S,由src/arch/i386/boot/compressed/head.S跳入。
    本部分开始涉及真正的内核,为系统建立一个初步的页表,启用分页机制,装载新的GDT、IDT,设置栈指针寄存器%esp指向0号进程init_task内核态栈的栈低,最后跳转位于文件src/init/main.c的系统初始化入口函数start_kernel()进行系统初始化工作。

二、初始化过程
    初始化过程由系统初始化进程init_task完成(也叫0号进程、idle进程,最后会变成空闲进程)。该进程完成系统内存管理子系统、进程管理子系统、中断异常子系统、时间度量子系统等初始化工作。最后通过函数kernel_thread()创建一个名为init的内核线程,然后0号进程调用调度器schedule(),释放处理器的使用权,成为系统空闲进程。
    内核线程init获得处理器后,首先完成对称多处理器系统中应用处理器的初始化,然后挂载系统的根文件系统,完成系统总线、网络协议栈等的初始化。最后调用execve()开始执行用户态程序/sbin/init,此时内核线程init转换称为用户进程。
    init进程会读取/etc/inittab文件(debian没有该文件,所有的配置信息都在/etc/event.d/目录下),并依据此文件来进行初始化工作。/etc/inittab(参见附注)中有这么一句"si::sysinit:/etc/rc.d/rc.sysinit",表明系统需要主动使用"rc.sysinit"这个shell脚本来设置系统环境。但这个文件的文件名在各个版本中是不一样的,需要自行查看确认。
    /etc/rc.d/rc.sysinit的主要初始化流程为:
(1)获取网络环境与主机类型。首先会读取网络环境设置文件"/etc/sysconfig/network",获取主机名称与默认网关等网络环境。
(2)测试与载入内存设备/proc及usb设备/sys。除了/proc外,系统会主动检测是否有usb设备,并主动加载usb驱动,尝试载入usb文件系统。
(3)决定是否启动SELinux。
(4)接口设备的检测与即插即用(pnp)参数的测试。
(5)用户自定义模块的加载。用户可以再"/etc/sysconfig/modules/*.modules"加入自定义的模块,此时会加载到系统中。
(6)加载核心的相关设置。又一个文件"/etc/sysctl.conf",按这个文件的设置值配置功能。
(7)设置系统时间(clock)。
(8)设置终端的控制台的字形。
(9)设置raid及LVM等硬盘功能。
(10)以方式查看检验磁盘文件系统。
(11)进行磁盘配额quota的转换。
(12)重新以读取模式载入系统磁盘。
(13)启动quota功能。
(14)启动系统随机数设备(产生随机数功能)。
(15)清楚启动过程中的临时文件。
(16)将启动信息加载到"/var/log/dmesg"文件中。

结束rc.sysinit之后,将根据文件/etc/inittab中指定的运行级别,运行相应级别的启动脚本,这些脚本负责控制一个后台服务进程运行,每个运行级别的启动脚本位于/etc/rc.d/rc<运行级别>.d目录中。最后将执行用于用户自定义启动执行程序的脚本/etc/rc.d/rc.loacl。这个脚本开头有一段表述可以清晰知道它的作用
# This script will be executed *after* all the other init scripts.
# You can put your own initialization stuff in here if you don’t
# want to do the full Sys V style init stuff.

完成系统所有的启动任务后,linux会启动终端或X-Window来等待用户登录

附:
一、内核镜像构建过程
1、构建内核镜像vmlinux
    根据内核配置文件src/.config中的配置,将系统核心组件和选项为built-in的系统组件进行编译,最终依据链接脚本arch/i386/kernel/vmLinux.lds生成没有压缩的内核核心src/vmLinux
2、对vmlinux进行压缩
    用objcopy来删除vmlinux多余的信息,转化为raw binrary格式,然后使用gzip进行压缩,最后使用链接器ld根据链接脚本arch/i386/boot/compressed/vmLinux.scr将压缩生成的vmlinux.bin.gz转化为可链接的、可重定位格式个文件piggy.o。
3、构建包含解压缩代码的vmlinux镜像
    用ld链接器将piggy.o和head.o和misc.o进行链接产生新的文件vmlinux,其中misc.o包含zlib算法的解压缩代码c语言的实现,head.o中包含的汇编代码用于调用解压缩程序设置参数和解压缩过程使用的堆、栈。然后调用misc.o中的解压缩代码将压缩的内核解压缩。
4、对vmlinux进行再次删减信息
    通过objcopy删除vmlinux的多余信息
5、构建内核镜像bzImage
    利用内核镜像构建工具build将bootsect(src/arch/i386/boot/bootsect.S)、setup(src/arch/i386/boot/setup.S)、vmlinux.bin三个文件依次存放到新建的bzImage文件中

二、/etc/inittab示例:
[root@linux ~]#vi /etc/inittab
# 设置系统启动默认的运行等级设置项目
id:3:initdefault:

# 开始启动运行等级的服务前,使用检测与初始化系统环境的设置文件:
si::sysinit:/etc/rc.d/rc.sysinit

# 7个不同运行等级需要启动的服务的脚本放置位置路径:
10:0:wait:/etc/rc.d/rc 0
11:1:wait:/etc/rc.d/rc 1
12:2:wait:/etc/rc.d/rc 2
13:3:wait:/etc/rc.d/rc 3
14:4:wait:/etc/rc.d/rc 4
15:5:wait:/etc/rc.d/rc 5
16:6:wait:/etc/rc.d/rc 6

# 是否运行按下[ctrl]+[alt]+[del]就重新启动的设置项目:
ca::ctrlatdel:/sbin/shutdown -t3 -r now

# 本机终端启动的个数:
1:2345:respawn:/sbin/mingetty tty1
2:2345:respawn:/sbin/mingetty tty2
3:2345:respawn:/sbin/mingetty tty3
4:2345:respawn:/sbin/mingetty tty4
5:2345:respawn:/sbin/mingetty tty5
6:2345:respawn:/sbin/mingetty tty6

# 在X Window(运行级别5)环境下启动脚本设置项目
x:5:once:/etc/X11/prefdm -nodaemon

三、现代引导简述
现代Linux大多采用独立的引导程序来完成内核的加载,例如最常用的GRUB引导加载器。

GRUB加载流程
安装在硬盘中的GRUB,完整的加载到内存中需要三步:
1.BIOS加载主引导记录MBR(512字节)到内存0x7C00~0x7DFF位置,并跳转执行0x7C00处指令。这些指令设置堆栈(SP=0x2000),探测硬盘/软盘读取模式(LBA/CHS)并保存。无论LBA或者CHS,都将加载另一扇区(该扇区位置在安装过程中确定)代码到内存0x7000位置,然后拷贝到内存0x8000处。跳转至0x8000执行。
2.0x8000~0x81FF处存放第一步中加载的扇区。这部分代码利用第一步中保存的读取模式(mode)、以及堆栈(0x2000),以及安装时指定的扇区位置和扇区数,循环加载GRUB剩余部分到内存0x8200开始的位置。在循环加载时,对于每次迭代,都是先加载到内存0x7000处,然后拷贝到从0x8200开始的正确位置。
3.0x8200~x(x<0x10000) 处存放第二步中加载的指令。这部分指令是一个包含自解压算法lzma的压缩包(启动和解压缩部分是未压缩的,对应为startup.S和lzma_decode.S文件)。GRUB在这里进入CPU保护模式,自解压并释放到0x10000开始内存处,解压完成后再拷贝回原来位置,然后调用grub_main,初始化系统,加载模块,并进入normal或者rescue模式。GRUB将根据配置文件或者用户输入,加载操作系统并运行。
4.如果需要启动Linux,GRUB会根据相应的配置文件(grub.cfg或其他文件)将Linux的内核镜像vmlinuz和initrd的临时RAM盘加载到内存,并且按照multiboot引导规范传递相关内核参数等信息。
5.如果需要启动如Windows等操作系统,可以使用两种方案:①使用GRUB将原本Windows的MBR或者PBR加载到0x7c00,恢复到实模式,跳转执行,重新完成系统的引导。②加载微软的引导管理器ntldr或者bootmgr,直接完成微软系统的启动。

新的电脑已经采用了UEFI的电脑固件来代替传统的BIOS,并且将使用多年的MBR式磁盘分区类型变更为GPT磁盘类型。
两种磁盘类型对比如下:
    MBR使用磁盘第一扇区中的64字节来保存磁盘分区表,仅支持4个主分区。由于历史原因,在分区表中对分区大小描述使用了32位整数,所以支持的最大的硬盘容量为2^32扇区,计2TB,现在看来,硬盘容量已经并超过了这个极限;
    GPT分区的第一个扇区和MBR类似,但是仅有一个主分区,这个主分区大小为整个磁盘大小,分区ID为(0xFE),这样的目的是为了防止不认识GPT的软件将GPT磁盘当做未分区的磁盘。GPT磁盘的扇区2存放GPT磁盘信息,定义了GPT磁盘签名、分区表大小、分区表位置、数据校验和等信息。从3扇区开始的若干个扇区为GPT分区表,每个分区表项128字节,其中包括分区起始扇区64位整数,分区结束扇区64位整数,因此GPT分区最大支持2^64个扇区,大小计8ZB大小的磁盘。从磁盘最后一扇区逆序向前,存放着GPT分区表的备份。
两种计算机固件对比如下:
    BIOS使用汇编编写,整个系统运行在x86的实模式下,存在着编程难度大、硬件寻址能力受限的限制。
    UEFI的代码大部分使用C语言编写。
    
    在BIOS启动时,加载操作系统的步骤是通过加载磁盘的MBR来完成的。
    在UEFI启动时,UEFI首先会初始化CPU和内存等,对于目前常见的UEFI_x64,UEFI将处理器初始化位64位模式,内存采用平坦模式。UEFI支持分区表和文件系统。UEFI将会从EFI(ESP)分区中加载/EFI/Boot/BOOTx64.efi作为系统启动文件。于此同时,UEFI支持在主板的储存中储存多个操作系统的引导管理器加载路径,从而实现对多系统的支持。

本文出自 “重剑无锋” 博客,请务必保留此出处http://qianyang.blog.51cto.com/7130735/1620568

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。