嵌入式 Linux操作系统内存磁盘初始化技术详细解析

2019-07-13 04:50发布

Linux内存初始化技术(initrd)用于支持两阶段的系统引导过程,是在系统启动过程中被挂载的临时root文件系统(译者注:这里的root 文件系统是指的根文件系统)。initrd包含很多可执行程序和驱动,并允许在临时的内存磁盘根文件系统被卸载,内存被释放后挂载真实的root文件系统。在许多嵌入式linux文件系统中,initrd是最终的根文件系统。这篇文章主要讲解了linux2.6内核的initrd技术,包括在内核中的创建及使用。   什么是内存磁盘初始化?   Initrd挂载优先级高于真实根文件系统,它被邦定在内核上,做为内核启动过程的一部分被加载(load)。然后,做为两阶段引导过程的第一部分,内核挂载(mount)initrd,用于获得并加载真实有效的文件系统。   为了达到这个目的,initrd包含有最起码的目录与程序,例如insmod,来安装内核模块到内核中。对于桌面或服务器linux,initrd 是临时文件系统,它的生存周期很短,仅仅是做为到达真实根文件系统的桥梁。但对于没有存储设备的嵌入式系统来说,它才是永久性的根文件系统。本篇文章对这两方面均有涉及。   深入分析initrd   Initrd 包含有必须的程序和系统文件,用于支持系统的启动的第二阶段过程。创建初始化内存的方法,是随着你所使用的系统版本而改变的。从Fedora Core3以后,initrd就由回送设备(loop device)建立。什么是回送设备?它是一个设备驱动,允许你将一个文件挂载为块设备,并对其文件系统做出描述。也许loop device并不存在与你的内核中,但是你能够通过内核的配置工具(make menuconfig)打开它。路径是: Device Drivers-》Block Devices-》LoopBack Device support。   下面为检查命令: # mkdir temp ; cd temp # cp /boot/initrd.img.gz . # gunzip initrd.img.gz # mount -t ext -o loop initrd.img /mnt/initrd # ls -la /mnt/initrd # 现在,你可以通过查看/mnt/initrd的子目录来查看initrd的内容。需要注意的是,即使你的initrd镜像文件并不是以.gz做为后缀名,但是你同样可以通过增加此后缀名来让gunzip打开它。   从Fedora Core3开始,默认的initrd镜像就是一个压缩的gpio归档文件。除了用挂载文件的方式以外,你同样可以通过cpio归档的方式来将其挂载成使用了回送设备的压缩镜像。你可以通过以下的指令来检查这个cpio归档文件的内容: # mkdir temp ; cd temp # cp /boot/initrd-2.6.14.2.img initrd-2.6.14.2.img.gz # gunzip initrd-2.6.14.2.img.gz # cpio -i –make-directories < initrd-2.6.14.2.img   你看到的结果将是一个小型根文件系统,如下所示:   # ls -la # drwxr-xr-x 10 root root 4096 May 7 02:48 . drwxr-x— 15 root root 4096 May 7 00:54 .. drwxr-xr-x 2 root root 4096 May 7 02:48 bin drwxr-xr-x 2 root root 4096 May 7 02:48 dev drwxr-xr-x 4 root root 4096 May 7 02:48 etc -rwxr-xr-x 1 root root 812 May 7 02:48 init -rw-r–r– 1 root root 1723392 May 7 02:45 initrd-2.6.14.2.img drwxr-xr-x 2 root root 4096 May 7 02:48 lib drwxr-xr-x 2 root root 4096 May 7 02:48 loopfs drwxr-xr-x 2 root root 4096 May 7 02:48 proc lrwxrwxrwx 1 root root 3 May 7 02:48 sbin -> bin drwxr-xr-x 2 root root 4096 May 7 02:48 sys drwxr-xr-x 2 root root 4096 May 7 02:48 sysroot #   一些小的,但是很有必要的程序组合能在./bin目录下得到,包括nash(它不是一个shell,而是一个脚本解释工具),用于加载内核模块的insmod,以及lvm等。   上面所示目录中,相对比较有趣的是root目录下的初始化文件。这些文件,和传统的linux启动过程中一样,是在initrd镜像被解压缩到RAM中时生成的。待会我们将继续探讨这个问题。   创建initrd的工具   现在,让我们回到一开始的讨论:initrd的镜像是如何被创建的?在传统的linux系统中,initrd是在linux build的时候被创建的。像mkinitrd这样的许许多多的工具,都能够用于通过必须的库和模块来自动构建一个用于过渡到真实根文件系统的 initrd。事实上,mkinitrd工具是一个脚本文件,因此,我们能够很清楚得看到,这个过程是如何进行的。同样的,YAIRD (Yet Another Mkinitrd)工具,也允许我们自定制每一个initrd被构建的阶段。   自己动手,打造自定义的初始化内存盘   由于很多基于linux的嵌入式系统都没有硬盘驱动器,initrd也可以做为永久性的根文件系统。下面我就将告诉你们,如何创建一个initrd 镜像。我使用的是标准linux桌面系统,因此大家即使没有嵌入式目标设备也可以照着做。除了交叉编译以外,嵌入式目标文件的构建过程是相同的。   #!/bin/bash   # Housekeeping… rm -f /tmp/ramdisk.img rm -f /tmp/ramdisk.img.gz   # Ramdisk Constants RDSIZE=4000 BLKSIZE=1024   # Create an empty ramdisk image dd if=/dev/zero of=/tmp/ramdisk.img bs=$BLKSIZE count=$RDSIZE   # Make it an ext2 mountable file system /sbin/mke2fs -F -m 0 -b $BLKSIZE /tmp/ramdisk.img $RDSIZE   # Mount it so that we can populate mount /tmp/ramdisk.img /mnt/initrd -t ext2 -o loop=/dev/loop0   # Populate the filesystem (subdirectories) mkdir /mnt/initrd/bin mkdir /mnt/initrd/sys mkdir /mnt/initrd/dev mkdir /mnt/initrd/proc   # Grab busybox and create the symbolic links pushd /mnt/initrd/bin cp /usr/local/src/busybox-1.1.1/busybox . ln -s busybox ash ln -s busybox mount ln -s busybox echo ln -s busybox ls ln -s busybox cat ln -s busybox ps ln -s busybox dmesg ln -s busybox sysctl popd   # Grab the necessary dev files cp -a /dev/console /mnt/initrd/dev cp -a /dev/ramdisk /mnt/initrd/dev cp -a /dev/ram0 /mnt/initrd/dev cp -a /dev/null /mnt/initrd/dev cp -a /dev/tty1 /mnt/initrd/dev cp -a /dev/tty2 /mnt/initrd/dev   # Equate sbin with bin pushd /mnt/initrd ln -s bin sbin popd   # Create the init file cat >> /mnt/initrd/linuxrc << EOF #!/bin/ash echo echo “Simple initrd is active” echo mount -t proc /proc /proc mount -t sysfs none /sys /bin/ash –login EOF   chmod +x /mnt/initrd/linuxrc   # Finish up… umount /mnt/initrd gzip -9 /tmp/ramdisk.img cp /tmp/ramdisk.img.gz /boot/ramdisk.img.gz   想创建initrd的话,你需要首先创建一个空文件,将/dev/zero(0字符流)做为ramdisk.img的输入。得到的文件大小大约是 4MB(有 4000个1K的块组成)。接下来,用mke2fs命令来创建一个使用这个空文件的ext2文件系统。现在,这个文件就是一个ext2文件系统。ok,接下来,以回路设备的形式挂载这个文件到/mnt/initrd,现在,你就在挂载点拥有一个代表着ext2文件系统的目录,并用与存放你的initrd。其他大多数的脚本语句都是用于实现这个功能。   下一步,就是创建一些必须的子目录,用于生成你的根文件系统: /bin, /sys, /dev, 和 /pro。这里只需要少数几个目录,例如,没有/lib。但是它们已经包含了大部分功能。   如果想让你的根文件系统发挥更大的作用,请使用 BusyBox。这个工具是一个包含了许多独立工具的镜像,这些独立的工具你都能在linux中找到( ash, a等等wk, sed, insmod)。BusyBox的优势在于,它把它们集合在了一起,并分享了公用的部分,从而极大缩小了镜像的体积。这对于嵌入式系统来讲,是非常理想的。请将BustBox镜像从它的源目录中复制出来,到你的/bin目录下,这样,很多指向BusyBox工具集的符号链接将被创建,BusyBox能确定哪一个工具将被使用,并自动引用它。这个/bin目录下被创建的链接的小型集合将用于对启动脚本的支持。 再下一步,就是一小部分特殊设备文件的创建。我从我的/dev文件夹中直接拷贝了出来,别忘了加上-a选项来保持它们原有的属性。   倒数第二步,就是生成linuxrc文件。在内核挂载了内存盘之后,它将搜索并执行相关的启动文件,如果没有找到,内核就将linuxrc文件做为其启动脚本。你最好在这个文件中对环境变量做一些基本设置,例如挂载/proc文件系统等。除了/proc外,我还挂载了/sys文件系统,将消息发送给终端。最后,我调用ash并通过它和根文件系统交互。最后记住,用chmod把linuxrc文件的属性改为可执行。   最后,你的根文件系统算是ok了。现在它并没有被挂载,用gzip将它压缩,并将压缩后的文件ramdisk.img.gz拷贝到/boot目录下,这样它就能被GRUB调用。   想要构建你的初始化ram盘的话,你只需要调用mkird,镜像就将自动创建并拷贝到/boot目录下。   测试自定义的初始化RAM盘   你拥有的新的initrd镜像是在/boot目录下,因此,下一步就是要用你默认的内核来测试它。ok,现在你可以先重新启动你的linux系统,当 GRUB引导画面出现时,按下C键,打开GRUB的命令行工具。现在,你就能通过GRUB确定启动专门的内核和initrd镜像。内核命令是允许你定制内核文件的,而initrd命令则允许你指定专门的initrd镜像文件。当它们都被指定之后,通过启动命令来启动内核,如下所示:   GNU GRUB version 0.95 (638K lower / 97216K upper memory)   [ Minimal BASH-like line editing is supported. For the first word, TAB lists possible command completions. Anywhere else TAB lists the possible completions of a device/filename. ESC at any time exits.]   grub> kernel /bzImage-2.6.1 [Linux-bzImage, setup=0×1400, size=0×29672e]   grub> initrd /ramdisk.img.gz [Linux-initrd @ 0×5f2a000, 0xb5108 bytes]   grub> boot   Uncompressing Linux… OK, booting the kernel.   在内核启动之后,它开始检查initrd镜像是否可用,如果答案是确定的,那么就作为根文件系统加载并挂载它。下面就是这个特殊启动过程的结尾:   md: Autodetecting RAID arrays md: autorun md: … autorun DONE. RAMDISK: Compressed image found at block 0 VFS: Mounted root (ext2 file system). Freeing unused kernel memory: 208k freed / $ ls bin etc linuxrc proc sys dev lib lost+found sbin / $ cat /proc/1/cmdline /bin/ash/linuxrc / $ cd bin /bin $ ls ash cat echo mount sysctl busybox dmesg ls ps /bin $ touch zfile /bin $ ls ash cat echo mount sysctl busybox dmesg ls ps zfile   当启动之后,可以通过ash来进入命令模式。在本例中,我探究了根文件系统并向你演示了,你能通过新建文件来写入这个文件系统。只需要注意,第一步是要创建linuxrc。   通过初始化内存盘启动   现在,大家已经看到了如何构建并使用一个自定制的初始化内存盘,这一节则用于介绍,内核是如何辨认initrd并将其作为它的根文件系统挂载的。我将涉及一些boot chain中的主要的函数并对发生的事件做出解释。   像GRUB这样的boot loader,通常会确认即将加载的内核并复制该内核镜像与任何相关联的initrd到内存中,你可以在你linux内核源程序目录下的./init子目录中找到这些功能实现。   在内核与initrd镜像被解压缩和复制到内存后,内核被调用。此时,开始各种各样的初始化过程,最终,你会发现自己处于init/main.c: init () (subdir/file:function)。这个函数实现了很多的子系统初始化。在这里,要调用init/do_mounts.c: prepare_namespace(),用来准备命名空间(挂载dev 文件系统, RAID, 或者md, devices, 以及, 最后的initrd)。通过对 init/do_mounts_initrd.c:initrd_load()的调用,最终完成对initrd的加载。   initrd_load ()调用init/do_mounts_rd.c:rd_load_image(),来决定是否通过调用init/do_mounts_rd.c: identify_ramdisk_image()来加载内存盘镜像。后面这个函数通过检查内核的编号来确定文件究竟是是minux,etc2, romfs,cramfs,还是gzip格式,直到返回initrd_load_image后,init/do_mounts_rd:crd_load ()又被调用。这个函数负责分配空间给内存盘,并进行校验计算,解压缩,最后将内存盘镜像加载到内存中。此时,你就已经拥有了一个适合于挂载的,在块设备中的initrd镜像。   现在,通过调用init/do_mounts.c:mount_root()将这个块设备做为root挂载。ok,根设备就被创建了,接下来调用的函数是init/do_mounts.c:mount_block_root(),此函数又调用fs/namespace.c: sys_mount()来挂载真实的根文件系统并对其进行chdir操作。   最后,会返回到启动函数中,并调用init/main.c:run_init_process。调用的结果是,初始化进程开始(在这里是通过/linuxrc)。linuxrc可以是一个可执行程序,也可以是脚本(只要脚本解释器能够正常解释它)。   函数调用的层次关系可以从下表中看出。并不是所有与复制、挂载初始化内存盘的函数都被列举出来,这里仅仅是大概的,对整体基本流程的回顾: init/main.c:init init/do_mounts.c:prepare_namespace init/do_mounts_initrd.c:initrd_load init/do_mounts_rd.c:rd_load_image init/do_mounts_rd.c:identify_ramdisk_image init/do_mounts_rd.c:crd_load lib/inflate.c:gunzip init/do_mounts.c:mount_root init/do_mounts.c:mount_block_root init/do_mounts.c:do_mount_root fs/namespace.c:sys_mount init/main.c:run_init_process execve   无盘启动的应用   同很多嵌入式系统的启动一样,本地磁盘(软驱或者光驱)对于启动内核和内存盘根文件系统来说,并不是必须的。DHCP工具能被用于确认网络参数,例如大家熟悉的IP抵制和子网掩码等。此外,TFTP能被用于将内核镜像以及初始化内存盘镜像传送到本地设备。一旦传输完成,linux内核就能被启动以及挂载 initrd,和本地镜像启动的过程一样。   让你的initrd尽可能小   当你在构建嵌入式系统时,总是希望initrd的镜像尽可能小,恩,这里将提供一些小技巧。首先就是使用BusyBox。前面已经提到过,BusyBox包含了很多较大的工具,通常体积都以MB计算,但是它成功得将自己的体积控制在几百KB的范围内。   在本例中,BusyBox镜像使用的是静态链接,因此不需要提供任何库文件。但是,如果你需要得到标准的C库文件来满足自己的二进制程序,除了大体积的 glibc库,你有其他更好的选择。第一个,小体积的uClibc库,是专门用于有空间限制的,标准C库的缩水版本。另一个适用于有空间限制环境的库是 dietlib。记住,你需要在自己的嵌入式系统中,用这些库重新编译你的二进制程序。虽然使用它们会带来一些附加的工作,但是,是值得的。   总结 初始化内存盘技术被创建的最初目的,是为了让内核通过一个临时的根文件系统来过渡到最终的根文件系统。initrd对于嵌入式linux系统同样是很有用处的:它能做为一个非持续性的根文件系统挂载到内存盘中。
简介: 引导 Linux® 系统的过程包括很多阶段。不管您是引导一个标准的 x86 桌面系统,还是引导一台嵌入式的 PowerPC® 机器,很多流程都惊人地相似。本文将探索 Linux 的引导过程,从最初的引导到启动第一个用户空间应用程序。在本文介绍的过程中,您将学习到各种与引导有关的主题,例如引导加载程序、内核解压、初始 RAM 磁盘以及 Linux 引导的其他一些元素。 发布日期: 2006 年 7 月 26 日 
级别: 初级 
访问情况 : 33138 次浏览 
评论: (查看 添加评论 - 登录)
平均分 5 星 共 92 个评分 平均分 (92个评分)
为本文评分

  早期时,启动一台计算机意味着要给计算机喂一条包含引导程序的纸带,或者手工使用前端面板地址/数据/控制开关来加载引导程序。尽管目前的计算机已经装备了很多工具来简化引导过程,但是这一切并没有对整个过程进行必要的简化。 让我们先从高级的视角来查看 Linux 引导过程,这样就可以看到整个过程的全貌了。然后将回顾一下在各个步骤到底发生了什么。在整个过程中,参考一下内核源代码可以帮助我们更好地了解内核源代码树,并在以后对其进行深入分析。 概述 图 1 是我们在 20,000 英尺的高度看到的视图。
