linux内核-内存寻址

内存地址

程序员偶尔会引用内存地址作为访问内存单元的一种方式,但是,使用X86处理器时,我们必须区分以下三种不同的地址:

逻辑地址:每一个逻辑地址都是由一个段和偏移量组成,偏移量指明了从段开始的地方到实际地址之间的距离。

线性地址:也称虚拟地址,是一个32位无符号整数,可以用来表示4GB的地址,值范围从0x00000000到0xffffffff。

物理地址:用于芯片级单元寻址。它们与微处理器的地址引脚发送到内存总线上的电信号相对应。物理地址由32位或36位无符号整数表示。

内存控制单元MMU通过一种称为分段单元的硬件电路把一个逻辑地址转换成线性地址,接着第二个称为分页单元的硬件电路把线性地址转换成一个物理地址。

逻辑地址——分段单元——线性地址——分页单元——物理地址。

硬件中的分段

Intel微处理器以两种不同的方式执行转换,这两种方式称为保护模式和实模式,这两个在其他博客中有详细介绍。

段选择符和段寄存器

一个逻辑地址由两部分组成:一个段标识符和一个指定段的偏移量。段标识符是一个16位字长的段,称为段选择符,而偏移量是一个32位字长的字段。

为了快速方便的找到段选择符,处理器提供段寄存器,段寄存器的唯一目的是存放段选择付。这些段选择符称为:cs,ss,ds,es,fs和gs。程序可以把把同一个段寄存器用于不同目的,方法是先将其值保存在内存中,用完后再恢复。

6个寄存器中有三个有专门的用途:

cs 代码段寄存器,指向程序指令的段

ss  栈段寄存器,指向包含当前程序栈的段。

ds 数据段寄存器,指向包含静态数据或全局数据的数据段

其他三个寄存器作为一般用途,可以指向任意数据段。

cs寄存器还有一个很重要的功能:它包含一个两位的字段,用以指明cpu的当前特权级,0或3,分别对应内核态和用户态。

段选择符格式:0-1:RPL请求者特权级;2 T1=标志器 3-15索引号

段描述符

每一个段由8个字节的段描述符表示,他表示了段的特征。段描述符放在全局描述符表GDT或局部描述符表中。

通常只定义一个GDT,而每个进程除了存放在GDT中的段之外如果还需要创建附加的段,就可以有自己的LDT。下面列出了linux中广泛使用的类型:

代码段描述符:表示这个段描述符代表一个代码段,它可以放在GDT或LDT中。该描述符置S标志为1(非系统段)

数据段描述符:表示这个段描述符代表一个数据段,它可以放在GDT或LDT中。该描述符置S标志为1.

任务状态段描述符:这个段描述符代表一个任务状态段TSS,也就是说这个段用于保存处理器寄存器的内容。它只出现在GDT中,根据相应的进程是否在cpu上运行,其TYPE字段的值分为11或9.这个描述符的S标志为0.

局部描述符表描述符LDTD:表示这个段描述符代表一个包含LDT的段,它只出现在GDT中。相应的TYPE=2.s=0;

快速访问段描述符:

逻辑地址由16位段选择符和32位偏移量组成,段寄存器仅仅存放段选择符。为了加快逻辑地址到线性地址的转换,80x86提供一种附加的非编程寄存器,供6个可编程寄存器使用。每个非编程寄存器由8个字节组成的段描述符,有相应的段寄存器中的段选择符来指定。每当一个段选择付被装入到段寄存器时,相应的段描述符就由内存装入到对应的非编程CPU寄存器中。从那时起,针对那个段的逻辑地址转换就可以不用访问内存中的GDT或LDT,处理器只需要直接引用存放段描述符的CPU寄存器即可。仅当段寄存器的内容改变时,才访问GDT。

GDT的第一项总是设为0,这就确保空段选择符的逻辑地址总是无效的,因此引起一个处理器异常。

段描述符地址=GDT基址+段选择付值*8;

分段单元执行以下操作讲逻辑地址转换为线性地址。

1、先检查段选择符T1字段,判定段描述符在GDT中还是在LDT中。

2、计算段描述符地址:段描述符地址=GDT(LDT)基址+段选择付值*8;

3、把逻辑地址偏移量与段描述符BASE字段相加得到线性地址。

有了与段选择符相关的非编程寄存器,只有当段寄存器的内容改变时,才需执行前两个操作。

Linux中的分段:与分段相比,Linux更喜欢使用分页方式,2.6版只有在80X86结构下才使用分段。

1、当所有进程使用相同的段寄存器值时,内存管理变得简单,也就是说他们能共享同一组线性地址。

2、linux设计目标之一是可以把他移植到绝大多数流行的处理器平台上,然而RISC体系结构对分段的支持有限。

LINUX GDT,在单处理机系统中只有一个GDT,而在多处理器系统中每个CPU都对应一个GDT。每个GDT包含18个段描述符和14个空的未使用的保留的项。

每一个GDT中包含的18个段指向下列的段:

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

任务状态段,每个处理器一个,保存处理器寄存器中的内容。

3个局部线程存储段TSL:这种机制允许多线程应用程序使用最多3个局部于线程的数据段

与高级电源管理相关的3个段

支持pnp和bios服务程序相关的5个段

被内核处理双重异常的TSS段。

硬件中的分页:

