操作系统的启动过程分析(以Linux系统为例)


购置一台电脑,我们要做Linux系统学习,我们首先安装好了一个Centos6.5系统。从摁下电源键的那一刻到出现桌面或者是字符界面。这期间发生了什么呢?

第一阶段:

为搞清楚这个,从硬件到软件,首先看硬件,看下计算机主板的组成,南北桥:

\

北桥连接CPU、内存、PCI等高速设备。南桥连接磁盘、网卡、USB、PS/2等低速设备。

我们摁下电源的一刻时,首先读取BIOS芯片内数据。这个BIOS芯片一般是EEROM,但是从Intel奔腾系列开始,主板都采用NorFlash作为BIOS芯片,位于主板的在南桥,其位置一般在主板纽扣电池周围,下面看下一个主板上BIOS芯片的位置:

\

BIOS芯片主要用来干什么的呢?首先BIOS芯片是一个存储芯片,主要存储以下程序:

1、系统参数设置程序。微机部件配置情况是放在一块可读写的CMOS RAM芯片中的,它保存着系统CPU、软硬盘驱动器、显示器、键盘等部件的信息。关机后,系统通过一块后备电池向CMOS供电以保持其中的信息。如果CMOS中关于微机的配置信息不正确,会导致系统性能降低、零部件不能识别,并由此引发一系列的软硬件故障。BIOS芯片中的“系统设置程序”,就是用来设置CMOS RAM中的参数的。这个程序一般在开机时按下一个或一组键即可进入,它提供了良好的界面供用户使用。这个设置 CMOS参数的过程,习惯上也称为“BIOS设置”。新购的微机或新增了部件的系统,都需进行BIOS设置。

2、系统上加电自检程序(POST)。微机接通电源后,系统将有一个对内部各个设备进行检查的过程,这是由一个通常称之为POST(Power On Self Test,上电自检)的程序来完成的。这也是BIOS的一个功能。完整的POST自检将包括CPU、内存、ROM、主板、 CMOS存贮器、串并口、显示卡、软硬盘子系统及键盘测试。自检中若发现问题,系统将给出提示信息或鸣笛警告,例如,最常见的是内存松动的情况,BIOS自检阶段会报错,系统就无法启动起来。

3、系统启动自举程序。在自检成功后将磁盘的MBR上的引导程序装入内存,让其运行以装入操作系统引导程序(grub/lilo、NTLDR);

4、主要的I/O驱动程序以及中断处理程序。BIOS中中断例程即BIOS中断服务程序。它是微机系统软、硬件之间的一个可编程接口,用于程序软件功能与微机硬件实现的衔接。DOS/Windows操作系统对软盘、硬盘、光驱与键盘、显示器等外围设备的管理即建立在系统BIOS的基础上。程序员也可以通过 对INT 5、INT 13等中断的访问直接调用BIOS中断例程。

到此操作系统启动的第一个阶段就是读取BIOS芯片内程序,并执行,主要是POST->读取MBR(主引导记录)上的内容到内存,下面我们看下MBR在磁盘中的具体位置:

\

由上面的图中可以看出,MBR位于硬盘的0柱面,0磁头,1扇区。它由三个部分组成,分别是:主引导程序、磁盘分区表(Disk Partition table)和分区有效标志。在总共512字节的MBR里主引导程序(boot loader)占446个字节,第二部分DPT,占64个字节,硬盘中分区有多少以及每一分区的大小都记在其中。第三部分是magic number,占2个字节,固定为0xAA55或0x55AA,这取决于处理器类型,如果是小端模式处理器(如Intel系列),则该值为0xAA55;如果是大端模式处理器(如Motorola6800),则该值为0x55AA。(ps:这里需要注意的一点是,由于MBR分区表的最大可寻址的存储空间只有2Tb(232×512字节)。因此,在大硬盘出现的现在,MBR分区方式逐渐被GUID分区表取代)

MBR中的Bootloader程序读入到内存就完成了操作系统启动的第一个阶段。接下来进入第二阶段Bootloader。