图 1. Linux 引导过程在 20,000 英尺处的视图
Linux 引导过程在 20,000 英尺处的视图 
当系统首次引导时,或系统被重置时,处理器会执行一个位于已知位置处的代码。在个人计算机(PC)中,这个位置在基本输入/输出系统(BIOS)中,它保存在主板上的闪存中。嵌入式系统中的中央处理单元(CPU)会调用这个重置向量来启动一个位于闪存/ROM 中的已知地址处的程序。在这两种情况下,结果都是相同的。因为 PC 提供了很多灵活性,BIOS 必须确定要使用哪个设备来引导系统。稍后我们将详细介绍这个过程。 当找到一个引导设备之后,第一阶段的引导加载程序就被装入 RAM 并执行。这个引导加载程序在大小上小于 512 字节(一个扇区),其作用是加载第二阶段的引导加载程序。 当第二阶段的引导加载程序被装入 RAM 并执行时,通常会显示一个动画屏幕,并将 Linux 和一个可选的初始 RAM 磁盘(临时根文件系统)加载到内存中。在加载映像时,第二阶段的引导加载程序就会将控制权交给内核映像,然后内核就可以进行解压和初始化了。在这个阶段中,第二阶段的引导加载程序会检测系统硬件、枚举系统链接的硬件设备、挂载根设备,然后加载必要的内核模块。完成这些操作之后启动第一个用户空间程序(init),并执行高级系统初始化工作。 这就是 Linux 引导的整个过程。现在让我们深入挖掘一下这个过程,并深入研究一下 Linux 引导过程的一些详细信息。 回页首 系统启动 系统启动阶段依赖于引导 Linux 系统上的硬件。在嵌入式平台中,当系统加电或重置时,会使用一个启动环境。这方面的例子包括 U-Boot、RedBoot 和 Lucent 的 MicroMonitor。嵌入式平台通常都是与引导监视器搭配销售的。这些程序位于目标硬件上的闪存中的某一段特殊区域,它们提供了将 Linux 内核映像下载到闪存并继续执行的方法。除了可以存储并引导 Linux 映像之外,这些引导监视器还执行一定级别的系统测试和硬件初始化过程。在嵌入式平台中,这些引导监视器通常会涉及第一阶段和第二阶段的引导加载程序。

