《2022年linux内核启动流程总结doc资料 .pdf》由会员分享,可在线阅读,更多相关《2022年linux内核启动流程总结doc资料 .pdf(7页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、X86体系结构内核启动分析一、硬件检测当机器加电后它首先执行BIOS( 基本输入输出系统)中的代码, BIOS 首先执行加电自检程序 (POST) ,当自检通过程便完成了硬件的启动。当自检完成后BIOS 按照系统 COMS 中设置的启动顺序搜寻有效的启动驱动器(这里我们以硬盘为例),并读入系统引导扇区,并将系统控制权交给引导程序。二、加载和执行引导程序系统引导程序主要是把系统内核装载到内存,启动盘必须在第一个逻辑磁道上包含引导记录。这 512 个字节的扇区又被称作是引导扇区,在系统完成加电自检后,BIOS从启动盘中将引导扇区读入到内存中。一旦引导记录加载完毕,BIOS 就交出系统的执行控制权,
2、跳转到引导程序的头部执行。有关 linux pc的引导程序 lilo 和grub , lilo 和grub 可以引导多个系统,嵌入式系统上,最常见的 bootloader是UBOOT ,如果机器上要装多系统的话一般都会用到它们,这一引导程序也储存在引导扇区中或者存放在主引导记录中(MBR) ,lilo 和grub 都许允用户自己配置,它们在系统安装时建立了关于系统内核占用磁盘数据块的位置对照表。比如, grub 程序就非常强大。Gurb 运行后,将初始化设置内核运行所需的环境。然后加载内核镜像。grub 磁盘引导全过程:stage1: grub读取磁盘第一个512字节(硬盘的0道0面1扇区,被
3、称为MBR (主引导记录) ,也称为 bootsect)。 MBR 由一部分 bootloader的引导代码、分区表和魔数三部分组成。stage1_5: 识别各种不同的文件系统格式。这使得grub 识别到文件系统。stage2: 加载系统引导菜单(/boot/grub/menu.lst或grub.lst 根据 grub 版本不同文件位置会有所不同),加载内核 vmlinuz 和RAM 磁盘 initrd 。有时候基本引导装载程序(stage1) 不能识别 stage2 所在的文件系统分区,那么这时候就需要stage1.5 来连接 stage1 和stage2 了假设有如下 grub 配置代码r
4、oot (hd0,0)/grub分区kernel /vmlinuz 2.6.35.10 74.fc14.i686 ro root=/dev/ram0 /linux分区initrd /initramfs 2.6.35.10 74.fc14.i686.img 精品资料 - - - 欢迎下载 - - - - - - - - - - - 欢迎下载 名师归纳 - - - - - - - - - -第 1 页,共 7 页 - - - - - - - - - - 要搞清楚上面两个root 的关系 ,root (hd0,0)中的 root 是grub 命令 ,它用来指定 boot 所在的分区作为 grub 的
5、根目录 .而root=/dev/ram0是kernel 的参数 ,它告诉操作系统内核加载完毕之后 ,真实的文件系统所在的设备.要注意 grub 的根目录和文件系统的根目录的区别。kernel 命令用来指定内核所在的位置,/ 代表 (hd0,0), 也就是 grub 的根目录 initrd 命令用来指定初始化 ram 的img 文件所在位置。三、内核启动内核映像文件vmlinuz :包含有 linux 内核的静态链接的可执行文件,传统上,vmlinux被称为可引导的内核镜像。vmlinuz是vmlinux的压缩文件。其构成如下:(1)第一个 512 字节(以前是在arch/i386/boot/b
6、ootsect.S)?(2)第二个,一段代码,若干个不多于512 字节的段(以前是在arch/i386/boot/setup.S)?(3)保护模式下的内核代码(在arch/x86/boot/main.c)。bzImage 文件:使用 make bzImage命令编译内核源代码,可以得到采用zlib 算法压缩的 zImage 文件,即 bigzImage文件。老的 zImage 解压缩内核到低端内存,bzImage则解压缩内核到高端内存(1M (0 x100000)以上),在保护模式下执行。bzImage文件一般包含有 vmlinuz 、bootsect.o、setup.o 、解压缩程序misc
7、.o 、以及其他一些相关文件(如 piggy.o)。注意,在 Linux 2.6 内核中, bootsect.S和setup.S 被整合为 header.S 。initramfs(或initrd) 文件: initrd 是initialized ram disk的意思。主要用于加载硬件驱动模块,辅助内核的启动,挂载真正的根文件系统。装载 Linux 内核的第一步应该是加载实模式代码(boot sector和setup 代码), grub 就会把实模式代码 setup 加载到 0 x07C00 之上的某个地址上,其中setup 的前 512 个字节是 boot sector (引导扇区),现在这
8、个引导扇区的作用并不是用来引导系统,而是为了兼容及传递一些参数。之后grub 跳转到 setup 的入口点,入口点为_start 例程 (根据arch/x86/boot/setup.ld可知 )。然后 setup 最后跳到 arch/x86/boot/main.c再经过一系列的跳转,跳到 start_kernel()函数,这是 Linux 内核的启动函数。main.c 文件是整个 Linux 内核的中央联结点。每种体系结构都会执行一些底层设置函数,然后执行名为start_kernel的函数(在 init/main.c中可以找到这个函数)。可以认为main.c 是内核的 “ 粘合剂( glue
9、) ” ,之前执行的代码都是各种体系结构相关的代码,一旦到达start_kernel(),就与体系结构无关了。 start_kernel()会调用一系列初始化函数来设置中断,执行进一步的内存配置,解析内核命令行参数。然后调用fs/dcache.c:vfs_caches_init()-精品资料 - - - 欢迎下载 - - - - - - - - - - - 欢迎下载 名师归纳 - - - - - - - - - -第 2 页,共 7 页 - - - - - - - - - - -fs/namespace.c:mnt_init()创建基于内存的rootfs 文件系统(是一个虚拟的内存文件系统,称
10、为 VFS),这是系统初始化时的根结点,即/ 结点,后面 VFS会指向真实的文件系统。 fs/namespace.c:mnt_init()会调用 fs/ramfs/inode.c:init_rootfs()会调用fs/filesystems.c:register_filesystem()注册 rootfs 。然后fs/namespace.c:init_mount_tree()调用 fs/super.c:do_kern_mount()在内核中挂载rootfs ,调用 fs/fs_struct.c:set_fs_root()将当前的 rootfs 文件系统配置为根文件系统。此时rootfs 里只有
11、根目录。为什么不直接把真实的文件系统配置为根文件系统?答案很简单,内核中没有真实根文件系统设备(如硬盘,USB)的驱动,而且即便你将根文件系统的设备驱动编译到内核中,此时它们还尚未加载,实际上所有内核中的驱动是由后面的kernel_init线程进行加载。另外,我们的root 设备都是以设备文件的方式指定的,如果没有根文件系统,设备文件怎么可能存在呢?start_kernel()在最后会调用 rest_init(),这个函数会启动一个内核线程来运行kernel_init(),自己则调用cpu_idle() 进入空闲循环,让调度器接管控制权。抢占式的调度器就可以周期性地接管控制权,从而提供多任务处
12、理能力。kernel_init()用于完成初始化rootfs 、加载内核模块、挂载真正的根文件系统。(因为已经初始化了 rootfs ,而且还加载了内核模块,所以可以找到设备如硬盘、内存,然后就可以把分区设置为根设备,并在根设备上挂载文件系统)挂载完真正的根文件系统后,goto 到out ,将挂载点从当前目录移到/ ,并把 / 作为系统的根目录,至此虚拟文件系统切换到了实际的根文件系统。目前 2.6 的kernel 支持三方式来挂载最终的根文件系统:(1)所有需要的设备和文件系统驱动被编译进内核,没有initrd 。通过 “root= 参数指定的根设备, init/main.c:kernel_
13、init()将调用 prepare_namespace()直接在指定的根设备上挂载最终的根文件系统。通过可选的init= 选项,还可以运行用户指定的init程序。(2)一些设备和文件驱动作为模块来构建并存放的initrd 中。 initrd 被称为 ramdisk ,是一个独立的小型文件系统。它需要包含/linuxrc 程序(或脚本),用于加载这些驱动模块,并挂载最终的根文件系统(这个根文件系统在pc平台存放在硬盘上,结合使用 pivot_root 系统调用),然后initrd 被卸载。 initrd 由prepare_namespace()挂载和运行。内核必须要使用CONFIG_BLK_DE
14、V_RAM(支持 ramdisk )和 CONFIG_BLK_DEV_INITRD(支持 initrd )选项进行编译才能支持initrd 。(方法 1只挂载了一次文件系统,而这个方法挂载了两次)initrd 文件通过在 grub 引导时用 initrd 命令指定。它有两种格式,一种是类似于linux2.4内核使用的传统格式的文件系统镜像,称之为imageinitrd ,它的制作方法同Linux2.4 内核的initrd 一样,其核心文件就是/linuxrc 。另外一种格式的initrd 是cpio 格式的,这种格式的initrd 从linux 2.5 起开始引入,使用cpio 工具生成,其核
15、心文件不再是/linuxrc ,而是 /init,这种initrd 称为 cpioinitrd 。为了向后兼容,linux2.6 内核对 cpioinitrd精品资料 - - - 欢迎下载 - - - - - - - - - - - 欢迎下载 名师归纳 - - - - - - - - - -第 3 页,共 7 页 - - - - - - - - - - 和imageinitrd 这两种格式的initrd 均支持,但对其处理流程有着显著的区别。cpioinitrd 的处理与 initramfs 类似,会直接跳过prepare_namespace(),imageinitrd的处理则由 prepar
16、e_namespace()进行。(3)使用 initramfs 。prepare_namespace()调用会被跳过。这意味着必须有一个程序来完成这些工作。这个程序是通过修改usr/gen_init_cpio.c的方式,或通过新的initrd 格式(一个 cpio 归档文件)存放在initramfs 中的,它必须是/init 。这个程序负责prepare_namespace()所做的所有工作。为了保持向后兼容,在现在的内核中,/init程序只有是来自 cpio 归档的情况才会被运行。如果不是来自cpio 归档, init/main.c:kernel_init()将运行 prepare_name
17、space()来挂载最终的根文件系统,并运行一个预先定义的init程序(或者是用户通过 init= 指定的,或者是/sbin/init ,/etc/init ,/bin/init )。 initramfs 是从 2.5 kernel开始引入的一种新的实现机制。顾名思义,initramfs 只是一种 RAM filesystem而不是 disk 。initramfs 实际是一个包含在内核映像内部的cpio 归档,启动所需的用户程序和驱动模块被归档成一个文件。因此,不需要cache ,也不需要文件系统。编译 2.6版本的 linux 内核时,编译系统总会创建initramfs ,然后通过连接脚本a
18、rchx86kernelvmlinux.lds.S把它与编译好的内核连接成一个文件,它被链接到地址_initramfs_start_initramfs_end处。内核源代码树中的usr 目录就是专门用于构建内核中的initramfs 的。缺省情况下,initramfs 是空的, X86 架构下的文件大小是134个字节。实际上它的含义就是:在内核镜像中附加一个cpio 包,这个 cpio包中包含了一个小型的文件系统,当内核启动时,内核将这个cpio 包解开,并且将其中包含的文件系统释放到rootfs 中,内核中的一部分初始化代码会放到这个文件系统中,作为用户层进程来执行。这样带来的明显的好处是精
19、简了内核的初始化代码,而且使得内核的初始化过程更容易定制。注意initramfs 和initrd 都可以是 cpio 包,可以压缩也可以不压缩。但initramfs 是包含在内核映像中的,作为内核的一部分存在,因此它不会由bootloader (如 grub )单独地加载,而initrd 是另外单独编译生成的,是一个独立的文件,会由 bootloader 单独加载到 RAM 中内核空间以外的地址处。目前initramfs 只支持 cpio 包格式,它会被populate_rootfsunpack_to_rootfs(&_initramfs_start, &_initramfs_end &_in
20、itramfs_start, 0)函数解压、解析并拷贝到根目录。initramfs 被解析处理后原始的cpio包(压缩或非压缩 )所占的空间 (&_initramfs_start &_initramfs_end)是作为系统的一部分直接保留在系统中,不会被释放掉。而对于initrd 镜像文件,如果没有在命令行中设置keepinitd 命令,那么 initrd 镜像文件被处理后其原始文件所占的空间(initrd_end initrd_start) 将被释放掉。四、启动应用程序prepare_namspace执行完后,真正的文件系统就挂载成功。转入init_post(),它用来运行用户空间的第一个进
21、程,即众所周知的init 进程,在我的 ubuntu下, init 先读/etc/init/下的配置文件,配置文件描述了运行级别、等,并通过从/etc/rcX.d目录到/etc/init.d目录的初始化脚本的链接来启动与终止系统服务。执行相关脚本,以完成系统初始化,如设置键盘、字体,装载模块,设置网络等,最后运行登录程序,出现登录界面。运行用户空间中的init 进程可能是以下几种情况:(1) noinitrd方式,则直接运行用户空间中的/sbin/init(或 /etc/init,/bin/init),精品资料 - - - 欢迎下载 - - - - - - - - - - - 欢迎下载 名师归
22、纳 - - - - - - - - - -第 4 页,共 7 页 - - - - - - - - - - 作为第一个用户进程。(2)传统的 imageinitrd方式。运行的第一个程序是/linuxrc脚本,由它来启动用户空间中的 init 进程。(3) cpioinitrd和initramfs方式。运行的第一个程序是/init 脚本,由它来启动用户空间中的 init 进程。总的来说, x86架构的 Linux 内核启动过程分为 6大步,分别为:(1)实模式的入口函数_start() :在 header.S 中,这里会进入众所周知的main 函数,它拷贝 bootloader 的各个参数,执行
23、基本硬件设置,解析命令行参数。(2)保护模式的入口函数startup_32() :在 compressed/header_32.S中,这里会解压bzImage 内核映像,加载vmlinux 内核文件。(3)内核入口函数startup_32() :在 kernel/header_32.S中,这就是所谓的进程0,它会进入体系结构无关的start_kernel() 函数,即众所周知的Linux 内核启动函数。start_kernel() 会做大量的内核初始化操作,解析内核启动的命令行参数,并启动一个内核线程来完成内核模块初始化的过程,然后进入空闲循环。(4)内核模块初始化的入口函数kernel_in
24、it() :在 init/main.c 中,这里会启动内核模块、创建基于内存的rootfs 、加载 initramfs 文件或 cpioinitrd ,并启动一个内核线程来运行其中的 /init脚本,完成真正根文件系统的挂载。(5)根文件系统挂载脚本/init :这里会挂载根文件系统、运行/sbin/init ,从而启动众所周知的进程 1。(6)init 进程的系统初始化过程:执行相关脚本,以完成系统初始化,如设置键盘、字体,装载模块,设置网络等,最后运行登录程序,出现登录界面。如果从体系结构无关的视角来看,start_kernel() 可以看作时体系结构无关的Linux main 函数,它是
25、体系结构无关的代码的统一入口函数,这也是为什么文件会命名为init/main.c 的原因。这个main.c 粘合剂把各种体系结构的代码“ 粘合 ” 到一个统一的入口处。精品资料 - - - 欢迎下载 - - - - - - - - - - - 欢迎下载 名师归纳 - - - - - - - - - -第 5 页,共 7 页 - - - - - - - - - - 精品资料 - - - 欢迎下载 - - - - - - - - - - - 欢迎下载 名师归纳 - - - - - - - - - -第 6 页,共 7 页 - - - - - - - - - - 文档编码:KDHSIBDSUFVBSUDHSIDHSIBF-SDSD587FCDCVDCJUH 欢迎下载 精美文档欢迎下载 精品资料 - - - 欢迎下载 - - - - - - - - - - - 欢迎下载 名师归纳 - - - - - - - - - -第 7 页,共 7 页 - - - - - - - - - -