探本溯源.docx

上传人:b****8 文档编号:10452575 上传时间:2023-02-11 格式:DOCX 页数:8 大小:18.85KB
下载 相关 举报
探本溯源.docx_第1页
第1页 / 共8页
探本溯源.docx_第2页
第2页 / 共8页
探本溯源.docx_第3页
第3页 / 共8页
探本溯源.docx_第4页
第4页 / 共8页
探本溯源.docx_第5页
第5页 / 共8页
点击查看更多>>
下载资源
资源描述

探本溯源.docx

《探本溯源.docx》由会员分享,可在线阅读,更多相关《探本溯源.docx(8页珍藏版)》请在冰豆网上搜索。

探本溯源.docx

探本溯源

探本溯源

在前文结尾处我们提到内核映像的加载是由专用的bootloader比如LILO或是GRUB来实现的,而在x86架构下Linux内核通常使用其中之一的GRUB,它通过执行initrd文件来识别内核映像所在的文件系统进而执行加载,然而有一个需要注意的问题是,并非所有的物理地址空间对内核而言都是可用的,比如其中的某个物理地址范围可能被映射为I/O设备的共享内存,也可能其中的一个物理页框存放着BIOS数据,综合上述原因,GRUB必须建立一个物理地址映射来将内核加载至可用的物理内存中,而建立映射的这一过程则是根据协议完成的,不仅如此,实模式下的内核代码能够占用的内存空间,以及初始化过程中用来建立堆栈的物理内存大小都需要遵守相关的启动协议,有关LINUX/x86启动协议的详细信息可参考Documentation\x86目录下的boot.txt文件,该文件还详细解释了header.S中所定义的全局变量hdr的各个字段的含义,而其重要性单从源文件中各个字段后的注释规模便可见一斑。

物理内存布局

在深入剖析源代码之前,我们有必要首先明确启动阶段物理内存的分布情况,因为只有明白了执行过程背后所依据的一些基本事实才能对源代码有更为深刻的认识,而其中的一些事实源于软硬件的高速发展,另外一些则是为了满足兼容性的要求。

总的来说,内核映像由三部分构成,它们分别是:

一个512字节的启动扇区——Kernelbootsector,也即在前文中所剖析的Linux内核自带的bootloader,但由于现在使用的bootloader为GRUB,为了避免产生歧义,所以冠以另外一个名称。

该bootsector占用操作系统映像所在逻辑分区的第一个扇区,注意从bootsect的偏移0x1f1处开始存放hdr变量。

实模式下的内核安装部分——Kernelsetup,连续占用若干个扇区大小的内存空间,主要用于检测硬件环境并执行一系列的初始化,为保护模式下内核代码的运行完成一些前期的准备工作。

保护模式下的内核代码,GRUB将其加载至从地址0x100000开始的物理内存中。

这里要注意的是实模式下的Kernelbootsector和Kernelsetup虽然从逻辑上被分成两部分,但它们的分布是连续的,并且这两部分都处于第一个1MB的物理内存空间中,而其上所提到的保护模式下的内核代码的初始地址为0x100000,表示这部分代码从物理内存的第二个1MB开始安装,因此内核映像的实模式及保护模式这两个部分的分界线即物理地址0x100000。

这里要特别注意的一点是,GRUB不可能单独在实模式下完成上述加载任务,因为在实模式下CPU只能寻址第一个1MB范围内的物理内存空间(原因请参见处理器体系结构及寻址模式一文)。

有意思的是,在GRUB的次引导过程执行时将会临时性地切换到保护模式,完成内核映像的加载后再次回到实模式。

因此虽然引导加载过程有两种模式的来回切换操作,但对于内核映像来说却是透明的,当控制权转交给内核后,它仍然首先从实模式下开始运行,但此时并不意味着超出第一个1MB范围的物理内存中没有任何内容,只是这些内容占用的内存空间无法被内核当前执行的指令所寻址,这使得保护模式下的内核代码不会被随意修改,也算是对保护模式中“保护”一词的另类诠释。

以下是GRUB将内核映像加载至内存空间后的分布图:

图1上图同样位于Documentation\x86目录下的boot.txt中。

在该文件中还展示了由zImage内核映像所使用的传统内存映射模型,但我们并不过多关注这类历史遗留问题,在后续的代码剖析过程中均以现阶段所使用的一些模型作为基准。

在前文结尾处曾提到过BIOS例程将内核放入低地址0x00010000(小内核映像zImage)或者从高地址0x0010

0000(大内核映像bzImage)开始的RAM中,而现阶段所使用的Linux内核均编译为bzImage,因此保护模式下的内核代码被安装在从高地址0x00100000开始的RAM中。

正如上图所示,我们也可以看到在完成POST以及一系列的初始化工作后,BIOS将bootloader加载至物理地址00007c00处。

而令人困惑的是,内核的启动扇区bootsect的起始地址并未被严格限制,这其实是由于Linux内核允许使用多种bootloader所导致的,比如前文所提到的LILO以及GRUB,在现实情况中存在更多不同类型的bootloader,不同的bootloader可能将实模式的起始地址加载至不同的位置,然而在x86架构下的GRUB设置的起始地址正是0x9

0000。

另外我们也可以看到实模式下的内核代码以及该代码所创建的堆栈的大小至多为8KB,之所以需要创建堆栈是为了提供C语言的运行环境,因为代码的执行过程需要使用栈来保存局部变量,函数调用则需要借助栈来传递参数以及保存返回地址,其中还有可能涉及到动态内存的分配,以及将内存分配给内核命令行。

这样一来,实模式下的内核代码需要使用的内存空间将会达到8KB*2=16KB,而该段的起始地址为0x9

