本文主要是介绍虚拟存储中的地址映射及编程演示,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
摘要
为了保证CPU执行指令时可正确访问存储单元,需将用户程序中的虚拟地址转换为运行时由机器直接寻址的物理地址,这一过程称为地址映射。
虚存中的地址映射一般都是从虚拟地址映射到物理地址。
第一章 地址映射的由来
1.1 为什么使用虚拟地址
在操作系统的发展过程中,虚拟地址的出现起到了十分重要的作用。前期操作系统中的地址基本都是直接使用物理地址,这就造成了以下几个问题:
(1)安全风险
每个进程都可以访问0-4G的任意的内存空间,这也就意味着任意一个进程都能够去读写系统相关内存区域,如果是一个木马病毒,那么他就能随意的修改内存空间,让设备直接瘫痪
(2)地址不确定
众所周知,编译完成后的程序是存放在硬盘上的,当运行的时候,需要将程序搬到内存当中去运行,如果直接使用物理地址的话,我们无法确定内存现在使用到哪里了,也就是说拷贝的实际内存地址每一次运行都是不确定的,比如:第一次执a.out时候,内存当中一个进程都没有运行,所以搬移到内存地址是0x00000000,但是第二次的时候,内存已经有10个进程在运行了,那执行a.out的时候,内存地址就不一定了
(3)效率低下
如果直接使用物理内存的话,一个进程就是作为一个整体(内存块)操作的,如果出现物理内存不够用的时候,我们一般的办法是将不常用的进程拷贝到磁盘的交换分区中,好腾出内存,但是如果是物理地址的话,就需要将整个进程一起拷走,这样,在内存和磁盘之间拷贝时间太长,效率较低。
综上所述,直接使用物理地址隐患较多,此时便提出了虚拟地址的概念。
1.2 虚拟地址
虚拟地址是Linux内核虚拟出来的地址,经由MMU内存管理单元映射到实际的物理地址。MMU是实际的管理内存的硬件。其具体内容和功能将在下面进行阐述。
第二章 地址映射的原理
2.1 虚拟地址空间
我们知道,使用虚拟地址之后,程序可以使用一系列相邻的虚拟地址来访问物理内存中不相邻的大内存缓冲区。
还可以使用一系列虚拟地址来访问大于可用物理内存的内存缓冲区。当物理内存的供应量变小时,内存管理器会将物理内存页(通常大小为 4 KB)保存到磁盘文件。数据或代码页会根据需要在物理内存与磁盘之间移动。
进程可用的虚拟地址范围称为该进程的“虚拟地址空间”。每个用户模式进程都有其各自的专用虚拟地址空间。 对于 32 位进程,虚拟地址空间通常为 2 GB,范围从 0x00000000 至 0x7FFFFFFF。对于 64 位进程,虚拟地址空间为 8 TB,范围从 0x000’00000000 至 0x7FF’FFFFFFFF。一系列虚拟地址有时称为一系列“虚拟内存”。
图1:虚拟地址空间
2.2 原理
介绍完虚拟地址空间后,我们就可以大概讲解一下地址映射的具体内容。目前主要有两种地址映射方式:分页和分段
2.2.1 分段映射
分段机制就是把虚拟地址空间中的虚拟内存组织成一些长度可变的称为段的内存块单元。虚拟地址由段号和段内地址组成,虚拟地址到实存地址的变换通过段表来实现。
每个段由三个参数定义:段基地址、段限长和段属性。
其中,段的基地址、段限长以及段的保护属性存储在一个称为段描述符的结构项中。段可以用来存放程序的代码、数据和堆栈,或者用来存放系统数据结构。
系统中所有使用的段都包含在处理器线性地址空间中。
图2:虚拟地址到物理地址的变换过程
具体步骤如下:
(1).程序执行时,从PCB中取出段表始址和段表长度,装入段表寄存器。
(2). 由分段地址变换机构将逻辑地址自动分成段号和段内地址。
例:7310D=0001|110010001110B=1C8EH
段号为1,位移量为C8EH=3214D
(3). 将段号与段表长度进行比较,若段号大于或等于段表长度,则表示本次访问的地址已超越进程的地址空间,产生越界中断。
(4). 将段表始址与段号和段表项长度的乘积相加,便得到该段表项在段表中的位置。
(5). 取出段描述子得到该段的起始物理地址。
(6). 检查段内位移量是否超出该段的段长,若超过,产生越界中断。
(7). 对该段的存取控制进行检查。
(8). 将该段基址和段内地址相加,得到实际的物理地址。
例:0001|110010001101B
起始地址17500D+段内地址3214D=20714D
分段映射优缺点:
分页对程序员而言是不可见的,而分段通常对程序员而言是可见的,因而分段为组织程序和数据提供了方便。与页式虚拟存储器相比,段式虚拟存储器有许多优点:
(1) 段的逻辑独立性使其易于编译、管理、修改和保护,也便于多道程序共享。
(2) 段长可以根据需要动态改变,允许自由调度,以便有效利用主存空间。
(3) 方便编程,分段共享,分段保护,动态链接,动态增长。
因为段的长度不固定,段式虚拟存储器也有一些缺点:
(1) 主存空间分配比较麻烦。
(2) 容易在段间留下许多碎片,造成存储空间利用率降低。
(3) 由于段长不一定是2的整数次幂,因而不能简单地像分页方式那样用虚拟地址和实存地址的最低若干二进制位作为段内地址,并与段号进行直接拼接,必须用加法操作通过段起址与段内地址的求和运算得到物理地址。因此,段式存储管理比页式存储管理方式需要更多的硬件支持。
分段映射能够解决安全隐患、地址不确定问题,但是对于效率问题仍然没有很好的解决。因此提出了新的方法:分页方法。
2.2.2 分页映射
分页映射的基本思想是用户程序的地址空间被划分成若干固定大小的区域,称为“页”,相应地,内存空间分成若干个物理块,页和块的大小相等。可将用户程序的任一页放在内存的任一块中,实现了离散分配。
分页存储管理的地址机构:
15 12 11 0
页号P 页内位移量W
页号4位,每个作业最多2的4次方=16页,表示页号从00001111(24-1),页内位移量的位数表示页的大小,若页内位移量12位,则2的12次方=4k,页的大小为4k,页内地址从000000000000111111111111
与此同时,引入了页表的概念:分页系统中,允许将进程的每一页离散地存储在内存的任一物理块中,为了能在内存中找到每个页面对应的物理块,系统为每个进程建立一张页面映射表,简称页表。页表的作用是实现从页号到物理块号的地址映射。
具体步骤如下:
(1) 程序执行时,从PCB中取出页表始址和页表长度,装入页表寄存器PTR。
(2)由分页地址变换机构将逻辑地址自动分成页号和页内地址。
例:11406D=0010|110010001110B=2C8EH
页号为2,位移量为C8EH=3214D
或11406 DIV 4096=2
11406 MOD 4096=3214
(3) 将页号与页表长度进行比较,若页号大于或等于页表长度,则表示本次访问的地址已超越进程的地址空间,产生越界中断。
(4)将页表始址与页号和页表项长度的乘积相加,便得到该页表项在页表中的位置。
(5)取出页描述子得到该页的物理块号。
(6) 对该页的存取控制进行检查。
(7)将物理块号送入物理地址寄存器中,再将有效地址寄存器中的页内地址直接送入物理地址寄存器的块内地址字段中,拼接得到实际的物理地址。
例:0010|110010001101B
1110|110010001101B=EC8EH=60558D
或 14*4096+3214=60558D
分页映射优缺点:
优点:有效提高内存利用率。
缺点:需要将程序全部装入内存
2.3两者的主要区别
1、分页机制会使用大小固定的内存块,而分段管理则使用了大小可变的块来管理内存。
2、分页使用固定大小的块更为适合管理物理内存,分段机制使用大小可变的块更适合处理复杂系统的逻辑分区。
3、段表存储在线性地址空间,而页表则保存在物理地址空间。
由上,人们又分析出了一种折中方案:段页式
2.4段页式
段页式存储管理系统中,作业的地址空间首先被分成若干个逻辑分段,每段都有自己的段号,然后再将每段分成若干个大小相等的页。对于主存空间也分成大小相等的页,主存的分配以页为单位。
段页式系统中,作业的地址结构包含三部分的内容:
段号 页号 页内位移量
程序员按照分段系统的地址结构将地址分为段号与段内位移量,地址变换机构将段内位移量分解为页号和页内位移量。
为实现段页式存储管理,系统应为每个进程设置一个段表,包括每段的段号,该段的页表始址和页表长度。每个段有自己的页表,记录段中的每一页的页号和存放在主存中的物理块号。
具体过程:
(1)程序执行时,从PCB中取出段表始址和段表长度,装入段表寄存器。
(2)由地址变换机构将逻辑地址自动分成段号、页号和页内地址。
(3)将段号与段表长度进行比较,若段号大于或等于段表长度,则表示本次访问的地址已超越进程的地址空间,产生越界中断。
(4)将段表始址与段号和段表项长度的乘积相加,便得到该段表项在段表中的位置。
(5)取出段描述子得到该段的页表始址和页表长度。
(6)将页号与页表长度进行比较,若页号大于或等于页表长度,则表示本次访问的地址已超越进程的地址空间,产生越界中断。
(7)将页表始址与页号和页表项长度的乘积相加,便得到该页表项在页表中的位置。
(8)取出页描述子得到该页的物理块号。
(9)对该页的存取控制进行检查。
(10)将物理块号送入物理地址寄存器中,再将有效地址寄存器中的页内地址直接送入物理地址寄存器的块内地址字段中,拼接得到实际的物理地址。
段页式管理优缺点:
优点:
(1) 它提供了大量的虚拟存储空间。
(2) 能有效地利用主存,为组织多道程序运行提供了方便。
缺点:
(1) 增加了硬件成本、系统的复杂性和管理上的开消。
(2) 存在着系统发生抖动的危险。
(3) 存在着内碎片。
(4) 还有各种表格要占用主存空间。
段页式存储管理技术对当前的大、中型计算机系统来说,算是最通用、最灵活的一种方案。
第三章 编程演示
要查看一个进程的虚拟地址空间的内存布局,需要设置阻塞。如果没有设置阻塞,当./a.out按下去后,程序执行的速度非常快以至于来不及查看,所以需要设置阻塞。
算法1-1:查看虚拟地址空间
#include<stdio.h>
int a;
static int b=10;
int main(int argc,char* argv[],char* envp[]){int a=10;//stackchar* p=(char*)malloc(sizeof(char));//heapprintf("a=%d,p=0x%x\n",a,p);//动态链接库,*.sogetchar();//设置阻塞getchar();return 0;
}
END
结果如下:
cat /proc/进程id/maps 输出进程虚拟地址空间的布局:
参考文献
[1] z4a1c2. 虚拟地址空间映射到物理地址空间[OL].
https://blog.csdn.net/z4a1c2/article/details/73457375,2017年6月19日
[2] 续航fff. 分段和分页机制[OL].
https://blog.csdn.net/qq_37924084/article/details/78360003,2018年3月29日
[3] 林纲 张治辉 Linux内核地址映射机制分析及实现[J].计算机与数字工程 第33卷(2005)第7期
这篇关于虚拟存储中的地址映射及编程演示的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!