温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

Linux系统中怎么实现内存管理

发布时间:2021-08-05 16:56:34 来源:亿速云 阅读:190 作者:Leah 栏目:编程语言

Linux系统中怎么实现内存管理,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。

Linux分段机制

背景

在8086处理器诞生之前,内存寻址方式就是直接访问物理地址。这就是直观的令人能很好理解的访问模式,cpu中的地址值就是我们要访问的内存的地址值。我们称之为cpu的实模式

由于8086处理器想要访问1MB(220)的内存,但是以前处理器只有16位(216=64kB),所以在8086时,设计师将CPU的地址总线升级为20位,但是,问题在于ALU只能处理16位数据,这时就想出了新的地址访问方法,就是分段机制。

分段机制:就是将整个一块内存分成几个段:CS, DS, SS, ES(代码段,数据段,堆栈段,附加段)。然后每个段内空间就变小,就可以对内存进行正常访问了。基本原理就是分段越多,段内需要访问的地址范围就越小。

所以,8086采用分段机制以后,物理地址的访问就变成,首先需要知道要访问的是哪个段,那个段的首地址是多少,然后在这个段内我需要访问的位置离首地址有多远。具体实现:物理地址=(段的首地址<<4) +段内偏移

因为寄存器是16位,而CPU总线为20位,所以从段寄存器中读出16位的段首地址放到CPU20位中的高16位。也就是说段首地址一定是24(16k)的倍数。然后再加上段内偏移就是我们CPU需要访问内存的实际地址。

重要问题:为什么是将段首地址向上移动4位而不是16位(整个段的长度)?

因为这里工程师进行设计的时候,只有20位的数据总线位数和16位的寄存器及ALU位数。没有20-16=4位的寄存器。所以工程师要利用16位合成20位。这里就想到了将一个16位放到20位的高16.将另一个16位放到20位的低16位。然后相加就是我们的寻址地址。这样做出现的问题就是每个段并不是独立的,而是重叠的。

举个例子。假设要访问030H地址的内存空间,可以使用段1的首地址:010H,段偏移是020H,则030H=010H+020H。

也可以使用段2的首地址:00H,段偏移是030H,则030H=000H+030H. 总之要想访问某个物理地址,只要凑出合适的段基地址和段内偏移地址,其和为该物理地址就行了。

因为段是重叠的,不同段可以访问到重叠地址的数据,所以在定义段的时候加上了段长数据来使不同段不重合。

随着时代的发展,出现了80386处理器是一个32位处理器,通常我们所说的CPU位数是指ALU可以处理数据的位数,通用寄存器的位数,数据总线的宽度中最小的一位。ALU和地址总线都是32位的,寻址空间达 4G。也就是说它可以不通过分段机制,直接访问4G的内存空间。但是为了兼容以前的访问模式,也为了方便其自己的内存访问,所以该处理器提供了两种内存访问模式:实模式和保护模式(分段机制)。所以其保留了几个分段寄存器CS,DS,SS,ES。

实模式特点:

  1. CPU刚上电时是出于实模式之下的,CPU访问的地址就是输入的物理。

  2. 实模式的物理地址=16位段基址<< 4 + 16位段偏移量

  3. 实模式下对任意段都具有读写权限。

  4. 实模式下可以访问的内存大小为1M(0x00000-0xfffff)

IA32的内存寻址机制

80386处理器提供的这种具有两种内存访问模式:实模式和保护模式。保留几个16位的分段寄存器的机制就是成为IA32架构。

在 8086 的实模式下,把某一段寄存器左移4位,然后与地址ADDR相加后被直接送到内存总线上,这个相加后的地址就是内存单元的物理地址,而程序中的这个地址就叫逻辑地址(或叫虚地址,就是CPU输出程序员写入的地址)。在IA32的保护模式下,这个逻辑地址不是被直接送到内存总线而是被送到内存管理单元(MMU)。MMU由一个或一组芯片组成,其功能是把逻辑地址映射为物理地址,即进行地址转换,如图所示。

