Notes on Understanding The Linux Kernel

第二章 内存寻址

内存地址

逻辑地址 logical address 包含在机器语言指令中用来指定一个操作数或一条指令的地址。每个逻辑地址由一个段segment和偏移量offset组成,偏移量指明了从段开始的地方到实际地址之间的距离。

线性地址 linear address 也称虚拟地址 virtual address 是一个32位无符号整数,可以用来表示高达4G的地址。线性地址通常用十六进制数字表示,值得范围从0x00000000到0xffffffff。

物理地址 physical address 用于内存芯片级内存单元寻址

内存控制单元 MMU通过一种称为分段单元 segmentation unit 的硬件电路把一个逻辑地址转换成线性地址;第二个称为分页单元 pagingunit的硬件电路把线性地址转换成一个物理地址。

多处理器系统中,所有CPU都共享同一内存,RAM芯片可以由独立的CPU并发地访问。在RAM上读或者写操作必须串行地执行,内存仲裁器memory arbiter 硬件电路在总线和RAM芯片之间。


段选择符和段寄存器

一个逻辑地址由一个段标识符和一个指定段内相对地址的偏移量组成。段标识符是一个16位长的字段,称为段选择符segment selector,而偏移量是一个32位长的字段。为了能方便的找到段选择符,处理器提供段寄存器,段寄存器的唯一目的是存放段选择符。这些段选择符有cs,ss,ds,es,fs和gs。

三个有专门的用途

cs 代码段寄存器,指向包含程序指令的段。

ss 栈段寄存器,指向包含当前程序栈的段。

ds 数据段寄存器,指向包含静态数据或者全局数据段。

其他三个寄存器作一般用途,可以指向任意的数据段。

cs寄存器还有一个很重要的功能:它含有一个两位的字段,用以指明CPU当前特权级。0值表示最高优先级,3表示最低优先级。Linux用0级和3级,分别表示内核态和用户态。


段描述符 

每个段由一个8字节的段描述符表示,它表示了段的特征。

快速访问段描述符

为了加速逻辑地址到线性地址的转换,8086处理器提供一种非编程式的寄存器,供6个可编程段寄存器使用。每个非编程寄存器含有8个字节的段描述符,由相应的段寄存器中的段选择符来指定。每当一个段选择符被装入段寄存器时,相应的段描述符就由内存装入到对应的非编程CPU寄存器。从此时开始,针对该段的逻辑地址转换就可以不访问主存中的GDT或LDT,处理器只需要直接引用存放段描述符的CPU寄存器即可。仅当段寄存器的内容改变时,才有必要访问GDT或LDT。

 段选择符

15-3索引号 2指示器 1-0请求者特权级

由于段描述符是8 字节长,因此在GDT或LDT内的相对地址是由段选择符的最高13位乘以8得到的。
GDT的第一项总设为0。确保空段选择符的逻辑地址会被认为是无效的,因此引起一个处理器异常。能够保证在GDT中的段描述符最大数目是2 ^13-1。

分段单元 segmentation unit

先检查段选择符的TI字段, 以决定段描述符保存在哪一个描述表中。



运行在用户态的所有Linux进程都使用一对相同的段来对指令和数据寻址。这两个段就叫做用户代码段和用户数据段。类似的运行在内核态的所有Linux进程都使用一对相同的段对指令和数据进行寻址。分别叫做内核代码段和内核数据段。

与段相关的线性地址从0开始到达了2^32-1的限制长度。所以用户态或者内核态下的所有进程可以使用相同的逻辑地址。

  

分段和分页都是划分进程的物理地址。

分段给每一个进程分配不同的线性地址空间,分页把同一线性地址空间映射到不同的物理空间。


Linux GDT

单处理器中只有一个GDT,多处理器系统中每个CPU对应一个GDT,所有GDT都放在cpu_gdt_table数组中,而所有gdt的地址和它们的大小都被存放在cpu_gdt_descr数组中。每个GDT包含18个段描述符和14个空的,未使用的,或保留的项。插入未使用的项的目的是为了使经常一起访问的描述符能够处于同一个32字节的高速缓存中。