提取 MBR 的信息

要查看 MBR 的内容,请使用下面的命令: # dd if=/dev/hda of=mbr.bin bs=512 count=1 #od -xa mbr.bin 这个 dd 命令需要以 root 用户的身份运行,它从 /dev/hda(第一个 IDE 盘) 上读取前 512 个字节的内容,并将其写入 mbr.bin 文件中。od 命令会以十六进制和 ASCII 码格式打印这个二进制文件的内容。 在 PC 中,引导 Linux 是从 BIOS 中的地址 0xFFFF0 处开始的。BIOS 的第一个步骤是加电自检(POST)。POST 的工作是对硬件进行检测。BIOS 的第二个步骤是进行本地设备的枚举和初始化。 给定 BIOS 功能的不同用法之后,BIOS 由两部分组成:POST 代码和运行时服务。当 POST 完成之后,它被从内存中清理了出来,但是 BIOS 运行时服务依然保留在内存中,目标操作系统可以使用这些服务。 要引导一个操作系统,BIOS 运行时会按照 CMOS 的设置定义的顺序来搜索处于活动状态并且可以引导的设备。引导设备可以是软盘、CD-ROM、硬盘上的某个分区、网络上的某个设备,甚至是 USB 闪存。 通常,Linux 都是从硬盘上引导的,其中主引导记录(MBR)中包含主引导加载程序。MBR 是一个 512 字节大小的扇区,位于磁盘上的第一个扇区中(0 道 0 柱面 1 扇区)。当 MBR 被加载到 RAM 中之后,BIOS 就会将控制权交给 MBR。 回页首 第一阶段引导加载程序 MBR 中的主引导加载程序是一个 512 字节大小的映像,其中包含程序代码和一个小分区表(参见图 2)。前 446 个字节是主引导加载程序,其中包含可执行代码和错误消息文本。接下来的 64 个字节是分区表,其中包含 4 个分区的记录(每个记录的大小是 16 个字节)。MBR 以两个特殊数字的字节(0xAA55)结束。这个数字会用来进行 MBR 的有效性检查。
图 2. MBR 剖析
MBR 剖析 
主引导加载程序的工作是查找并加载次引导加载程序(第二阶段)。它是通过在分区表中查找一个活动分区来实现这种功能的。当找到一个活动分区时,它会扫描分区表中的其他分区,以确保它们都不是活动的。当这个过程验证完成之后,就将活动分区的引导记录从这个设备中读入 RAM 中并执行它。 回页首 第二阶段引导加载程序 次引导加载程序(第二阶段引导加载程序)可以更形象地称为内核加载程序。这个阶段的任务是加载 Linux 内核和可选的初始 RAM 磁盘。

