读《深入理解Linux内核》笔记:第零篇 [author: linkrain]
80x86微处理器中的地址
在80x86微处理器中,存在三种不同的地址概念:
- 逻辑地址
- 线性地址(虚拟地址)
- 物理地址
逻辑地址:
在机器语言中用来指定操作数或者指令的地址。逻辑地址由两部分构成,分别是段以及段内偏移量。(16位的段选择符与32位的段内偏移量)
线性地址(虚拟地址):
以连续的二进制数方式组织的地址表示形式,以连续的方式对物理地址进行映射。线性地址通常是一个32位无符号整数,总共可以表示4GB个地址,通常使用8位16进制数来表示,其范围为0x00000000至0xFFFFFFFF。
物理地址:
用于内存芯片级内存单元寻址。它们与从微处理器芯片上的地址引脚发送到内存总线上的电信号相对应。物理地址由32位或者36位无符号整数表示。
地址转换:
内存控制单元-MMU通过叫分段单元-segmentation unit的硬件电路把逻辑地址转换成线性地址,接着通过叫分页单元-paging unit的硬件电路把线性地址转换成物理地址。
内存仲裁器-memory arbiter:
在RAM芯片上,读或写操作必须串行执行,因此同一时间RAM只能为一个目标提供服务。
在多处理器系统中,多个CPU可以并发访问RAM芯片;在单处理器系统中,存在DMA能够和CPU同时访问RAM。因此,为了避免多个目标同时访问RAM芯片造成的冲突,在RAM芯片前面存在一个硬件模块用于进行访问冲突处理,这个硬件模块就是内存仲裁器。
段选择符与段描述符:
逻辑地址由16位的段选择符与32位的段内偏移量组成。其中段选择符共有三个字段,索引号-index、表指示器-TI、请求者特权级-RPL。
其中索引号占高13位,请求者特权级占低2位,表指示器占剩余的1位。
请求者特权级占2位意味着存在4种不同的特权级,但在Linux中,只用到特权级3与特权级0,分别对应用户态与内核态。
段描述符存储于描述符表中,描述符表共有两种,GDT-全局描述符表、LDT-局部描述符表。段描述符共有8字节长度,即64位。
由于段选择符中用于表示段描述符在描述符表中的位置的索引号-index字段只占13位,因此GDT或LDT中总共能够存储213个段描述符。**在GDT中,第一个段描述符总是全0**,这用来确保空段选择符会被认为是无效的。因此,在GDT中,最多只能存储213-1个段描述符。
段的查找路径是这样的:
- 从逻辑地址中拿到段选择符,将段选择符存入段寄存器,通过段选择符中的表指示器-TI字段判断是从全局描述符表-GDT还是从局部描述符表-LDT中查找对应段
- 确定从GDT或是LDT中查找对应段后,通过段选择符中的索引号字段去相应的描述符表中查找对应段的段描述符,GDT从gdtr-全局描述符表寄存器中获得GDT的基线性地址,LDT的基线性地址则从ldtr-局部描述符表寄存器中获得
- 之后将对应段的段描述符存入对应段寄存器的一个非编程寄存器中,这样如果该段寄存器中的段选择符没有改变,就不需要再重复地去描述符表中查找对应的段描述符了
段寄存器:
存在6个段寄存器,其中三个为特殊用途段寄存器,剩余三个为通用段寄存器。
- cs(代码段寄存器)
- ds(数据段寄存器)
- ss(栈段寄存器)
- es
- fs
- gs
cs-代码段寄存器含有一个2位的字段CPL-当前特权级,用以指明CPU的当前特权级,同RPL类似,Linux中只用到了3与0,对应用户态与内核态。
同一个段寄存器可以通过寄存器内容暂存的方式用于不同的用途,即将寄存器的内容暂时存入内存,待寄存器用完后再从内存恢复其内容。
段的大小:
在段描述符中,有一个粒度标志-G字段,占1位,当此字段置0时,那么段的容量单位以1字节计,当此字段置1时,段的容量单位以4KB计;
在段描述符中,还有一个段末偏移量-Limit字段,用来表示段的最后一字节的偏移量,当G字段为0时,此偏移量范围在1B~1MB之间;当G字段为1时,此偏移量范围在4KB~4GB之间。