分页单元把线性地址转换成物理地址。为了效率起见,线性地址被分成以固定长度为大小的组,页内部连续的线性地址被映射到连续的线性地址中。分页单元把RAM分成固定大小的页框。一个页框的长度和一个页的长度一致。线性地址映射到物理地址的数据结构称为页表。页表放在主存中,在启用分页单元前对页表初始化。

所有的80x86都支持分页,它通过设置cr0寄存器的PG标志启用。当PG=0时,线性地址被解释成物理地址。

常规分页:

32位地址被分成3个域:目录 最高10为;页表-中间10位;偏移量-最低12位。根据偏移量可得,每一页都包含有4KB数据。

线性地址=CR3寄存器+页目录+页表+偏移量

关于cr3寄存器,看下面物理地址扩展PAE分页机制中的内容。

页目录项和页表项下的字段:

present标志:1表示在主存中;0表示不在主存中

accesse标志:当选中的页被交换出去时,这一标志可以由操作系统使用。分页单元从不重置这个标志,而是必须由操作系统去做。

dirty标志:只应用于页表项。每当对一个页框写操作时就设置这个标志,当选中的页被交换出去时,这一标志由操作系统来使用。

Read/Write标志:包含页和页表的存取权限

User/supervisor含有访问页或页表的特权优先级。对于linux而言,只有cpl=1,才能对也寻址,cpl=0则不能寻址。

PCD和PWT标志:控制硬件高速缓存处理页或页表的方式

Page size标志:只应用于页目录项,如果1则页目录项指的是2M或4M的页框。

Global标志:只应用于页表项。用来防止常用页从TLB高速缓存中刷新出去。

扩展分页

他允许的页面大小是4MB而不是4KB,扩展分页用于吧大段连续的线性地址转换成相应的物理地址,这种情况下,内核可以不用中间页表进行转换。

此时线性地址=CR3寄存器+页目录+偏移量;偏移量22位,页目录10位;

 物理地址扩展PAE分页机制

通过设置cr4寄存器中的物理地址扩展标志激活PAE。页目录中的也大小标志PS启用大页尺寸。PAE启用是为2M。处理器管脚从32增加到64个,具有64G的寻址空间

引入一个页目录指针表PDPT的页表新级别,它由4个64位表项组成

cr3寄存器保护一个27位的也目录指针表的基址字段。PDPT在32字节上对齐,27位足以表示这种基址。

硬件中的高速缓存:基于局部性原理,为内存某一块的副本。

全相连:主存中的一行可以放在高速缓存中的任意位置

N路组相连:意味着主存中的一行可以放在高速缓存中N行的任意一行

对于写操作,采用如下两种策略:

通写:写高速缓存,并写内存

回写:只写高速缓存,只有在换出的时候才更新内存。

懒惰TLB技术:如果几个cpu正在使用相同的页表,而且必须对这些cpu上的一个TLB表项刷新,那么在某些情况下,正在运行内核线程的那些cpu上刷新就可以延迟。当某个cpu开始运行在内核线程时,内核把它设置成TLB模式。当发出清除TLB表项请求时,处于懒惰TLB模式的每个CPU都不刷新对应的表项;但是,当cpu记住他的当前进程正运行在一组页表上,而这些页表的TLB表项对用户地址是无效的。只要处于懒惰TLB模式的cpu用一个不同的页表集切换到普通进程时,硬件自动刷新TLB表项,同时内核把cpu设置成非懒惰模式。

物理内存布局:

在初始化阶段,内核必须建立一个物理地址映射来指定哪些物理地址范围对内核可用,哪些不可用(映射硬件I/O设备的共享内存或者是含有BIOS数据)。

内核将下面页框即为保留:1、在不可用的物理地址范围内的页框;2、含有内核代码和已初始化的数据结构的页框。保留的页框不能交换到磁盘上

一般来说,linux内核从第二个M开始。页框第一M主要存放BIOS加电自检期间的硬件配置和BIOS例程,也可能是特定计算机模型保留。

进程页表:0到3G线性地址,无论内核态还是线形态都可以寻址

3-4G,只有内核态才可以寻常。

当进程运行在用户态时,它产生的地址总是在0-3G之间,当进程运行在内核态时,他产生的地址总是大于3G,不过有时内核为了检索数据必须访问用户态地址空间。

内核页表:

内核维持着一组自己使用的页表,驻留在所谓的主内核页全局目录中。系统初始化后,这组页表还没被任何进程和线程使用;内核镜像刚装入到内存后,cpu仍然运行在实模式,分页功能还没有启用

第一阶段:内核创建一个有限的地址空间,包括内核的代码段和数据段、初始页表和用于存放动态结构的供128KB大小的空间。这个最小限度的地址空间仅仅能够将内核装入RAM和对初始化核心数据结构。

第二阶段:利用剩余的RAM建立页表。

当RAM小于896M的最终页表:内核页表提供把0xc00000000开始的线性地址转化为从0开始的物理地址,32位地址足以对所有RAM进行寻址,由于内核态寻址空间1G,可以直接转化。

当RAM大小在896M到4G之间时的最终页表:

这种情况下,并不把RAM全部映射到内核地址空间。linux在初始化阶段可以做的是把一个具有896M的RAM窗口映射到内核线性地址空间。如果一个程序还要对其他RAM寻址,则利用内核空间剩余的128M做映射。






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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值