pre { font-family: "Times New Roman"; }p { margin-bottom: 0.08in; }
Linux Boot Process 分析与研究 作者: 钱国正 (淮阴师范学院) 对于linux的研究正在紧锣密鼓的进行着,源于对系统启动的兴趣,开始对其启动过程全面的分析,建立在以前玩过Linux的基础上,知道Linux启动时有GRUB可以选择想要启动的系统,开始进行启动深入剖析。大家不要担心,即使您对计算机的启动过程一无所知也可以看下面的文章。 当我们按下开机电源按钮的时候,系统就在启动的过程中了,相信使用过Windows的人或多或少的都对其启动速度有些意见,但是对它的启动过程却一无所知,这就让人很郁闷,呵呵,不急,下面进入正文。所有的计算机开机时都会有BIOS(Basic Input and Output System)对计算机进行自检,如内存测试等,并对本地设备进行枚举和初始化工作,接下来搜索BIOS中设置的处于活动状态并且可以引导的启动设备。通常情况下,Linux系统是从硬盘(也可以从CD,DVD,USB等)引导的,硬盘的引导扇区在每个分区中都存在,但是主引导扇区只有一个并位于第一个物理扇区。主引导扇区包含主引导记录(MBR)和硬盘分区表(DPT)。硬盘的主引导扇区中包含了主引导记录(MBR)的引导加载程序。此时BIOS将控制权交给MBR。Windows操作系统在这一步与Linux相同。 主引导扇区大小为512B,包含MBR程序代码,分区表DPT(64KB)以及结束标志(AA55)。MBR中包含了关于GRUB(或者早期的LILO)信息。MBR加载并执行GRUB(Grand Unified Bootloader) boot loader。下面的事情就由GRUB boot loader来控制执行了。 在Windows中MBR直接引导Windows操作系统,如果有两个版本的Windows系统,要先安装低版本的,否则只能进入一个操作系统!简单的说就是,Windows只认自己人,别的都不认,足见其霸道之处,所以如果要安装双系统如Windows和Linux要先安装Windows,然后安装Linux系统。现在回到GRUB,GRUB加载后基本上就完成了大部分工作了。BIOS与MBR都是硬件上的东东,咱不太熟悉,到了GRUB咱就可以对其代码进行分析了,由于篇幅限制,这里就不赘述了,其源代码大家可以到www.sourceforge.org下载。GRUB的工作是显示一个启动画面,如果你不输入或者选择,它将启动默认选择的操作系统(Ubuntu中如果想修改启动选项可以修改/boot/grub/grub.cfg,慎用!!!),其还可以识别文件系统,下面'Ubuntu, with Linux 2.6.32-27-generic'是系统启动所显示的默认选项 ### BEGIN /etc/grub.d/10_linux ### menuentry 'Ubuntu, with Linux 2.6.32-27-generic' --class ubuntu --class gnu-linux --class gnu --class os { recordfail insmod ext2 set root='(hd0,10)' search --no-floppy --fs-uuid --set e36d89d9-ad79-467d-8acf-fadef717c6b7 linux /boot/vmlinuz-2.6.32-27-generic root=UUID=e36d89d9-ad79-467d-8acf-fadef717c6b7 ro splash vga=758 quiet splash initrd /boot/initrd.img-2.6.32-27-generic } 。由于咱侧重的是Linux的启动过程,下面介绍下Linux下的一个被成组自举引导的程序(bootsect-loader),在Linux源码树中的arch/i386/boot/bootsect.S,其为CPU实模式下的汇编程序,为了方便分析,对其源码进行简单介绍。 SETUPSECTS=4 //安装时的缺省值 BOOTSEG =0x07C0 //引导时的原始地址 INITSEC =DEF_INITSEG //移动引导程序 SETUPSEG =DEF_SETUPSEG //从这里开始安装 SYSSEG =DEF_SYSSEG //系统装载 SYSSIZE =DEF_SYSSIZE //系统大小 在include/asm-i386/boot.h中的宏定义 #define DEF_INITSEG 0x9000 #define DEF_SYSSEG 0x1000 #define DEF_SETUPSEG 0x9020 #define DEF_SYSSIZE 0x7F00 以下这段初学者可以略过(摘自《Linux系统分析与实践》)! bootsect.S在内核编译时生成bootsect模块,在整个内核镜像文件的最前面。当计算机启动时先进入实模式,首先执行位于0xffff0这个地址的程序代码,这个地址实际上是映射到计算机ROM BIOS地址,也就是说首先执行BIOS的代码,执行某些检测,在地址0x0000位置设置中断向量表,并把第一个扇区读入到内存绝对地址0x7c00这个位置。bootsect的代码就位于第一扇区,所以跳到这个位置执行。bootsect将自己移动到0x90000,并把setup模块搬运到0x90200这个位置,bootsect执行完后,执行setup模块。在setup执行过程中,完成必要设置,并把system搬移到0x10000这个位置。但system前面的head部分执行完毕后,然后把system搬运到0x0000的地址以后,system模块中代码地址就等于物理地址,这样便于对内核操作,之所以不在开始的时候直接把system模块搬运到0x0000的位置是因为,当setup模块没有执行完毕的时候,0x0000这个位置还存放这BIOS的信息和一些中断向量表。如果开始就搬运就会把这些内容覆盖掉而出错。 setup.S的主要功能就是将系统参数(包括内存、磁盘等,由BIOS返回)复制到0x90000~0x901FF内存中。这个位置原本是bootsect.S被存放的位置,这时它将被系统参数覆盖。以后这些参数将由保护模式下的代码来读取。除此之外,setup.S还将video.S中的代码包含进来,检测和设置显示器和显示模式。最后,setup.S将系统转换到保护模式,并跳转到0x100000的内核引导代码,Bootloader将控制权移交系统内核。 Kernel负责加载根文件系统,执行/sbin/init中的程序,以及一些驱动程序。其由head.S开始执行初始地址为0x100000,主要完成一些初始化工作,清除BSS,复制启动参数,初始化寄存器,初始化GDT和IDT等,然后执行start_kernel()。 Init为Linux的核心线程,完成外设的初始化工作,包括SMP(),以及调用do_basic_setup()完成外设及驱动的加在和初始化。 至此,Linux已经启动完毕,虽然写了这么多,但是其速度快的惊人阿,只有不到30秒的时间,哈哈,快的可以达到10多秒,比Windows可好多啦。