GRUB 阶段引导加载程序

/boot/grub 目录中包含了 stage1、stage1.5  stage2 引导加载程序,以及很多其他加载程序(例如,CR-ROM 使用的是 iso9660_stage_1_5)。 在 x86 PC 环境中,第一阶段和第二阶段的引导加载程序一起称为 Linux Loader(LILO)或 GRand Unified Bootloader(GRUB)。由于 LILO 有一些缺点,而 GRUB 克服了这些缺点,因此下面让我们就来看一下 GRUB。(有关 GRUB、LILO 和相关主题的更多内容,请参阅本文后面的 参考资料 部分的内容。) 关于 GRUB,很好的一件事情是它包含了有关 Linux 文件系统的知识。GRUB 不像 LILO 一样使用裸扇区,而是可以从 ext2 或 ext3 文件系统中加载 Linux 内核。它是通过将两阶段的引导加载程序转换成三阶段的引导加载程序来实现这项功能的。阶段 1 (MBR)引导了一个阶段 1.5 的引导加载程序,它可以理解包含 Linux 内核映像的特殊文件系统。这方面的例子包括 reiserfs_stage1_5(要从 Reiser 日志文件系统上进行加载)或 e2fs_stage1_5(要从 ext2 或 ext3 文件系统上进行加载)。当阶段 1.5 的引导加载程序被加载并运行时,阶段 2 的引导加载程序就可以进行加载了。 当阶段 2 加载之后,GRUB 就可以在请求时显示可用内核列表(在 /etc/grub.conf 中进行定义,同时还有几个软符号链接/etc/grub/menu.lst  /etc/grub.conf)。我们可以选择内核甚至修改附加内核参数。另外,我们也可以使用一个命令行的 shell 对引导过程进行高级手工控制。 将第二阶段的引导加载程序加载到内存中之后,就可以对文件系统进行查询了,并将默认的内核映像和 initrd 映像加载到内存中。当这些映像文件准备好之后,阶段 2 的引导加载程序就可以调用内核映像了。 回页首 内核

