Linux内存寻址-分段

内存寻址

内存地址

X86处理器

内存控制单元MMU通过分段单元的硬件电路把一个逻辑地址转换成线性地址,接着分页单元的硬件电路把线性地址转换成一个物理地址如下图:
请添加图片描述

Linux arm处理器

下面会介绍,linux下逻辑地址与线性地址是一致的

分段

分段可以给每一个进程分配不同的线性地址空间,而分页可以把同一个线性地址空间映射到不同的物理空间。

硬件中的分段

Intel微处理器有两种不同的方式执行地址转换,这两种方式分别为实模式(real mode)和保护模式(protected mode)。

逻辑地址由两部分组成:段标识符和指定段内相对地址的偏移量。

段选择符和段寄存器

请添加图片描述
段标识符又叫段选择符,16位长的字段
为了快速找到这些段选择符,处理器需要提供段寄存器用来存放这些段选择符:cs:代码段寄存器 ss:栈段寄存器 ds:数据段寄存器

es fs gs 这三个可以指向任意数据段。cs还有一个重要的功能,它含有两位字段,可以指明CPU的当前特权级(CPL)。0为最高优先级,3表示最低优先级,分别称为内核态和用户态

段描述符

寻址之前还需要知道段到底是什么,实际每个段由一个8字节的段描述符表示,描述了段的特征,段描述符放在**全局描述符表(GDT)局部描述符表(LDT)**中。GDT在主存中的地址和大小存放在gdtr控制寄存器中,当前正在被使用的LDT地址和大小放在ldtr控制寄存器中。格式如下
请添加图片描述

Base:包含段的首字节的线性地址

G:粒度标志:清零的话,则段大小以字节为单位,否则以4096字节的倍数计算

Limit:存放段中最后一个内存单元的偏移量,从而决定段的长度

S:系统标志 0 为系统段

Type:描述段的类型特征和它的存取权限

DPL:描述符特权级字段:用于限制对这个段的存取。它表示访问这个段而要求的CPU的最小优先级,因此,DPL设为0的段只能当CPL为0时候才是可以访问的,而DPL为3的段对任何CPL值都是可访问的。

P:Segment-present标志位 0表示不在主存中,linux总是设成1,因为它从来不把整个段交换到磁盘上去

D或B:代码段还是数据段
快速访问段描述符

处理器中为了加速逻辑地址到线性地址的转换,每当一个段选择符被装入段寄存器时,相应的段描述符就由内存装入到对应的非编程CPU寄存器。该段的逻辑地址转换就不用访问主存中的GPT和LDT了,处理器直接引用存放段描述符的CPU寄存器即可。仅当段寄存器的内容改变时,才有必要访问GDT和LDT,这种技术就叫做快速访问段描述符。如下图:
请添加图片描述
逻辑地址转换成线性地址的过程
请添加图片描述

index:指定了放在GDT或LDT中的相应段描述符的入口

TI:TI标志指明段描述符实在GDT中(TI=0)或在LDT中(TI=1)

RPL:请求者特权级:当相应的段选择符装入到cs寄存器中时指出CPU当前的特权级。

流程:

1 先检查段选择符的TI字段,确定段描述符保存在哪一个描述符表中。

2 从段选择符的index字段计算段描述符的地址。index字段的值乘以8(一个段描述符的大小),这个结果与gdtr或ldtr寄存器中的内容相加

3 把逻辑地址的偏移量与段描述符的Base字段的值相加就得到了线性地址。

例:如果GDT在0x00020000且由段选择符所指定的index索引号为2,那么相应的段描述符的地址是0x00020000+(2*8),即0x00020010

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

Linux中的分段

Linux更喜欢使用分页的方式,2.6之后就没有分段了

因为当所有进程使用相同的段寄存器值时,内存管理变得简单,共享同样的一组线性地址,且Linux设计的目标之一是移植到绝大多数流行处理器平台上

所以运行在用户态的所有Linux进程都使用一对相同的段来对指令和数据寻址。这两个段就是所谓的用户代码段和用户数据段。

类似的运行在内核态的所有Linux进程都是用了一对相同的段对指令和数据寻址。分别叫做内核代码段和内核数据段。

下图是四个主要的Linux段描述符值
请添加图片描述
相应的段选择符由宏 ____USER_CS,____USER_DS ___KERNEL_CS ___KERNEL_DS分别定义,为了对内核代码段寻址,内核只需要把 ___KERNEL_CS 宏产生的值装进cs段寄存器即可。

在Linux中所有与段相关的线性地址从0开始。达到2^32 -1 寻址限长,这就意味着在用户态或内核态下的所有进程可以使用相同的逻辑地址。所有段都是从0x00000000开始,在Linux下逻辑地址与线性地址是移植的,即逻辑地址的偏移量字段的值与相应的线性地址的值是一致的。

Linux GDT

在进入保护模式之前,必须要定义GDT,也就是说,我们要在内存中构建出一张表。

需要说明的是:在整个系统中,全局描述符表GDT只有一张(一个处理器对应一个GDT);GDT可以被放在内存的任何位置,但CPU必须知道GDT的入口。

你也许会问:CPU如何知道GDT的入口呢?别担心,在处理器内部,有一个48位的寄存器,名叫GDTR,也就是全局描述符表寄存器。其结构如下图:
请添加图片描述
该寄存器分为2部分:

  • 32位的线性基地址:GDT在内存中的起始线性地址(我们还没有涉及到分页,所以这里的线性地址等同于物理地址,下同,以后同);
  • 16位的表界限:在数值上等于表的大小(总字节数)减去1;

注意:在处理器刚上电的时候,基地址默认为0,表界限默认为0xFFFF; 在保护模式初始化过程中,必须给GDTR加载一个新值。

因为表界限是16位的,最大值是0xFFFF,也就是十进制的65535,那么表的大小就是65535+1=65536.又因为一个描述符占用8个字节,所以65536字节相当于8192个描述符(65536/8=8192).故理论上最多可以定义8192个描述符。实际上,不一定这么多,具体多少根据需要而定。

linux的GPT如下图所示
请添加图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值