初识Linux内存

一.内存的地址

身为程序员活动多或少都要和地址打交道,地址作为我们访问内存单元的一种方式,我们要分清以下三种不同的地址。

1.逻辑地址:众所周知我们用的大部分语言到最后都会被编译成机器语言,而逻辑地址就是机器语言指令中用来操作数或者一个指令的地址,没一个逻辑地址都由一个段和偏移量所组成,其中偏移量指明了从段开始的地方到实际地址之间的距离,学过stm32的朋友可能会倍感熟悉。

2.线性地址:线性地址也叫虚拟地址,是一个32位的无符号的整数,可以表示高达4GB的地址,线性地址通常用十六进制表示。

3.物理地址:其主要用于内存芯片级内存单元寻址,即我们内存的实际地址,而X86物理地址由32位或36位无符号整数表示里边把它替换成了一个比较复杂的电路,叫MMU(内存管理单元):它包含两个部件,一个分段部件,完成逻辑地址到线性地址的转换;一个分页部件,完成线性地址到物理地址的映射。

二.硬件中的分段

从80286模型开始,Intel微处理器以两种不同的方式执行地址转换,这两种方式分别称为实模式和保护模式。

1.实模式:是指8086CPU的寻址方式,寄存器大小,指令用法等,是用来反应CPU在该环境下如何工作的概念。实模式下操作系统和用户程序属于同一特权级,并不会区别对待。用户程序所引用的地址都是指向真实的物理地址,也就是说逻辑地址等于物理地址。这也是实模式会被保护模式淘汰的原因,最主要是安全隐患的问题。

2.保护模式:保护模式是一种80286系列和之后的x86兼容CPU的运行模式。保护模式有一些新的特性,如存储器保护、标签页系统以及硬件支持的虚拟内存。这样的话物理地址不能直接被程序访问,程序内部的地址(虚拟地址)需要被转化成物理地址之后再去访问,而程序对此不需要进行干涉,它只负责运行即可。

3.段选择符和段寄存器

如上文所述一个逻辑地址由两部分组成一个段和偏移量,这里的段实际上是一个16位长的字段,称为段选择符,而偏移量是一个32位长的字段。为了加快逻辑地址到线性地址的转换80x86系统提供了一种不能被程序员所设置的寄存器,段寄存器,来供6个可以编程的段寄存器使用。段寄存器唯一的目的就是存放段选择符。这些段寄存器分别为cs(代码段寄存器)它包含一个两位的字段以指明cpu当前的特权0最高3最低,而我们Linux里只有0和3即内核态和用户态,ss(栈段寄存器),ds(数据段寄存器,指向包含静态数据段或全局数据段),es,fs,gs,做一般用途可以指向任意数据段。每当一个段选择符被装到段寄存器里,相应的段描述符就由内存装入对应的非编程cpu寄存器。从这时开始对这个段的逻辑地址转化就可以不访问主存中的GDT(全局描述表)或LDT(局部描述符表),处理器只要访问存放段描述符的cpu寄存器即可。

        而什么是段描述符呢,它描述了段的特征,每个段由8个字节的段描述符表示,其存放于GDT(全局描述表)或LDT(局部描述符表)中。在Linux中存在代码段描述符,数据据段描述符,任务状态段描述符(TSSD)这个段用于保存处理器寄存器的内容,它只能出现在GDT(全局描述表)中,局部描述符表描述符(LDTD)这个段代表了一个包含LDT(局部描述符表)的段,它也只能出现在GDT(全局描述表)中。

4.分段单元

上图显示了一个逻辑地址是怎样转换成一个线性地址的。

1)先检查TI段决定段描述符保存在哪一个描述符表中。

2)将Index字段的值乘以8于TI段取出的内容相加得到段描述符

3)在将段描述符+逻辑地址的偏移量得到线性地址

如上就完成了逻辑地址到线性地址的转换。

三.linux中的分段

 分段可以给每一个进程分配不同的线性地址空间,而分页可以把同一线性地址空间映射到不同的物理空间。而Linux里更多的用到的是分页方式。每个单核处理器只有一个GDT(全局描述表),而多核处理器每个cpu对应一个GDT(全局描述表)。所有的GDT(全局描述表)存放在cpu_gbt_table数组中在源码arch/i386/kernel/head.S。每个GDT包含18个段描述符,其中任务段(TSS)都顺序的存放在init_tess数组中Linux不允许用户态下进程访问TSS段。3个局部线程存储(TLS)段这种机制允许多线程应用程序最多使用3个局部于线程的数据段set_thread_area()创建(TLS)段,get_thread_area()撤销(TLS)段。

四.硬件中的分页

线性地址被分为固定长度为单位的组,称为页。页内部连续的线性地址被映射到物理地址中,分页单元把所有的RAM分成固定长度的页框(物理页),每一个页框包含一个页,页框是主存的一部分,把线性地址映射到物理地址的数据结构称为页表,也存放在主存中,在启用分页单元前要对其进行初始化。

1.缺页异常:通常是进程A通过CPU访问虚拟地址VA,通过MMU找到对应的物理地址,当内存页在物理内存中没有对应的页帧或者存在但无对应的访问权限,在这种情况下,CPU就会报告一个缺页的错误。其分为

硬件缺页: 此时物理内存中没有对应的页帧,需要CPU打开磁盘设备读取到物理内存中,再让MMU建立VA和PA的映射

软缺页: 此时物理内存中存在对应的页帧,只不过可能是其他进程调入,发生缺页异常的进程不知道,此时MMU只需要重新建立映射即可,无需从磁盘写入内存,一般出现在多进程共享内存区域

无效缺页: 比如进程访问的内存地址越界访问,空指针引用就会报段错误等

2.常规分页

32位的线性地址被分为3个域:目录(Directory)最高10位,页表(Table)中间10位,偏移量(Offset)最低12位。

3.MMU

MMU是Memory Management Unit的缩写,即内存管理单元,又称分页内存管理单元它是一种负责处理cpu的内存访问请求的硬件。

从上面可以看出,一个虚拟地址被分成从左至右四个位段。第一个位段索引顶级页目录中一个项,该项指向一个中级页目录,然后用第二个位段去索引中级页目录中的一个项,该项指向一个页表,再用第三个位段去索引页表中的项,该项指向一个物理页地址,最后用第四个位段作该物理页内的偏移去访问物理内存。这就是 MMU 的工作流程。

五. Linux的分页

通过前文我们得知Linux采用了四级分页模型1)页全局目录pte_t  2)页上级目录pmd_t  3)页中间目录 pud_t 4)页表pgd_t。

内核提供了许多宏和函数用于读或修改页表,这里不在一一罗列。

1.内存布局:在初始化阶段,内核必须构建一个物理地址映射来指定哪些物理地址可用,一般来说Linux内核安装在RAM中从物理地址0x00100000开始的地方。在启动过程中,内核询问BIOS了解物理内存大小,随后执行machine_specific_memory_setup()函数,用于建立物理地址映射。

2.进程页表‘

0x00000000到0xbfffffff的地址无论用户态还是内核态都可寻址

0xc0000000到0xffffffff,只有内核态进程才能寻址

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值