内存寻址——分段

段描述符

硬件中包含很多段,比如代码段、数据段等,每个段都由一个8字节的段描述符表示,段描述符存放在全局描述符表(GDT)或者局部描述符表(LDT)中,段描述符中的字段如下所示:
在这里插入图片描述

注意其中的Type字段就描述了段类型,下面是Linux中采用的各种段描述符中每个字段的情况
在这里插入图片描述
GDT地址和大小存放在gdtr控制寄存器中,LDT地址和大小存放在ldtr控制寄存器中
Linux对gdt的定义如下

Linux中对gdt的定义如下
ENTRY(cpu_gdt_table)
	.quad 0x0000000000000000	/* NULL descriptor */
	.quad 0x0000000000000000	/* 0x0b reserved */
	.quad 0x0000000000000000	/* 0x13 reserved */
	.quad 0x0000000000000000	/* 0x1b reserved */
	.quad 0x0000000000000000	/* 0x20 unused */
	.quad 0x0000000000000000	/* 0x28 unused */
	.quad 0x0000000000000000	/* 0x33 TLS entry 1 */
	.quad 0x0000000000000000	/* 0x3b TLS entry 2 */
	.quad 0x0000000000000000	/* 0x43 TLS entry 3 */
	.quad 0x0000000000000000	/* 0x4b reserved */
	.quad 0x0000000000000000	/* 0x53 reserved */
	.quad 0x0000000000000000	/* 0x5b reserved */

	.quad 0x00cf9a000000ffff	/*__KERNEL_CS  0x60 kernel 4GB code at 0x00000000 */
	.quad 0x00cf92000000ffff	/* __KERNEL_DS 0x68 kernel 4GB data at 0x00000000 */
	.quad 0x00cffa000000ffff	/* __USER_CS 0x73 user 4GB code at 0x00000000 */
	.quad 0x00cff2000000ffff	/*__USER_DS  0x7b user 4GB data at 0x00000000 */

	.quad 0x0000000000000000	/* 0x80 TSS descriptor */
	.quad 0x0000000000000000	/* 0x88 LDT descriptor */

	/* Segments used for calling PnP BIOS */
	.quad 0x00c09a0000000000	/* 0x90 32-bit code */
	.quad 0x00809a0000000000	/* 0x98 16-bit code */
	.quad 0x0080920000000000	/* 0xa0 16-bit data */
	.quad 0x0080920000000000	/* 0xa8 16-bit data */
	.quad 0x0080920000000000	/* 0xb0 16-bit data */
	/*
	 * The APM segments have byte granularity and their bases
	 * and limits are set at run time.
	 */
	.quad 0x00409a0000000000	/* 0xb8 APM CS    code */
	.quad 0x00009a0000000000	/* 0xc0 APM CS 16 code (16 bit) */
	.quad 0x0040920000000000	/* 0xc8 APM DS    data */

	.quad 0x0000000000000000	/* 0xd0 - unused */
	.quad 0x0000000000000000	/* 0xd8 - unused */
	.quad 0x0000000000000000	/* 0xe0 - unused */
	.quad 0x0000000000000000	/* 0xe8 - unused */
	.quad 0x0000000000000000	/* 0xf0 - unused */
	.quad 0x0000000000000000	/* 0xf8 - GDT entry 31: double-fault TSS */

可以看到四个主要的Linux段描述符的值
.quad 0x00cf9a000000ffff /*__KERNEL_CS 0x60 kernel 4GB code at 0x00000000 /
.quad 0x00cf92000000ffff /
__KERNEL_DS 0x68 kernel 4GB data at 0x00000000 /
.quad 0x00cffa000000ffff /
__USER_CS 0x73 user 4GB code at 0x00000000 /
.quad 0x00cff2000000ffff /
__USER_DS 0x7b user 4GB data at 0x00000000 */
其Base都为0

逻辑地址

逻辑地址由16位的段选择符(又称为段标识符)和32位的指向段内相对地址的偏移量组成,段标识符的组成如下所示:
在这里插入图片描述

分段单元

给定一个逻辑地址,我们就可以结合段描述符得到对应的线性地址,
(1)先检查段选择符的TI字段,TI字段指明段描述符是存放在GDT还是LDT中,如果是在GDT中,则需要从gdtr寄存器中读取GDT的线性基地址,否则就从ldtr寄存器中读取LDT的线性基地址。
(2)从段选择符中读取index字段的值,因为一个段描述符的大小是8字节,所以index字段的值乘以8在加上gdtr或者ldtr的值就可以得到对应段描述符的地址。
(3)读取段描述符中的Base字段的值,其是段的首字节的线性地址,将其与逻辑地址的偏移量相加就得到线性地址。
上述过程如下图所示:
在这里插入图片描述

快速访问段描述符

从上述的分段过程可以看到每次都需要访问gdtr或者ldtr寄存器,然后才能找到对应的段描述符,为了加速逻辑地址到线性地址的转换,80x86提供了一种附加的非编程的寄存器含有8个字节的段描述符,供6个可编程的段寄存器使用(cs,ss,ds,es,fs,gs),当一个段选择符被装入段寄存器时,相应的段描述符就由内存装入对应的非编程CPU寄存器,从那时起,真对哪个段的逻辑地址转换就可以不访问主存中的GDT或LDT,处理器只需要直接饮用存放段描述符的CPU寄存器即可,仅当段寄存器的内容改变时,才有必要访问GDT或LDT。

Linux中的分段

分段可以给每个进程分配不同的线性地址空间,而分页把同一线性地址空间映射到不同物理地址空间,2.6版本的Linux只有在80x86体系结构下才使用分段。运行在用户态所有Linux进程都使用同一对用户代码段和用户数据段,运行在内核态的所有Linux进程都使用同一对内核代码段和内核数据段,下面是这四个段描述符字段的值
在这里插入图片描述
相应的段选择符由__KERNEL_CS、__KERNEL_DS、__USER_DS、__USER_CS分别定义,例如,为了对内核代码段寻址,内核只需要把__KERNEL_CS宏产生的值装进cs寄存器即可,相应的内核代码段描述符就会自动装入对应的非编程CPU寄存器。
从上表可以看到,Linux所有的段地址都是从0x00000000开始,也就是说Linux下逻辑地址与线性地址是一致的,即逻辑地址的偏移量字段的值与相应的线性地址的值总是一致的。
CPU的当前特权级(CPL)反映了进程是处于内核态还是用户态,并由存放在cs寄存器中的段选择符的RPL字段指定。只要当前特权级改变,一些段寄存器就必须被更新,比如当CPL=3(用户态)时,ds寄存器必须含有用户数据段的选择符,而当CPL=0(内核态)时,ds寄存器必须含有内核数据段的选择符,类似的情况也出现在cs寄存器中。
当对执向指令或者数据结构的指针进行保存时,内核根本不需要为其设置逻辑地址的段选择符号,因为cs寄存器就含有当前的段选择符号,例如,当内核调用一个函数时,它执行一条call汇编语言指令,该指令仅指定它逻辑地址的偏移量部分,而段选择符不用设置,其隐含在cs寄存器中了。因为“在内核态执行” 的段只有一种,叫做代码段,由宏_KERNEL_CS定义,所以只要当CPU切换入内核态时将__KERNEL_CS装载入cs。同样的道理也适用于指向内核数据结构的指针(隐含地使用ds寄存器)以及指向用户数据结构的指针(内核显式地使用es寄存器)。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值