Linux系统启动初始化的主要流程是:
1.上电BIOS自检
2.启动Boot Loader(GRUB)
3.加载内核
4.启动第一个进程
5.配置环境
BIOS(英文:Basic Input/Output System),即基本输入输出系统,亦称为ROM BIOS、System BIOS、PC BIOS,是在通电引导阶段运行硬件初始化,以及为操作系统提供运行时服务的固件。BIOS最早随着CP/M操作系统的推出在1975年出现。BIOS预安装在个人电脑的主板上,是个人电脑启动时加载的第一个软件。
现在,BIOS的作用是初始化和测试硬件组件,以及从大容量存储设备(如硬盘)加载引导程序,并由引导程序加载操作系统。BIOS还为DOS操作系统提供键盘、显示及其他I/O设备的硬件抽象层。
许多BIOS程序都只能在特定电脑型号或特定主板型号上运行。早年,BIOS存储于ROM芯片上;现在的BIOS多存储于闪存芯片上,这方便了BIOS的更新。
计算机上电之后,CPU就可以执行程序了,但是此时内存中并没有程序让CPU执行,因为内存是RAM掉电丢失。而且操作系统也没有装上,这个时候就要装系统了。ROM中就固化了一些计算机刚上电要执行的初始化程序,也就是BIOS(Basic Input and Output System,基本输入输出系统) 。简单地理解 BIOS,它就是固化在主板上一个 ROM(只读存储器)芯片上的程序,主要保存计算机的基本输入/输出信息、系统设置信息、开机自检程和系统自启动程序,用来为 计算机提供最底层和最直接的硬件设置与控制。
BIOS 的初始化主要完成以下 3 项工作:
BIOS从MBR中读取启动引导程序,加载至RAM中,就可以执行启动引导程序了。
MBR是用来存储启动引导程序的!!!
MBR 也就是主引导扇区 ,位于硬盘的 0 磁道、0 柱面、1 扇区中,主要记录了启动引导程序和磁盘的分区表 ,如图是MBR的结构:
MBR大小是一个扇区512Byte, 其中 446 Byte 安装了启动引导程序,其后 64 Byte 描述分区表,最后的 2 Byte 是结束标记 。
BIOS从MBR中读取启动引导程序,将启动引导程序加载至RAM中,然后BIOS将控制权交给启动引导程序。
所以,虽然启动引导程序是在MBR中的,但是实际上是由BIOS从MBR中将启动引导程序加载至RAM中运行的!
注意:这里的446Byte中存放的只是启动引导程序的一个镜像文件,后面还要通过这个镜像文件来加载出完整的启动引导程序!
Linux下是通过一个工具Grub2( Grand Unified Bootloader Version 2 ),这个工具就是专门引导系统启动的。
GNU GRUB(简称“GRUB”)是一个来自GNU项目的启动引导程序。GRUB是多启动规范的实现,它允许用户可以在计算机内同时拥有多个操作系统,并在计算机启动时选择希望运行的操作系统。GRUB可用于选择操作系统分区上的不同内核,也可用于向这些内核传递启动参数。
BootLoader,是启动引导程序,启动引导程序的主要任务就是加载操作系统内核, 每种操作系统的文件格式不同,因此,每种操作系统的启动引导程序也不一样。不同的操作系统只有使用自己的启动引导程序才能加载自己的内核。这里使用GRUB2作为启动引导程序。
上面说BIOS将启动引导程序加载到RAM中执行,但是实际上启动引导程序的大小比512Byte要大,所以MBR中的只是一个镜像文件:boot.img
。我们还得通过MBR中的boot.img
找到完整的启动引导程序,从而正式启动内核。
所以,boot.img
的任务并不是启动内核,而是加载其他镜像文件来完成内核的启动,也可以理解为只有446Byte大小的boot.img
不足以启动内核,只能召唤出更加强大厉害的其他镜像文件来启动内核。
这个强大的后援就是:core.img
, core.img 由 lzma_decompress.img、diskboot.img、kernel.img 和一系列的模块组成,功能比较丰富,能做很多事情。
boot.img 先加载的是 core.img 的第一个扇区。如果从硬盘启动的话,这个扇区里面是 diskboot.img,对应的代码是 diskboot.S。boot.img 将控制权交给 diskboot.img 后,diskboot.img 的任务就是将 core.img 的其他部分加载进来,先是解压缩程序 lzma_decompress.img,再往下是 kernel.img,最后是各个模块 module 对应的映像。这里需要注意,它不是 Linux 的内核,而是 grub 的内核。lzma_decompress.img 对应的代码是 startup_raw.S,本来 kernel.img 是压缩过的,现在执行的时候,需要解压缩。
在这之前,我们所有遇到过的程序都非常非常小,完全可以在实模式下运行,但是随着我们加载的东西越来越大,实模式这 1M 的地址空间实在放不下了,所以在真正的解压缩之前,lzma_decompress.img 做了一个重要的决定,就是调用 real_to_prot,切换到保护模式,这样就能在更大的寻址空间里面,加载更多的东西。
切换到保护模式需要做以下几点:
这里kernel.img不是Linux的内核,而是GRUB的内核。
kernel.img 对应的代码是 startup.S 以及一堆 c 文件,在 startup.S 中会调用 grub_main,这是 grub kernel 的主函数,主函数中 grub_load_config() 开始解析,我们上面写的那个 grub.conf 文件里的配置信息。
当启动了操作系统后,就要开始调用 grub_menu_execute_entry() ,开始解析并执行你选择的那一项。
例如里面的 linux16 命令,表示装载指定的内核文件,并传递内核启动参数。于是 grub_cmd_linux() 函数会被调用,它会首先读取 Linux 内核镜像头部的一些数据结构,放到内存中的数据结构来,进行检查。如果检查通过,则会读取整个 Linux 内核镜像到内存。
上面已经引导了操作系统,也就是引导了内核,接下来就是内核的初始化了。
内核启动的入口函数从start_kernel()
开始, 在init/main.c
文件中,start_kernel 相当于内核的 main 函数。打开这个函数,你会发现,里面是各种各样初始化函数 XXXX_init .
内核初始化做了以下这些事:
这样看来,内核初始化靠的是start_kernel()
函数,初始化主要做三件事:
1.创建样板进程(也就是0号进程),以及各个模块的初始化
2.创建用户态的进程
3.创建内核态的进程
用户态访问核心资源时,通过中断来请求,就是系统调用的统一中断,流程如下:
从上面得知,一般程序运行在用户态,如果要想使用核心资源,就要进入内核态,就要通过系统调用。
此外,linux的一些驱动程序都是写在内核中的,当上层应用想要调用驱动的接口时,也要通过系统调用来实现。
但是,直接操作系统调用比较繁琐,所以一般使用glibc库对系统调用进行封装。