读书笔记:LINUX内核完全剖析:基于0.12内核

来源:互联网 发布:自学云计算 编辑:程序博客网 时间:2024/06/06 00:21
读书笔记:LINUX内核完全剖析
  IBM PC及其兼容机主要使用独立编址方式,采用独立的I/O地址空间对控制设备中的寄存器进行寻址和访问,IBM PC也部分地使用统一编址。在普通Linux系统下通过查看/proc/ioports文件可以得到相关控制器或设置使用的I/O地址。1981年IBM PC刚推出时系统只带有640KB的RAM主存储器,由于8088/8086CPU只有20根地址线,因此内存寻址范围最高为1024KB(1MB)。现在CPU的物理内存寻址范围已经高达4GB(甚至更高)。为了与原来的PC在软件上兼容,系统1MB以下物理内存使用分配上仍然保持与原来PC基本一致。只是原来系统ROM中的基本输入输出程序BIOS一直处于CPU能寻址的内存最高端位置处,现在BIOS原来所在的位置将在计算机开机初始化时被用作BIOS的影子区域,即BIOS代码仍然会被复制到这个区域(参考BIOS研发技术剖析)。除了地址从0xA0000-0xFFFFF(640KB-1MB)和0xFFFE0000-0xFFFFFFFF(4GB最后64KB)范围以外的所有内存都可以用做系统内存。这两个特定范围被用于I/O设备和BIOS程序。

当计算机系统上电后,CPU会自动把代码段寄存器CS设置为0xF000,其段基地址被设置为0xFFFF0000,段长度设置为64KB。而IP被设置为0xFFF0,因此CPU代码指针指向0xFFFFFFF0处(4GB空间最后16B)。BIOS会在这里存放一条跳转指令JMP,跳转到BIOS代码中64KB范围内某一条指令执行。由于目前PC/AT中BIOS容量大多有1MB-2MB,为了执行BIOS中超过64KB的其他BIOS代码或数据。BIOS程序会首先使用32位访问方式把数据段寄存器的访问范围设置成4GB(而非原来的64KB)。这样CPU就可以在0-4GB范围执行和操作数据。此后,BIOS在执行一些硬件检测和初始化操作后,就会把与原来PC兼容64KB BIOS代码和数据复制到内存低端1MB末端的64KB处,然后跳转到这个地方让CPU真正运行在实地址模式下。具体BIOS的发展是随着CPU的发展而发展的(参考BIOS研发技术剖析)。

关于实地址模式和保护模式:http://blog.csdn.net/heiworld/article/details/24371677 注意这里面介绍的这句话:Intel选择了在段寄存器的基础上构筑保护模式,并且保留段寄存器16位。在保护模式下,它的段范围不再受限于64K,可以达到4G。
  80x86处理器使用了一种称为段的寻址技术,从内存中寻址需要使用段地址和段内偏移地址。段地址部分使用16位的段选择符指定,段内偏移地址部分使用32位的值来指定。这两部分构成48位地址称为逻辑地址。80x86从逻辑地址到物理地址变换过程使用了分段分页两种机制。第一阶段使用分段机制把程序的逻辑地址变换为线性地址,第二阶段使用分页机制把线性地址转换为物理地址。
  分段提供了一种机制,用于把处理器可寻址的线性地址空间划分成一些较小的称为段的受保护地址空间区域。段可以用于存放程序代码、数据、堆栈,或者用来存放系统数据结构(如TSS或LDT)。

