Intel 80x86微处理器 芯片级的内存寻址
Linux 如何利用寻址硬件
一、 内存地址
地址 | 英文 | 说明 |
---|---|---|
逻辑地址 | logical address | 由一个段(segment)和偏移量(offset)组成 |
线性地址 | linear address | 是一个32位无符号整数,可以用来表示高达4GB的地址 |
物理地址 | physical address | 用于芯片级内存单元寻址 |
二、硬件的分段单元
段寄存器
逻辑地址由段标识符和指定段内相对地址的偏移量
段标识符是一个16bit的域,称为段选择符(segment selector),偏移量是一个32bit的域
为了快速方便地找到段选择符,处理器提供段寄存器(segmentation register),目的是存放段选择符。
段寄存器 | 用途 |
---|---|
cs | 代码段寄存器,指向存放程序指令的段 含有一个两位的域,用于指明CPU的当前特权级。Linux 0 内核态 3 用户态 |
ss | 栈段寄存器,指向存放当前程序栈的段 |
ds | 数据段寄存器,指向存放静态数据或者外部数据的段 |
es,fs,gs | 一般用途,可以用来访问任何段 |
段描述符
每个段由一个8字节的段描述符(segment descriptor)来表示,它描述了段的特征。段描述符被放在全局描述符表(Global Descriptor Table,GDT)或局部描述表(Local Descriptor Table, LDT)
系统通常只定义一个GDT,而每个进程可以有自己的LDT.GDT在主存中的地址被放在处理器的gdtr寄存器中,当前正被使用的LDT的地址被放在处理器的ldtr寄存器中。
段描述符:不可编程寄存器
段选择符
段选择符 | 说明 |
---|---|
13位的索引 | 指定了放在GDT或LDT中的相应段描述符的入口 |
TI(描述符表指示符) | TI = 0,在GDT中;TI=1,在LDT中 |
两位RPL(请求特权级)域 | 描述了当相应的段选择符装入到cs寄存器中时,CPU的当前特权级 |
段单元
一个逻辑地址是怎样转换成相应的线性地址的。
1.检查段选择符的TI域,用于决定段描述符保存在哪一个描述符表中。
TI | 说明 |
---|---|
0(描述符在GDT中) | 段单元从gdtr寄存器中得到GDT的线性基地址 |
1(描述符在LDT中) | 段单元从ldtr寄存器中得到LDT的线性基地址 |
2.从段选择符的索引域计算段描述符的地址,索引域的值乘以8,这个结果被放在gdtr或ldtr寄存器中
3.将逻辑地址的偏移量与段描述符基地址域的值相加,就得到了线性地址
只有当段寄存器的内容被改变时才需要执行前两个操作
Linux中的段
段可以给每一个进程分配不同的线性地址空间,而页可以把同样的线性地址空间映射到不同的物理空间。
Linux喜欢使用分页方式:
1.当所有的进程使用同样的段寄存器值时,内存管理变成更简单,也就是说它们能共享同样的线性地址
2.Linux目标:可以把它移植到绝大多数流行的处理器平台上。
由于所有的进程使用同样的逻辑地址空间,因此需要定义的段的总数就可以被限制得很少,于是把所有的段描述符存放在全局描述符*(GDT)中就成了可能。
域 | 内核代码段 | 内核数据段 | 用户代码段 | 用户数据段 |
---|---|---|---|---|
Base | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 |
Limit | 0xfffff | 0xfffff | 0xfffff | |
G(粒度标志) | 1(段大小,单位以页计) | 1 | 1 | 1 |
S(系统标志) | 1(正常的代码段或数据段) | 1 | 1 | 1 |
Type | 0xa(可读可执行的代码段) | 0x2(可读写数据段) | 0xa | 0x2 |
DPL | 0(内核态) | 0 | 3(用户态) | 3 |
D/B(32位地址标志) | 1(偏移量为32bit) | 1 | 1 | 1 |
段选择符 | __KERNEL_CS | __KERNEL_DS | __USER_CS | __USER_DS |
对于每一个进程,GDT包含两个不同的段描述符,一个用于TSS段,一个用于LDT段。当进程被创建时,其TSS和LDT描述符被加入到GDT中。