计算机在加电的那一刻几乎是毫无用处的,因为RAM芯片中包含的是随机数据,此时还没有操作系统在运行。在开始启动时,有一个特殊的硬件电路在CPU的一个引脚上产生一个reset逻辑值。在reset产生之后,就把处理器的一些寄存器设置成固定的值,并执行在物理地址0Xfffffff0处找到的代码。硬件把这个地址映射到某个只读,持久的存储芯片中,该芯片通常称为ROM,ROM中所存放的程序集在80x86体系中通常叫作基本输入输出系统(BASIC INPUT/OUTPUT SYSTEM,BIOS),因为它包含几个中断驱动的低级过程。所有操作系统在启动时,都要通过这些过程对计算机硬件设备初始化。一些操作系统,如微软的MS—DOS,依赖于,BIOS实现大部分系统调用。
linux一旦进入保护模式,就不再使用BIOS,而是为计算机上的每个硬件设备提供各自的设备驱动程序。实际上,因为BIOS过程必须在实模式下运行,所以即使有益,两者之间也不能共享函数.
BIOS使用实模式地址,因为在计算机加电启动时只有这些可以使用。一个实模式的地址由一个seg段和一个off偏移量组成。相应的物理地址可以这样计算:seg*16+off.所以CPU寻址电路根本就不需要全局描述符表,局部描述符表或者页表把逻辑地址转换成物理地址。显然对GDT,LDT和页表进行初始化的代码在实模式下运行。
linux在启动阶段必须使用BIOS,此时LINUX必须要从磁盘或者其他外部设备中获取内核映像。BIOS启动过程实际上执行一下4个操作:
1.
类似于任何其他程序,内核在执行通常的任务之前,会经历一个加载和初始化的阶段。尽管普通应用程序对该阶段不是特别感兴趣,但内核作为中枢的系统层,必须解决若干特定的问题。启动阶段划分为以下3个部分。
内核载入物理内存,并创建最小的运行时环境。
转移到内核中的机器码,并初始化基本的系统功能,初始化代码是特定于系统的,用汇编语言编写。
转移到初始化代码中平台无关的部分,用C语言编写,并完成所有子系统的初始化,最后切换到正常运作模式。
通常,由启动装载程序负责第一阶段。其任务很大程度上依赖于具体的体系结构所要求完成的工作。因为只有深入了解特定处理器的特性和问题,才能理解第一阶段的所有细节,特定与体系结构的参考手册是一个很好的信息来源。第二阶段与硬件的相关度也很高。
第三阶段,即体系无关的初始化阶段,内核已经载入内存,而处理器已经由启动模式切换为执行模式,内核接下来将开始运行。
在使用启动装载器(如LILO,GRUB等)将内核载入物理内存之后,将通过跳转语句,将控制流切换到内存中适当的位置,来调用arch/x86/boot/header.S中的汇编语言setup。这是可能的,因为setup函数总是位于目标文件中的同一位置。该代码执行下列任务,这需要许多汇编代码。
它检查内核是否加载到内存中正确的位置。为此,它使用一个4字节的特征标记,该标记在编译时集成到内核映像中,并且总是位于物理内存中一个不变的正确位置。
它确定系统内存的大小。
初始化显卡。
将内核映像移动到内存中的某个位置,使得在后续的解压缩期间,映像不会自阻其路。
将CPU切换到保护模式。
在完成这些任务后,代码分支到startup_32函数。该函数将执行下列任务。
创建一个临时内核栈。
用0字节填充未初始化的内核数据。相关的区域位于edata和end常数之间。在内核链接时,会根据内核二进制文件,自动对这些常数设置正确的值。调用arch/x86/boot/compressed/misc_32.c中的c语言例程decompress_kernel.该函数将解压缩内核,并将未压缩的机器码写入从0x10000000开始的内存区,这刚好紧接着内存的第一个MiB。解压缩是内核执行的第一个操作。
现在,开始特定于处理器的初始化过程的最后一部分工作,首先要将控制流重定向到arch/x86/kernel/head_32.S中的startup_32.
这部分代码负责执行下列任务。
激活分页模式,设置一个最终的内核栈。
用0字节填充_bss_start和_bss_stop之间的.bss段。
初始化中断描述符表。但所有的处理程序都设置为igonre_ini空例程,实际的处理程序在之后安装。
检测处理器类型。
平台相关的初始化现在已经完成,代码将分支到start_kernel函数。
阅读(1473) | 评论(0) | 转发(0) |