段选择符是16位标识符,用于提供段描述符表的偏移量,包含3个字段:请求特权级RPL(位0、1)、表指示标志TI(位2)、索引值(位3-15)。表索引字段TI用来指出包含指定段描述符的段描述符表GDT或LDT。对应用程序来说段选择符是作为指针变量的一部分而可见,但选择符的值通常是由链接编辑器或链接加载程序进行设置或修改。由于段选择符中有14位用于作为索引项,所以逻辑地址空间可包含最多16K(2^14)个段。每个段最长可达4GB(偏移地址32位)。段寄存器是用于存放段选择符的寄存器。为了避免每次访问内存时都去引用描述符表,段寄存器除去存放段选择符部分,其余的是描述符缓冲区(影子寄存器)。当一个段选择符被加载到一个段寄存器可见部分时,处理器同时把段选择符指向的段描述符中的段地址、段限长以及访问控制信息加载到段寄存器的影子寄存器区域。
段描述符表是段描述符的数组。有两种描述符表:全局描述符表GDT和局部描述符表LDT。虚拟空间被分割成大小相等的两半,一半是由GDT映射的全局虚拟地址空间,一半是由LDT映射的局部虚拟地址空间。包含LDT表的段必须在GDT表中有一个段描述符项。而GDT本身并不是一个段,而是线性空间中的一个数据结构。访问LDT需要使用其段选择符。为了在访问LDT时减少地址转换次数,LDT的段选择符、基地址、段限长以及访问权限需要存放在LDTR寄存器中。

全局描述符表寄存器GDTR和局部描述符表寄存器LDTR:GDTR寄存器用于存放全局描述符表GDT的32位的线性基地址和16位的表限长值。基地址指定GDT表中字节0在线性地址空间中的地址。GDT的限长以字节位单位,因为段描述符总是8字节长,所以GDT的限长值应该设置为总是8的倍数减1,GDT的基地址应该进行内存8字节对齐。LDTR寄存器用于存放局部描述符表LDT的32位线性基地址、16位限长和描述符属性值以及段选择符。

段描述符用于向处理器提供有关一个段的位置和大小信息以及访问控制的装态信息。每个段描述符的长度是8字节,含3个主要字段:段基地址、段限长和段属性。段描述符通常由编译器、链接器、加载器或者操作系统来创建。段类型字段用行指定段或门的类型、说明段的访问种类以及段的扩展方向。

把逻辑地址转换成线性地址:
(1)使用段选择符中的偏移值在GDT或LDT表中定位相应的段描述符(仅当一个新的段选择符加载到段寄存器中时才需要这一步)。
(2)利用段描述符检验段的访问权限和范围,以确保该段是可访问的并且偏移量位于段界限内。
(3)把段描述符中取得的段基地址加到偏移量上,最后形成一个线性地址。

分页机制
  80x86使用4K(2^12)字节固定大小的页面。每个页面均是4KB,并且对齐于4K地址边界处。这表示分页机制把4GB的线性地址空间划分成2^20(1M)个页面。线性地址的低12位作为页内偏移量直接作为物理地址的低12位。为了减少地址转换所要求的总线周期数量,最近访问的页目录和页表会存放在处理器的缓冲器件中(转换查找缓冲区)。
两级页表结构:高20位线性地址到物理地址的转换被分为两步,每步使用其中的10bit。第一级表称为页目录。它被存放在1页4K页面中,最多含有1K个4字节的表项。线性地址的最高10位用于索引页目录来选择页表。第二级表称为页表,它被存放在1页4K页面中,最多含有1K个4字节的表项。每个4字节表项含有相关页面的20位物理基地址。二级页表使用线性地址中间10位作为表项索引值。CR3寄存器指定页目录表的基地址。


代码段可以是一致性的或者非一致性的。向更高特权级一致性代码段的执行控制转移,允许程序以当前特权级执行。向一个不同特权级的非一致性代码段的转移将导致一般保护异常,除非使用了一个调用门或任务门。所有数据段都是非一致性的,即意味着它们不能被低特权级的程序或过程访问,但可以被更高特权级的程序或过程访问,而无需使用特殊的访问门。

请求特权级RPL(Requested Privilege Level)处于段选择符的1、0位
描述符特权级字段DPL(Descriptor privilege level)处于段描述符的14、13位
当前特权级CPL(Current Privilege Level)处于CS和SS段寄存器的1、0位


阅读全文
0 0