Linux内存寻址

本文介绍了Linux内核中80286处理器的内存寻址方式,包括实地址模式和虚地址保护模式。在虚地址保护模式下,通过段选择符进行地址转换,实现了对存储空间的保护和越界访问的避免。Linux虽然使用了分段机制,但通过特殊设置使其简化,实际操作中更依赖于分页机制来实现虚拟存储器管理,以适应不同处理器平台并提高可移植性。
摘要由CSDN通过智能技术生成

/*读《深入理解linux内核》第二章 内存寻址 读书笔记*/

以80286处理器为例,因为80286在8086基础上引入存储管理中的虚存管理机制。通过“虚地址”和“保护”两重功能对存储器管理提供了支持, 加强了对多用户/多任务运行的管理能力。以后的80x86基本都是这种方式。(8086为何要分段:16位寄存器形成20位地址空间,使用分段来寻址。)

下图为地址转换过程


1、首先是逻辑地址-->虚拟地址,分为实地址模式(8086方式兼容,让操作系统自举)与虚地址保护模式。

实地址模式:

将存储器逻辑上划分为每64K为一个段

实际访问单元地址(物理地址) = 段基地址*16(左移4位) + 段内偏移量


虚地址保护模式:

段寄存器存放段选择符, 而非段基地址。该模式主要针对在多任务机制中的存储管理。

1. 虚地址保护模式的基本概念

两个方面的含义

(1) 虚地址: 程序设计者可以寻址一个比实际物理地址空间(16M)大得多的虚存空间(1000M)。

(2) 保护:对存储空间的(数据和程序)保护。

*地址空间上的保护避免多任务机制下的越界访问

*特权级的保护,比如防止应用软件修改系统软件或数据

*访问权限的保护,如可读或可读/写、可执行或可读/可执行等

2.虚地址到实地址转换关键:段选择符(作用:刻划存储段的属性(比如一个段的保护属性), 并提供虚地址到实地址转化的信息)

基本过程如下:

i

具体过程:

1)根据逻辑地址的段选择符*8(因为段描述符大小为8字节,所以相对于段描述符表地址的偏移量要*8,跨过一个段描述符,与linux红黑树颜色表示类似),由TI选择是GDTR(0)、LDTR(1),将之前的结果与寄存器相加得到段描述符地址。(段寄存器内容被改变时才执行这一步)

2)将偏移地址与段描述符Base字段相加得到线性地址。

Linux中的分段:

实际上2.6版本的linux只有在80X86结构下才使用分段。(IA32结构必须使用段机制)

Linux的设计人员让段的基地址为0,而段的界限为4GB,这时任意给出一个偏移量,则等式为“0+偏移量=线性地址”,也就是说“偏移量=线性地址”。另外由于段机制规定“偏移量 < 4GB”,所以偏移量的范围为0H~FFFFFFFFH,这恰好是线性地址空间范围,也就是说虚拟地址直接映射到了线性地址,我们以后所提到的虚拟地址和线性地址指的也就是同一地址。看来,Linux在没有回避段机制的情况下巧妙地把段机制给绕过去了。

由于IA32段机制还规定,必须为代码段和数据段创建不同的段,所以Linux必须为代码段和数据段分别创建一个基地址为0,段界限为4GB的段描述符。不仅如此,由于Linux内核运行在特权级0,而用户程序运行在特权级别3,根据IA32的段保护机制规定,特权级3的程序是无法访问特权级为0的段的,所以Linux必须为内核和用户程序分别创建其代码段和数据段。这就意味着Linux必须创建4个段描述符——特权级0的代码段和数据段,特权级3的代码段和数据段。

Linux在启动的过程中设置了段寄存器的值和全局描述符表GDT的内容,段的定义在include/asm-i386/segment.h中,共32个段:

/*内核代码段,index=2,TI=0,RPL=0*/

#define GDT_ENTRY_KERNEL_BASE 12
#define GDT_ENTRY_KERNEL_CS (GDT_ENTRY_KERNEL_BASE + 0)
#define __KERNEL_CS (GDT_ENTRY_KERNEL_CS * 8)

/*内核数据段, index=3,TI=0,RPL=0*/
#define GDT_ENTRY_KERNEL_DS (GDT_ENTRY_KERNEL_BASE + 1)
#define __KERNEL_DS (GDT_ENTRY_KERNEL_DS * 8)

/*用户代码段, index=4,TI=0,RPL=3*/

#define GDT_ENTRY_DEFAULT_USER_CS 14
#define __USER_CS (GDT_ENTRY_DEFAULT_USER_CS * 8 + 3)
/*用户数据段, index=5,TI=0,RPL=3*/
#define GDT_ENTRY_DEFAULT_USER_DS 15
#define __USER_DS (GDT_ENTRY_DEFAULT_USER_DS * 8 + 3)  //用于DS寄存器

从定义看出,没有定义堆栈段,实际上,Linux内核不区分数据段和堆栈段,这也体现了Linux内核尽量减少段的使用。因为没有使用LDT,因此,TI=0,并把这4个段都放在GDT中, index就是某个段在GDT表中的下标。内核代码段和数据段具有最高特权,因此其RPL为0,而用户代码段和数据段具有最低特权,因此其RPL为3。可以看出,Linux内核再次简化了特权级的使用,使用了两个特权级而不是4个。


2、虚地址-->物理地址,分页

总体过程(摘自深入理解计算机系统)(VA:虚拟地址VPN:虚拟地址页号PTE:页表项PTEA:页表地址PA:物理地址)


具体地址转换过程:


有了以上计算机系统认识,来看linux中的分页

Linux中的分页:

Linux主要采用分页机制来实现虚拟存储器管理。这是因为:

· Linux的分段机制使得所有的进程都使用相同的段寄存器值,这就使得内存管理变得简单,也就是说,所有的进程都使用同样的线性地址空间(0~4G)。

· Linux设计目标之一就是能够把自己移植到绝大多数流行的处理器平台。但是,许多RISC处理器支持的段功能非常有限。为了保持可移植性,Linux采用三级分页模式而不是两级,这是因为许多处理器都采用64位结构的处理器,在这种情况下,两级分页就不适合了,必须采用三级分页。

为此,Linux定义了三种类型的页表:

· 总目录PGD(Page Global Directory)

· 中间目录PMD(Page Middle Derectory)

· 页表PT(Page Table)

Linux所定义的一些主要数据结构,其分布在include/arch_name/目录下的page.h,pgtable.h及pgtable-2level.h三个文件中。

Linux内存初始化启动分页机制参考:Linux内存初始化之一启用分页机制

更多参考:

简述x86寻址演变

分页机制及Linux中的初步表示

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值