《深入理解Linux内核》读书笔记-第二章-内存寻址(1)

时间:2023-02-15 11:53:56


内存地址:


逻辑地址:包含在机器语言指令中用来指定一个操作数或一条指令的地址。


线性地址:一个32位无符号整数,也称虚拟地址。


物理地址:用于内存芯片级内存单元寻址,与从微处理器的地址引脚发送到内存总线上的电信号相对应。


在多处理器系统中,所有CPU都共享同一内存,这意味着,RAM芯片可以由独立的CPU并发地访问。由于RAM芯片上的读或写操作必须串行地执行,

因此一种所谓内存仲裁器的硬件电路插在总线和每个RAM芯片之间,其作用是如果某一个RAM芯片空闲,就准予一个CPU访问,如果该芯片忙于为

另一个处理器提出的请求服务,就延迟这个CPU的访问。


即使在单处理器上也使用内存仲裁器。因为单处理器系统中包含一个叫做DMA控制器的特殊处理器,而且DMA控制器与CPU并发操作。



硬件中的分段:


一个逻辑地址由一个16段标识符(或称为段选择符,如下)和一个32段内偏移组成。


段选择符包含13位的索引号、1TI表指示器以及2RPL请求者特权级。


每个段由一个8字节的段描述符表示,段描述符存放在全局描述符表(GDT)或者局部描述符表(LDT)中。通常只定义一个GDT,而每个进程都拥

有自己的LDT


GDT的第一项总是设为0,这就确保空段选择符的逻辑地址会被认为是无效的,并能引起一个处理器异常。由此,GDT中最大段描述符数目为213-1

例:如果GDT0x0020000(这个值保存在gdtr寄存器中),且由段选择符所指定的索引号为2(即段选择符高13位的值为2),则由于GDT第一项为0

由该段选择符所指定的段描述符地址是0x0020000+2*8或者0x0020010


逻辑地址转换成相应线性地址:分段单元先检查段选择符的TI字段,决定段描述符的保存位置(GDT或者LDT),若为GDT,分段单元从gdtr寄存器

中得到GDT的线性基址,若为激活的LDT,则分段单元从ldtr寄存器中得到LDT的线性基址;然后,从段选择符的index字段计划段描述符的地址,把

这个地址与逻辑地址中的偏移量字段值相加,得到线性地址。


Linux中的分段:


分段可以给每一个进程分配不同的线性地址空间,而分页则可以把同一线性地址空间映射到不同的物理空间。与分段相比,Linux更喜欢使用分页方式,

因为:当所有进程使用相同的段寄存器值时,内存管理变得更加简单;Linux设计目标之一是可移植到大多数处理器平台上,而RISC体系结构对分段的

支持很有限。


Linux主要使用了四个段:用户代码段、用户数据段、内核代码段、内核数据段。相应的段选择符由宏__USER_CS__USER_DS__KERNEL_CS

__KERNEL_DS定义,内核只需把相应宏产生的值装入cs段寄存器即可对相应的段寻址。


当对指向指令或者数据结构的指针进行保存时,内核不需为其设置逻辑地址的段选则符,因为cs寄存器就含有当前的段选择符。


例:当内核调用一个函数时,它执行一条call汇编语言指令,该指令仅指定其逻辑地址的偏移量部分,而段选择符不用设置,因为“在内核态执行”

的段只有内核代码段,由__KERNEL_CS定义。


在单处理器系统中只有一个GDT,而多处理器系统中每个CPU对应一个GDT


Linux中每一个GDT包含18个段描述符,指向下列段:

l 用户态和内核态下的代码段和数据段共4个;

l 任务状态段,每个处理器有1个;

l 1个包含缺省局部描述符表的段,这个段通常被所有进程共享;

l 3个局部线程存储(TLS)段;

l 3个与高级电池管理相关的段;

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

l 1个被内核用来处理“双重错误”异常的特殊TSS段;

注:处理一个异常时可能会引发另一个异常,这咱情况下产生双重错误。


大多数用户态下的Linux程序不使用LDT,这样内核就定义了一个缺省的LDT供大多数进程共享。