Linux 内存地址与分段机制
基于80x86
逻辑地址
指定一个操作数或一条指令的地址,这种寻址方式,在80x86分段模式下表现的尤为得体,在Windows里二进制可执行文件会分为若干段。每一个逻辑地址都由一个段和偏移组成,偏移量指定了从段开始的地方到实际地址的距离。
线性地址
也可以叫做虚拟地址,是一个32位整数,最高可以表示到4294967296个内存单元。通常用16进制表示,值的范围0x00000000到0xFFFFFFFF
物理地址
用于内存芯片级内存单元寻址。他们从微处理器的地址引脚发送到内存总线上的电信号对应。物理地址由32位或36位unsigned int 表示
内存管理单元(MMU)通过一种称为分段单元的硬件电路,把一个逻辑地址转换为线性地址,接着,第二个称之为分页单元的硬件电路把线性地址转换为物理地址。
在多处理器系统中,所有 CPU都共享同一内存,这意味着 RAM芯片可以由独立的 CPU并发地访问。因为在RAM芯片上的读或写操作必须串行地执行,因此一种所谓内存仲裁器的硬件电路插在总线和每个 RAM芯片之间,其作用是如果某个 RAM芯片空闲,就准予一个 CPU访问,如果该芯片忙于为另一个处理器提出的请求服务,就延迟这个 CPU的访问 。即使在单处理器上h也使用内存仲裁器,因为单处理器系统中包含一个叫做 DMA控制器的特殊处理器,在多处理器系统的情况下,因为仲裁器有多个输入端口,所以其结构更加复杂,例如,双Pentium在每个芯片的入口维持一个两端口仲裁器,并在试图使用公用总线前请求两个CPU交换同步信息.从编程观点看,因为仲栽器由硬件电路管理,因此它是隐藏的。
系统分段
一个逻辑地址由两部分组成:一个段标识符和一个指定段内先对地址的偏移量。段标识符是一个16位长的字段,称为段描述符。而偏移量是一个32位长的字段。
处理器提供6个段寄存器,保存了段选择符,分别是:cs ss ds es fs gs,6个段寄存器。
cs 代码段寄存器
ss 栈段寄存器
ds 数据段寄存器
cs 寄存器中,含有一个两位字段,用以指明CPU的当前CPL(Current Privilege Level),0代表R0级别,1代表R3级别。分别称为,内核态和用户态。
段描述符
每个段由一个8字节的段描述符表示,他描述了段的属性,段描述符放在GDT(Global Descriptor Table)或LDT(Local Description Table)中。
一般来说只定义一个GDT,而每个进程除了存放在GDT中的段之外如果还有附加的段,就可以有自己的LDT。GDT在主存中的地址和大小存放在gdtr控制寄存器中,当前正在被使用的LDT地址和大小放在ldtr寄存器中。
段描述符格式
段描述符字段:
字段 | 描述 |
---|---|
BASE | 包含段的首字节的线性地址 |
G | 粒度标志:如果该位清0,、则段大小以字节为单位,否则以4096字节的倍数计算 |
Limit | 存放段中最后一个内存单元的偏移量,从而决定段的长度,如果G被置为0,则一个段的大小在1个字节1 MB之间变化。否則,将在4 KB到 4 GB 之间变化 |
S | 系统标志:如果它被清0,则这是一个系统段,存储诸如 LDT这种关鍵的数据结构,否則它是一个普通的代码段或数据段。 |
Type | 描述了段的类型特征和它的存取权限。 |
DPL | 描述符特权级字段,用于限制对这个段的存取 。 它表示为访问这个段而要求的 CPU最小的优先级 。因此, DPL设为0的段只能当 CPL设为0时(即在内核态)才垃可访问的,而 DPL设为3的段对任何CPL都是可访问的。 |
P | Segment-Present标志:等于0表示段当前不在主存中, Linux总是把这个标志(第47位)设为1。因为它从来不把整个段交换到磁盘上去。 |
D 或 B | 称 为 D成 B的标志,取决于是代码段还是数据段 ,D或B的含义在两种情况下稍有所区别,但是如果段偏移量的地址是32位长,就基本上把它置为 1.如果这个偏移置是16位长,它被清0。 |
AVL | 被Linux忽略。 |
代码段描述符
表示这个段代表一个代码段,他可以放在GDT或LDT中,S标志位为1。
数据段描述符
表示这个段代表一个数据段,他可以放在GDT或LDT中,S标志位为1。
任务状态段描述符(TSSD)
表示这个段描述符代表一个任务状态段(Task State Segment)TSS,也就是说这个段用于保存处理器寄存器的内容它只能出现在GDT中。根据相应的进程是否正在CPU上运行,Type字段的值分别为11或9.这个描述符的S标志置0。
局部描述符表描述符(LDTD)
表示这个段描述符代表一个包含LDT的段,它只出现在GDT中。相应的Type字段的值 为2,S标志置为0。