Linux系统中怎么实现内存管理MMU是一种硬件电路,它包含两个部件,一个是分段部件,一个是分页部件,在此,我们把它们分别叫做分段机制和分页机制,以利于从逻辑的角度来理解硬件的实现机制。分段机制把一个逻辑地址转换为线性地址;接着,分页机制把一个线性地址转换为物理地址。

Linux系统中怎么实现内存管理IA32中有六个16位段寄存器:CS, DS, SS, ES,FS, GS.跟8086的段寄存器不同的是,这些寄存器存放的不再是某个段的基地址,而是某个段的选择符(Selector)。

分段机制的实现

1.GDT与GDTR的出现

随着CPU和操作系统的发展,人们对内存访问的要求越来越高,不仅仅要求能够访问到内存,还要求要对内存进行访问权限,段的大小等属性的设置与查看等。所以80386处理器与8086处理器的分段模式并非完全相同的,而是进行了相当大的改进,把分段目的从能访问到更大内存转变成了对内存的访问能进行控制,实现段保护,所以成为保护模式。

但是16位的段寄存器还是存储的信息是完全不够的,所以CPU规定操作系统必须提供一个表,这个表中存储了每个段的的段描述符(段描述符中包括了:段的基地址,段的界限,段的保护属性)。这张表就是我们称的GDT表。所以,我们就减轻段寄存器的工作,只需要其找到这张表上的对应的段描述符既可。所以,每个CPU一定有一个GDT表,同时为了找到这张表,CPU提供一个专门的寄存器来存储这张表的地址,成为GDTR寄存器。

2.段描述符(GDT表项)的解析

段描述符是一个8字节的数据结构。

Linux系统中怎么实现内存管理

3.实例分析

1>实验性分析

Linux系统中怎么实现内存管理

这个是gdt表的一部分,我们可以看到,第2段和第6段分别是存放64位下的内核代码和用户代码的段信息。其中保存了段大小,段的内存地址以及访问权限等信息。

不过,通过分析,在linux中,不同段的基地址都相同,为0.代表linux只是使用了分段机制的权限保护功能,而没有使用其访问更大内存的功能。

2>源码性分析

GDTR寄存器的值是GDT表的内存地址,是需要linux内核写入的。而GDT表示属于per cpu变量中gdt_page中存储的。per cpu变量是每个CPU独有的变量,是linux系统分配给cpu的。

Linux分页机制

分页的硬件支持

一个逻辑地址经过分段机制转换为一个线性地址之后,便需要分页单元将线性地址转换为实际的物理地址。

从80386开始,所有的80x86处理器都支持分页。是否开启分页通过设置cr0寄存器的PG标志来决定,当PG为0时,表示不开启分页,此时线性地址呗解释为物理地址。

分页机制管理的对象是固定大小的存储块,称之为(page)。分页机制把整个线性地址空间及整个物理地址空间都看成由页组成,在线性地址空间中的任何一页,可以映射为物理地址空间中的任何一页(我们把物理空间中的一页叫做一个页面或页框(page frame))。

80386中每一页的大小都为4KB,每一页的起始地址都能被4K整除12位全为0)。因此,80386把4G的线性地址空间,划分为1M个页面。

线性地址映射到物理地址的数据结构称为页表,页表存放在主存中,由内核进行适当的初始化。

线性地址转换

一个线性地址被分页机制解析为三部分:目录项(高10位),页表(中间10位),页内偏移量(最低12位)

                         Linux系统中怎么实现内存管理

  • cr3寄存器:存储页目录基地址

  • 目录项:存储在页目录中的偏移量

  • 页表(中间10位):存储在页表内存中的偏移量

  • 页内偏移量:存储在页内的偏移量

  • 页目录:每一项都是一张页表的基地址

  • 页表:每一项都是一个页

  • 页:存储数据的地方,每一个页对应一个数据块。

在linux中使用了4级分页模型

Linux系统中怎么实现内存管理

看完上述内容,你们掌握Linux系统中怎么实现内存管理的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注亿速云行业资讯频道,感谢各位的阅读!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI