找回密码
 立即注册
首页 安全领域 安全板块 Linux系统安全:Linux系统内存管理的基础知识

系统安全 Linux系统安全:Linux系统内存管理的基础知识

2023-3-2 15:13:07 评论(0)
本帖最后由 海上明月 于 2023-3-2 15:14 编辑

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。
实模式特点:
a. CPU刚上电时是出于实模式之下的,CPU访问的地址就是输入的物理。
b. 实模式的物理地址=16位段基址<< 4 + 16位段偏移量
c. 实模式下对任意段都具有读写权限。
d. 实模式下可以访问的内存大小为1M(0x00000-0xfffff)

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

MMU是一种硬件电路,它包含两个部件,一个是分段部件,一个是分页部件,在此,我们把它们分别叫做分段机制和分页机制,以利于从逻辑的角度来理解硬件的实现机制。分段机制把一个逻辑地址转换为线性地址;接着,分页机制把一个线性地址转换为物理地址。
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字节的数据结构。

3.实例分析
1>实验性分析
这个是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位)
· cr3寄存器:存储页目录基地址
· 目录项:存储在页目录中的偏移量
· 页表(中间10位):存储在页表内存中的偏移量
· 页内偏移量:存储在页内的偏移量
· 页目录:每一项都是一张页表的基地址
· 页表:每一项都是一个页
· 页:存储数据的地方,每一个页对应一个数据块。
linux中使用了4级分页模型
· 页全局目录:pgd
· 页上级目录:pud
· 页中间目录:pmd
· 页表:pt

总结
本文介绍了x86架构CPU中MMU中重要得硬件机制。这是我们研究操作系统安全的基础。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

使用道具 举报

特别声明:以上内容(图片及文字)均为互联网收集或者用户上传发布,本站仅提供信息存储服务!如有侵权或有涉及法律问题请联系我们(3513994353@qq.com)。
您需要登录后才可以回帖 登录 | 立即注册
楼主
海上明月

关注0

粉丝0

帖子12

最新动态