GRUB 中的手工引导

在 GRUB 命令行中,我们可以使用 initrd 映像引导一个特定的内核,方法如下: grub> kernel /bzImage-2.6.14.2
[Linux-bzImage, setup=0x1400, size=0x29672e]
grub> initrd /initrd-2.6.14.2.img
[Linux-initrd @ 0x5f13000, 0xcc199 bytes]
grub> boot
Uncompressing Linux... Ok, booting the kernel.
如果您不知道要引导的内核的名称,只需使用斜线(/)然后按下 Tab 键即可。GRUB 会显示内核和 initrd 映像列表。 当内核映像被加载到内存中,并且阶段 2 的引导加载程序释放控制权之后,内核阶段就开始了。内核映像并不是一个可执行的内核,而是一个压缩过的内核映像。通常它是一个 zImage(压缩映像,小于 512KB)或一个 bzImage(较大的压缩映像,大于 512KB),它是提前使用 zlib 进行压缩过的。在这个内核映像前面是一个例程,它实现少量硬件设置,并对内核映像中包含的内核进行解压,然后将其放入高端内存中,如果有初始 RAM 磁盘映像,就会将它移动到内存中,并标明以后使用。然后该例程会调用内核,并开始启动内核引导的过程。 当 bzImage(用于 i386 映像)被调用时,我们从./arch/i386/boot/head.S  start 汇编例程开始执行(主要流程图请参看图 3)。这个例程会执行一些基本的硬件设置,并调用./arch/i386/boot/compressed/head.S 中的 startup_32 例程。此例程会设置一个基本的环境(堆栈等),并清除 Block Started by Symbol(BSS)。然后调用一个叫做 decompress_kernel 的 C 函数(在 ./arch/i386/boot/compressed/misc.c 中)来解压内核。当内核被解压到内存中之后,就可以调用它了。这是另外一个 startup_32 函数,但是这个函数在 ./arch/i386/kernel/head.S 中。 在这个新的 startup_32 函数(也称为清除程序或进程 0)中,会对页表进行初始化,并启用内存分页功能。然后会为任何可选的浮点单元(FPU)检测 CPU 的类型,并将其存储起来供以后使用。然后调用 start_kernel 函数(在 init/main.c 中),它会将您带入与体系结构无关的 Linux 内核部分。实际上,这就是 Linux 内核的 main 函数。
图 3. Linux 内核 i386 引导的主要函数流程 
Linux 内核 i386 引导的主要函数流程  
通过调用 start_kernel,会调用一系列初始化函数来设置中断,执行进一步的内存配置,并加载初始 RAM 磁盘。最后,要调用kernel_thread(在 arch/i386/kernel/process.c 中)来启动 init 函数,这是第一个用户空间进程(user-space process)。最后,启动空任务,现在调度器就可以接管控制权了(在调用 cpu_idle 之后)。通过启用中断,抢占式的调度器就可以周期性地接管控制权,从而提供多任务处理能力。 在内核引导过程中,初始 RAM 磁盘(initrd)是由阶段 2 引导加载程序加载到内存中的,它会被复制到 RAM 中并挂载到系统上。这个 initrd 会作为 RAM 中的临时根文件系统使用,并允许内核在没有挂载任何物理磁盘的情况下完整地实现引导。由于与外围设备进行交互所需要的模块可能是 initrd 的一部分,因此内核可以非常小,但是仍然需要支持大量可能的硬件配置。在内核引导之后,就可以正式装备根文件系统了(通过 pivot_root):此时会将 initrd 根文件系统卸载掉,并挂载真正的根文件系统。

