作者:王姗姗,华清远见嵌入式学院讲师。
Linux启动过程的剖析能帮助那些想深入学习Linux用户建立一个相关Linux启动过程的清晰概念,进而可以进一步研究Linux接下来是如何工作的。
Linux启动过程如下:当用户打开PC的电源,BIOS开机自检,按BIOS中设置的启动设备(通常是硬盘)启动,接着启动设备上安装的引导程序lilo或grub开始引导Linux,Linux首先进行内核的引导,接下来执行init程序,init程序调用了rc.sysinit和rc等程序,rc.sysinit和rc当完成系统初始化和运行服务的任务后,返回init;init启动了mingetty后,打开了终端供用户登录系统,用户登录成功后进入了Shell,这样就完成了从开机到登录的整个启动过程。如图所示:
下面分步来介绍下linux的启动过程:
1、BIOS自检
当你打开计算机电源,计算机会首先加载BIOS信息,BIOS信息是如此的重要,以至于计算机必须在最开始就找到它。这是因为BIOS中包含了CPU的相关信息、设备启动顺序信息、硬盘信息、内存信息、时钟信息、PnP特性等等。在此之后,计算机心里就有谱了,知道应该去读取哪个硬件设备了。
计算机在接通电源之后首先由BIOS进行自检,即进行所谓的POST(Power On Self Test),然后依据BIOS内设置的引导顺序从硬盘、软盘或CDROM中读入“引导块”。 在 PC 中,引导 Linux 是从 BIOS 中的地址 0xFFFF0 处开始的。BIOS 的第一个步骤是加电自检(POST)。POST 的工作是对硬件进行检测。BIOS 的第二个步骤是进行本地设备的枚举和初始化。给定 BIOS 功能的不同用法之后,BIOS 由两部分组成:POST 代码和运行时服务。当 POST 完成之后,它被从内存中清理了出来,但是 BIOS 运行时服务依然保留在内存中,目标操作系统可以使用这些服务。
要引导一个操作系统,BIOS 运行时会按照 CMOS 的设置定义的顺序来搜索处于活动状态并且可以引导的设备。引导设备可以是软盘、CD-ROM、硬盘上的某个分区、网络上的某个设备,甚至是 USB 闪存。通常,Linux 都是从硬盘上引导的,其中主引导记录(MBR)中包含主引导加载程序。
众所周知,硬盘上第0磁道第一个扇区被称为MBR,也就是Master Boot Record,即主引导记录,它的大小是512字节,别看地方不大,可里面却存放了预启动信息、分区表信息。系统找到BIOS所指定的硬盘的MBR后,就会将其复制到0×7c00地址所在的物理内存中。当 MBR 被加载到 RAM 中之后,BIOS 就会将控制权交给 MBR。其实被复制到物理内存的内容就是Boot Loader,而具体到你的电脑,那就是lilo或者grub了。
提取MBR的信息
要看MBR的内容,请使用下面的命令
#从/dev/sda上读取前512个字节的内容,并将其写入mbr.bin文件中
root@farsight:/home/linux# dd if=/dev/sda of=mbr.bin bs=512 count=1
1+0 records in
1+0 records out
512 bytes (512 B) copied, 0.000798615 seconds, 641 kB/s
#以十六进制和ASCII码格式打印这个二进制文件的内容
root@farsight:/home/linux# od -xa mbr.bin
0000000 48eb 1090 d08e 00bc b8b0 0000 d88e c08e
k H dle dle so P < nul 0 8 nul nul so X so @
0000020 befb 7c00 00bf b906 0200 a4f3 21ea 0006
{ > nul | ? nul ack 9 nul stx s $ j ! ack nul
0000040 be00 07be 0438 0b75 c683 8110 fefe 7507
nul > > bel 8 eot u vt etx F dle soh ~ ~ bel u
……
这个 dd 命令需要以 root 用户的身份运行,它从 /dev/hda(第一个 IDE 盘) 上读取前 512 个字节的内容,并将其写入 mbr.bin 文件中。od 命令会以十六进制和 ASCII 码格式打印这个二进制文件的内容。
2、启动GRUB/LILO
Boot Loader 就是在操作系统内核运行之前运行的一段小程序。通过这段小程序,我们可以初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核做好一切准备。
Boot Loader有若干种,其中Grub、Lilo和spfdisk是常见的Loader。
GRUB和LILO都是引导加载程序,它们会引导操作系统。当机器引导它的操作系统时,BIOS会读取引导介质上最前面的512字节(即MBR: master boot record)。在单一的 MBR 中只能存储一个操作系统的引导记录,所以当需要多个操作系统时就会出现问题。所以需要更灵活的引导加载程序。
GRUB 与 LILO 的比较
所有引导加载程序都以类似的方式工作,满足共同的目的。不过,LILO 和 GRUB 之间有很多不同之处:
● LILO 没有交互式命令界面,而 GRUB 拥有。
● LILO 不支持网络引导,而 GRUB 支持。
● LILO 将关于可以引导的操作系统位置的信息物理上存储在 MBR 中。如果修改了 LILO 配置文件,必须将 LILO 第一阶段引导加载程序重写到 MBR。相对于 GRUB,这是一个更为危险的选择,因为错误配置的 MBR 可能会让系统无法引导。使用 GRUB,如果配置文件配置错误,则只是默认转到 GRUB 命令行界面。
安全提示:
关于安全性,任何可以接触到引导磁盘/CD 的人,只需要使用没有设置安全性的 grub.conf 或 lilo.conf,就可以绕过本文中提及的所有安全措施。特别是使用 GRUB 时,因为能够引导到单用户模式,所以是一个严重的安全漏洞。解决此问题的一个简单方法是在机器的 BIOS 中禁止通过 CD 和软盘进行引导,并确保为 BIOS 设置了一个口令,使得其他人不能修改这些设置。
我们以Grub为例来讲解。系统读取内存中的grub配置信息(一般为menu.lst或grub.lst),并依照此配置信息来启动不同的操作系统。
3、加载内核
当内核映像被加载到内存后,内核阶段就开始了。 内核映像并不是一个可执行的内核,而是一个压缩过的内核映像。通常它是一个zImage(压缩映像,小于512KB)或bzImage(较大的压缩映像,大于512KB),它是提前使用zlib进行压缩的。在这个内核映像前面是一个例程,它实现少量硬件设置,并对内核映像中包含的内核进行解压,然后将其放入高端内存中,如果有初始RAM磁盘映像,就会将它移动到内存中,并标明以后使用。然后此例程会调用内核,并开始启动内核引导的过程。
在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.
根据grub设定的内核映像所在路径,系统读取内存映像,并进行解压缩操作。此时,屏幕一般会输出“Uncompressing Linux”的提示。当解压缩内核完成后,屏幕输出“OK, booting the kernel”。 如果不知道要引导的内核的名称,只需使用/然后按下Tab键,就会显示内核和initrd映像列表。
系统将解压后的内核放置在内存之中,并调用start_kernel()函数来启动一系列的初始化函数并初始化各种设备,完成Linux核心环境的建立。至此,Linux内核已经建立起来了,基于Linux的程序应该可以正常运行了。
4、执行init进程
init进程是所有进程的起点,内核在完成内核引导后,即在本线程(进程)空间内加载init程序,它的进程号为1。init进程是所有进程的发起者和控制者。因为在任何基于Unix的系统(比如Linux)中,它都是第一个运行的进程,所以init进程的编号(Process ID,PID)永远是1。如果init出现了问题,系统的其余部分也就随之而垮掉了。
init进程有两个作用:
● 扮演终结父进程的角色:所有的孤儿进程都会被init进程接管。快速执行一下ps -af 命令,可以列出许多父进程ID(Parent Process ID,PPID)为1的进程来。
● 进入某个特定的运行级别时运行相应的程序,以此对各种运行级别进行管理,这个作用由/etc/inittab文件定义的。 内核被加载后,第一个运行的程序便是/sbin/init,该文件会读取/etc/inittab文件,并依据此文件来进行初始化工作。
5、通过/etc/inittab文件进行初始化
init进程的工作是根据/etc/inittab来执行相应的脚本进行系统初始化,如设置键盘、字体,装载模块,设置网络等。其最主要的作用就是设定Linux的运行等级,其设定形式是“:id:5:initdefault:”,这就表明Linux需要运行在等级5上。Linux的运行等级设定如下:
0:关机
1:单用户模式
2:无网络支持的多用户模式
3:有网络支持的多用户模式
4:保留,未使用
5:有网络支持有X-Window支持的多用户模式
6:重新引导系统,即重启
对于RedHat来说,按以下顺序执行:
a、执行/etc/rc.d/rc.sysinit(由init执行的第一个脚本)
在设定了运行等级后,Linux系统执行的第一个用户层文件就是/etc/rc.d/rc.sysinit脚本程序,它做的工作非常多,包括设定PATH、设定网络配置(/etc/sysconfig/network)、启动swap分区、设定/proc、把root文件系统输入到mtab、使用系统为装入模块做准备、查找模块的相关文件、检查文件系统,以进行必要的修复、加载所有其他文件系统、清除几个/etc文件,如/etc/mtab、/etc/fastboot和/etc/nologin、删除UUCP的lock文件、删除过时的子系统文件、删除过时的pid文件、设置系统时钟、打开交换、初始化串行端口、装入模块等等。如果你有兴趣,可以到/etc/rc.d中查看一下rc.sysinit文件,里面的脚本够你看几天的。
b、执行/etc/rc.d/rcX.d[KS]
首先终止K开头的服务(用来关闭一个服务),然后启动S开头的服务(用来启动一个服务);对每一个运行级别来说,在/etc/rc.d子目录中都有一个对应的下级目录。这些运行级别的下级子目录的命名方法上rcX.d, 其中X就是代表运行级别的数字。在各个运行级别的子目录中,都建立有到/etc/rc.d/init.d子目录中命令脚本程序的符号链接。链接的名称在K与S后有一个数字,表示执行顺序,数字小的先执行如K01tog-pegasus 、 S00microcode_ctl。对以K开头的脚本执行时系统会传递stop参数,而S开头的脚本系统会传递start参数。
c、执行/etc/rc.d/rc.local
Redhat中运行模式2,3,5都把/etc/rc.d/rc.local作为初始化脚本中的最后一个文件,所以用户可以自己在这个文件中添加一些需要在其他初始化工作之后,登陆之前执行的命令。
你如果打开了此文件,里面有一句话,读过之后,你就会对此命令的作用一目了然:
# 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.
rc.local就是在一切初始化工作后,Linux留给用户进行个性化的地方。你可以把你想设置和启动的东西放到这里。
6、执行/bin/login
login程序会提示使用者需输入帐号与密码,接着编码并确认密码的正确性,若二者相合,则为使用者进行初始化环境,并将控制权交给shell,即用户登录。