通过这段小程序,我们可以初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核做好一切准备。

Linux下的Bootloader主要有grub或者Lilo,大多数Linux操作系统主要是grub。这里我们以grub为主。

操作系统引导程序进入内存后,从0x7c00处开始行,先将磁盘0头0道2扇区(stage1)读入内存,这512字节的程序是后面执行程序的入口。具体是将硬盘0头0道2扇区内容载入内存0x7000处,然后调用copy_buffer将其转移到内存0x8000处。在定位0头0道2扇区时通常有两种寻址方式:LBA和CHS。

这段代码是LBA扇区号2(0柱面,0磁道,3扇区)开始拷贝若干个扇区到内存,我们可以发现/boot/grub目录下有个许多*_stage_1_5的文件,其中我们关注的是e2fs_stage1_5这个文件,这个文件的作用是识别ext3文件系统的。我们知道GRUB开始没有OS,也没有文件系统的概念。那么GRUB是从何时开始有文件系统的功能的呢。这就是stage1.5干的事情,stage1.5过后,GRUB就能识别文件系统了,就能在磁盘上识别加载文件了。这样通过对应的stage1.5文件,就可以正确加载stage2文件。为什么会有stage1.5这个阶段呢?主要是当stage2不连续或者需要在stage2前,对文件系统做些特殊处理。如果没有这样的需求,完全可以避免stage1.5。

stage2文件为最主要的加载代码,这时由于已经stage1.5已经支持文件系统了,所以stage2可以比较大,能让用户以菜单方式将操作系统加载、新增参数、修改选项。stage2被载入内存执行时,它首先会去解析grub的配置文件/boot/grub/grub.conf,然后加载内核镜像到内存中,并将控制权转交给内核。而内核会立即初始化系统中各设备并做相关的配置工作,其中包括CPU、I/O、存储设备等。

以上就完成了系统启动的第二个阶段,接下来进入第三阶段。

我们看到grub.conf文件中指定了内核镜像的位置,在/boot目录下会看到两个有关内核镜像的文件,一个是以initramfs开头的文件,一个是以vmlinuz开头的文件。为何有这两个文件呢?因为Linux的设备驱动程序的加载,有一部分驱动程序直接被编译进内核镜像中,另一部分驱动程序则是以模块的形式放在了ramdisk中。

首先将内核镜像vmlinuz拷贝内存进行解压缩,系统将解压后的内核放置在内存之中,并调用start_kernel()函数来启动一系列的初始化函数并初始化各种设备,完成Linux核心环境的建立。

\

然后到这步完成了Linux内核的启动。

接下来进入第四步,初始化内核。

我们如果解压缩initramfs文件,解压缩此文件就会发现此镜像内含有一个init脚本。我们完成Linux内核的启动后,然后将initramfs镜像加载到内存中,将其中的内容释放到内存中,内核便去执行initramfs中的init脚本,这时内核将控制权交给了init文件处理。如果查看init脚本的内容,发现它也主要是加载各种存储介质相关的设备驱动程序。当所需的驱动程序加载完后,会创建一个根设备,然后将根文件系统rootfs以只读的方式挂载。这一步结束后,释放未使用的内存,转换到真正的根文件系统上面去,同时运行/sbin/init程序,执行系统的1号进程。此后系统的控制权就全权交给/sbin/init进程了。

\

然后执行第五步,进行系统的初始化。

接第四步运行/sbin/init程序后,该程序首先会读取/etc/inittab文件,并依据此文件来进行初始化工作。
其实/etc/inittab文件最主要的作用就是设定Linux的运行等级,其设定形式是“:id:5:initdefault:”,这就表明Linux需要运行在等级5上。Linux的运行等级设定如下:
0:关机
1:单用户模式
2:无网络支持的多用户模式
3:有网络支持的多用户模式
4:保留,未使用
5:有网络支持有X-Window支持的多用户模式
6:重新引导系统,即重启

在设定了运行等级后,然后就是初始化系统。

第一个是执行系统初始化脚本(/etc/rc.d/rc.sysinit),对系统进行基本的配置,主要包括设定PATH、设定网络配置(/etc/sysconfig/network)、启动swap分区、设定/proc等等。内容超多,这里就不说了。

第二个是启动内核模块,具体是依据/etc/modules.conf文件或/etc/modules.d目录下的文件来装载内核模块。

第三个是 执行/etc/rc.d/rc脚本。该文件定义了服务启动的顺序是先K后S,而具体的每个运行级别的服务状态是放在/etc/rc.d/rc*.d(*=0~6)目录下,所有的文件均是指向/etc/init.d下相应文件的符号链接。rc.sysinit通过分析/etc/inittab文件来确定系统的启动级别,然后才去执行/etc/rc.d/rc*.d下的文件。比如我们设置运行级别为3,则执行rc3.d目录下的内容,S表示的是启动时需要start的服务内容,K表示关机时需要关闭的服务内容。/etc/rc.d/rc*.d中的系统服务会在系统后台启动,如果要对某个运行级别中的服务进行更具体的定制,通过chkconfig命令来操作,或者通过setup、ntsys、system-config-services来进行定制。如果我们需要自己增加启动的内容,可以在init.d目录中增加相关的shell脚本,然后在rc*.d目录中建立链接文件指向该shell脚本。这些shell脚本的启动或结束顺序是由S或K字母后面的数字决定,数字越小的脚本越先执行。例如,/etc/rc.d/rc3.d /S01sysstat就比/etc/rc.d/rc3.d /S99local先执行。

第四个是执行用户自定义引导程序/etc/rc.d/rc.local。其实当执行/etc/rc.d/rc3.d/S99local时,它就是在执行/etc/rc.d/rc.local。S99local是指向rc.local的符号链接。就是一般来说,自定义的程序不需要执行上面所说的繁琐的建立shell增加链接文件的步骤,只需要将命令放在rc.local里面就可以了,这个shell脚本就是保留给用户自定义启动内容的。

第五个是建立终端登录

上面rc初始化结束后,init进程接下来打开终端以便用户登录。

tty1,tty2,tty3...这表示在运行等级1,2,3,4的时候,都会执行"/sbin/mingetty",而且执行了6个,所以linux会有6个纯文本终端,mingetty就是启动终端的命令。

除了这6个之外还会执行"/etc/X11/prefdm-nodaemon"这个主要启动X-Window,图形化登录界面。

最后一步:

上面初始化完成,打开终端提示用户输入用户名,getty程序得到用户名后,启动/bin/login程序并将用户名参数传递给此程序,此程序提示用户输入密码,用户输入密码后,/bin/login程序对用户名与密码进行检查,这里检查的时候是匹配存储用户帐号的文件为/etc/passwd与存储用户名密码的文件为/etc/shadow,其中存储用户帐号的文件中含有用户对应的shell,/bin/login程序会启动此shell进程,然后启动/etc/profile与$Home/.bashrc等设置的环境变量,如果是字符界面登录,会出现一个$符号等待用户输入命令。

至此操作系统启动完成。可以按照下面的图进行理解:

\

这里需要注意的一点是Linux的帐号验证程序时login之前,为了防止root根目录被任意用户访问,会执行一个chroot程序(change root directory),改变根目录的路径。经过chroot后,新根下无法访问旧根目录结构和文件,建立了一个与原来根相隔离的系统目录结构,方便用户开发,更加提高了系统的安全性。如果一个系统遭到了破坏,我们可以利用chroot切换到另一个系统,从这一点来看chroot起到的作用有点沙箱的味道。

(ps:沙箱,是为一些来源不可信、具备破坏力或无法判定程序意图的程序提供试验的环境。然而,沙盒中的所有改动对操作系统不会造成任何损失。通常,这种技术被计算机技术人员广泛使用,尤其是计算机杀毒软件行业,沙盒是一个观察计算机病毒的重要环境。)

chroot完毕后,login然后执行后续的过程。

相关内容