decompress_kernel 输出

函数 decompress_kernel 就是显示我们通常看到的解压消息的地方: Uncompressing Linux... Ok, booting the kernel. initrd 函数让我们可以创建一个小型的 Linux 内核,其中包括作为可加载模块编译的驱动程序。这些可加载的模块为内核提供了访问磁盘和磁盘上的文件系统的方法,并为其他硬件提供了驱动程序。由于根文件系统是磁盘上的一个文件系统,因此 initrd 函数会提供一种启动方法来获得对磁盘的访问,并挂载真正的根文件系统。在一个没有硬盘的嵌入式环境中,initrd 可以是最终的根文件系统,或者也可以通过网络文件系统(NFS)来挂载最终的根文件系统。 回页首 Init 当内核被引导并进行初始化之后,内核就可以启动自己的第一个用户空间应用程序了。这是第一个调用的使用标准 C 库编译的程序。在此之前,还没有执行任何标准的 C 应用程序。 在桌面 Linux 系统上,第一个启动的程序通常是 /sbin/init。但是这不是一定的。很少有嵌入式系统会需要使用 init 所提供的丰富初始化功能(这是通过 /etc/inittab 进行配置的)。在很多情况下,我们可以调用一个简单的 shell 脚本来启动必需的嵌入式应用程序。 回页首 结束语 与 Linux 本身非常类似,Linux 的引导过程也非常灵活,可以支持众多的处理器和硬件平台。最初,加载引导加载程序提供了一种简单的方法,不用任何花架子就可以引导 Linux。LILO 引导加载程序对引导能力进行了扩充,但是它却缺少文件系统的感知能力。最新一代的引导加载程序,例如 GRUB,允许 Linux 从一些文件系统(从 Minix 到 Reise)上进行引导。
参考资料 学习
  • 您可以参阅本文在 developerWorks 全球站点上的 英文原文 

  • Boot Records Revealed 是有关 MBR 和各种引导加载程序很好的资源。这个资源不仅仅是有关 MBR 的资料的汇编,还讨论了 GRUB、LILO 和各种 Windows® 引导加载程序的问题。 

  • 请查看 Disk Geometry 页面来理解磁盘及其结构。您会发现有关磁盘的有用属性。 

  • live CD 是一个可以从 CD 或 DVD 上引导的操作系统,它不需要使用硬盘。 

  • “引导加载程序之争:了解 LILO 和 GRUB”(developerWorks,2005 年 8 月)详细介绍了 LILO 和 GRUB 引导加载程序。 

  • 在 developerWorks 上的 LPI 考试准备 系列教程中,我们可以学习有关引导 Linux 系统的详细介绍,以及在准备参加系统管理员认证考试时需要准备的 Linux 基础知识。 

  • LILO 是 GRUB 的先驱,但是我们可能发现它依然可以引导 Linux。 

  • mkintrd 命令用来创建初始的 RAM 磁盘映像。这个命令可以用来构建初始的根文件系统,它可以用来引导允许提前加载访问真正根文件系统所需要的块设备的配置。 

  •  Debian Linux Kernel Project 中,我们可以找到更多有关 Linux 内核、引导和嵌入式开发的信息。 

  •  developerWorks Linux 专区 中可以找到为 Linux 开发人员准备的更多资源。 

  • 随时关注 developerWorks 技术事件和网络广播。 

获得产品和技术