用户态和内核态的代码段和数据段共4个。

任务状态段TSS,每个处理器有一个。每个TSS相应的线性地址空间都是内核数据段相应线性地址空间的一个小子集。所有任务状态段都顺序地存放在init_tss数组中,第n个CPU的TSS描述符的Base字段指向init_tss数组的第n个元素。

一个包括缺省局部描述符表的段,这个段被进程共享。

3个局部线程存储段thread-local storage TLS段:这种机制允许多线程应用程序使用最多3个局部于线程的数据段。

与高级电源管理相关的三个段

与支持即插即用PnP功能的BIOS服务程序相关的5个段

被内核用来处理双重错误异常的特殊TSS段


Linux LDT

大多数用户态下的Linux程序不使用局部描述表,这样内核就定义了一个缺省的LDT供大多数进程共享。缺省的局部描述符表存放在default_ldt数组中。


硬件中的分页

分页单元 paging unit把线性地址转换成物理地址。其中一个关键任务是把所请求的访问类型与线性地址的访问权限相比较,如果这次内存访问是无效的,就产生一个缺页异常。线性地址被分成以固定长度为单位的组,称为页。页内部的线性地址被映射到连续的物理地址中。内核可以指定一个页的物理地址和其存储权限,而不用指定页所包含的全部线性地址的存取权限。分页单元把所有RAM分成固定长度的页框。每一个页框包含一个页,也就是说一个页框的长度与一个页的长度相同。把线性地址映射到物理地址的数据结构称为页表。

32位线性地址被分为3个域:

目录 最高10位

页表 中间10位

偏移量 最低12位

线性地址的转换分两步完成,每一步都基于一种转换表。第一种转换表称为页目录表,第二种表称为页表。

使用二级模式的目的在于减少每个进程页表所需RAM的数量。每个活动进程必须由一个分配给它的页目录。不过没有必要马上为进程所有的页表分配RAM,只有在进程实际需要一个页表时才给该页表分配RAM会更有效率。 

硬件高速缓存

 高速缓存单元插在分页单元和主内存之间。它包含一个硬件高速缓存内存和一个高速缓存控制器。高速缓存内存存放内存中真正的行。高速缓存控制器存放一个表项数组,每个表项对应内存中的一个行。每个页目录和页表都包含两个标志:PCD page cache disablt指明当访问包含在这个页框中的数据时,高速缓存功能必须被启用还是禁用。PWT page write-through 指明当把数据写到页框时,必须使用的策略是回写策略还是通写策略。Linux清除了标志,对所有页框都启用高速缓存,对于写操作总是采用回写策略。

linux中的分页

页全局目录

页上级目录

页中间目录

页表


物理内存布局

Linux内核安装在RAM中从0x00100000开始的地方,也就是从第二个MB开始。所需页框总数依赖于内核的配置方案。

内核不安装在RAM第一个MB开始的地方,因为PC体系结构有几个独特的地方。

页框0由BIOS存放加电自检几千检查到的系统硬件配置。

物理地址0x000a0000-0x000fffff通常留给BIOS例程

第一个MB内的其他页框可能由特定计算机模型保留。


进程页表

0x00000000-0xbfffffff的线性地址,无论进程运行在用户态还是内核态都可以寻址。

0xc0000000-0xffffffff的线性地址只有内核态的进程才能寻址。

 

处理硬件高速缓存和TLB

硬件高速缓存是通过高速缓存行寻址的

为了使高速缓存的命中率达到最优化

一个数据结构中最常使用的字段放在该数据结构内低偏移部分,以便它们能够处于高速缓存的同一行中。

当为一大组数据结构分配空间时,内核试图把它们都存放在内存中,以便所有高速缓存行按同一方式使用。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值