0000,因此这段内存空间的结尾处的地址至多将会达到0xA0000。

而这是不允许,因为现代机器中的许多BIOS例程需要使用从起始地址0x9A000开始的额外内存空间,该内存空间即扩展的BIOS数据区(ExtendedBIOSDataArea,EBDA)。

若内核被GRUB安装至较高的内存空间,那么执行代码将被BIOS修改。

事实上实模式下的Linux内核正是基于这一原则所设计的,我们将在后文的源代码剖析过程中看到其具体的实现细节。

我们在前文提到过,代码背后所依据的某些事实是为了满足历史遗留问题所提出的兼容性要求,这一点可从上图中物理地址范围0xA0000~0x100000的内存占用情况得知。

追溯至上世纪80年代,当时IBM所推出的第一台PC机可供寻址的物理内存总共为1MB。

而这1MB中的低640KB供DOS以及应用程序使用,而高端的384KB则被留作它用,其中低端的640KB被称为常规内存,高端的384KB则被称为保留内存,这两种不同的内存类型通过物理地址0xA

0000得以分隔,此后这个分界线便被确定下来并沿用至今。

简单总结一下,在x86体系结构中,RAM的第一个1MB内存空间包含如下两个“独特”的地方:

物理地址0x0000~0x1000以及0x9A000~0xA0000所占内存由BIOS使用,存放加电自检(Power-OnSelf-Test,POST)期间检查到的系统硬件配置。

有些类型的BIOS甚至在系统初始化之后依然将数据写入该内存。

从0xA0000~0x100000范围内的物理内存通常保存BIOS例程,并且映射ISA图形卡上的内部内存。

这个区域就是IBM兼容PC上从640KB到1MB之间的著名的洞——图1中的I/Omemoryhole:

物理地址存在但被保留,不能由操作系统使用。

内核将上述地址范围的物理内存所占页框标记为保留,它们连同内核代码以及已初始化或未初始化的内核数据一起,在整个机器的执行周期中常驻内存,而绝不能被动态分配或由内核调度程序交换到磁盘上。

图2上图形象地显示了常驻物理内存中各个区域的使用情况,需要注意的一点是保护模式下的内核代码所占页框的总数依赖于对应的配置方案,因此上图中与之对应的区域只是用符号简单的加以表示。

而实模式下的内核代码仅在启动期间执行硬件检测及一系列初始化操作之后便不再被使用,因此当实模式下的内核代码跳转到保护模式之后,这部分内存空间即可由内核代码用于其他用途。

实模式下内核的初始化变量——安装头(setupheader)

该安装头为从Kernelbootsector中偏移0x1f1处开始的hdr变量,主要存放初始化期间将会使用到的一些数据。

我将把该变量中各个字段的含义集中罗列在这里,在后文中讲到内核执行初始化过程中使用到这些数据时不再单独详细描述。

另外要注意的是有些字段的存在同样属于历史遗留性问题,对于这些内容我们直接一带而过。

下表是这些字段的概要说明:

偏移量/大小

字段名

含义

 

0x1F1/1

setup_sects

表示Kernelsetup中的代码所占用的物理存储空间

 

0x1F2/2

root_flags如果被设置,则根以可读形式挂载

 

0x1F4/4

syssize32位代码的大小,该值以16字节的段为单位

 

0x1F8/2

ram_size不再使用——仅由bootsect.S文件使用(已被废弃)

 

0x1FA/2

vid_mode视频模式控制

 

0x1FC/2

root_dev默认根设备号

 

0x1FE/2

boot_flag魔数,其值为常量"0xAA55",标识有效引导记录结尾的标签

 

0x200/2

jump跳转指令

 

0x202/4

header魔数标签,其值为"HdrS"

 

0x206/2

version启动协议版本支持

 

0x208/4

realmode_swtch引导加载程序钩子

 

0x20C/2

start_sys_seg加载低地址段(已被废弃)

 

0x20E/2

kernel_version指向内核版本字符串的指针

 

0x210/1

type_of_loader引导加载程序标识符

 

0x211/1

loadflags引导协议选项标志

 

0x212/2

setup_move_size移动至高位内存所需的大小(已被废弃)

 

0x214/4

code32_start引导加载程序钩子

 

0x218/4

ramdisk_imageinitrd加载地址

 

0x21C/4

ramdisk_sizeinitrd的尺寸

 

0x220/4

bootsect_kludge不再使用——仅由bootsect.S使用(已被废弃)

 

0x224/2

heap_end_ptr在setup尾部之后的空闲内存

 

0x226/1

ext_loader_ver扩展的引导加载程序版本

 

0x227/1

ext_loader_type扩展的引导加载的ID

 

0x228/4

cmd_line_ptr指向内核命令行的32位指针

 

0x22C/4

ramdisk_maxinitrd可用的最高地址

 

0x230/4

kernel_alignment对内核要求的物理地址对齐

 

0x234/1

relocatable_kernel内核是否可重定位

 

0x235/1

min_alignment最小对齐,其值要求为2的平方

 

0x236/2

pad3不再使用(已被废弃)

 

0x238/4

cmdline_size内核命令行的最大尺寸

 

0x23C/4

hardware_subarch硬件子架构

 

0x240/8

hardware_subarch_data特定子架构的数据

 

0x248/4

payload_offset内核负载偏移

 

0x24C/4

payload_length内核负载的长度

 

0x250/8

setup_data指向存放setup_data结构体的链表的64位物理指针

 

0x258/8

pref_address首选的加载地址

 

0x260/4

init_size初始化期间的线性地址要求

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 小学教育 > 小学作文

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1