unix虚拟存储器详解

       昨晚和舍友星光夜谈到12点多,今天一大早就要睡觉。你要问谈的什么这么来劲,我只能说既不是美女也不是电影,而是Linux下面的虚存管理机制!尴尬我们是不是很用功,哈哈哈。今天抽时间来对unix下面的虚存机制总结一下,就当温故而知新吧!

       大家可能经常听说什么段页式内存管理,虚拟内存,虚地址等,然后没学过操作系统的朋友听着一片晕乎!今天我们就来深扒一下unix系统下的内存管理机制得意

       首先很多朋友会问,为什么要有虚拟存储器呢?比如我单片机写程序的时候就直接用物理内存地址啊,没见得什么不妥的。确实,对应某个单一程序而言,在保证内存空间大小满足的情况下,确实不需要什么虚拟内存机制,直接用就OK了。但是对于一个多进程运行的系统而言,虚拟内存机制实在是必不可少的。我们知道一个系统的进程是与其他进程共享CPU和内存资源的,但是共享内存会形成一些问题。如果没有虚存机制,我们来看一下下面两种情况:

1.当多个进程需要的空间大于内存容量,那么它们当中必然会有某些进程无法运行,超过空间的进程就会崩溃了。

2.当A进程不小心写了B进程使用的地址空间时,那么B进程在执行时行为会无法预测,太危险了。

一直这样做也会使得物理存储器特别容易损坏。

       为了更好地有效管理存储器并且不出错,现在的主流系统(unix/windows)都提供了虚拟内存管理机制,将逻辑存储地址与实际物理地址区分开来。并且虚存为每个进程都提供一个独立的,同等大小的私有地址空间。

一. 虚拟寻址











图中的MMU是CPU芯片中用于将虚拟地址转换成物理地址的地址翻译器。

首先忽略高级缓存L1,我们只看处理器,MMU,还有物理存储器。

处理器只管寻址虚拟地址,交给MMU,MMU翻译成物理地址,到物理存储器中取指令或数据。

早期的PC和现在的数字信号处理器比如DSP,单片机等用的还是直接物理寻址,没有虚存机制。不过目前的计算机系统大都提供虚存机制。


二. 地址空间

如果是虚存寻址,那么虚存空间可以完全和物理存储在逻辑上独立开来,容量也可以不受实际物理存储器的大小限制。那么实际虚存容量究竟有多大呢?以前在本科上课的时候听老师说过是4G,当时就郁闷过,如果一个进程所需空间大于4G怎么办,而且4G也不大啊。。。然后发现老师说的并不准确,确实虚存的大小要CPU地址总线长度有关系,如果是32位的CPU,那么2的32次方是4G。如果是64位的CPU(目前PC基本都是64位的,32位绝种了),那么2的64次方是4G*4G(太大了,天文数字)。。。因为太大了,远远超过目前实际所需,所以CPU生产厂商限制了地址位长


三.虚拟存储器结构

概念上讲,虚拟存储器(VM)被组织为一个由存放在磁盘(注意是磁盘!!!生气)上的N个连续的字节大小的单元组成的数组。每个字节都有一个唯一的虚拟地址,这个唯一的虚拟地址是作为到数组的索引。

虚拟存储器以虚拟页为单位,每个页有P个字节。

物理内存也是以页为单位区别的,每个页也是P个字节,不过是叫物理页。

虚拟存储机制是用物理内存做为缓存的,下面我们来看一下:












任意时刻,虚拟页只能有三种状态:

1.未分配:VM系统还未分配的页。意思是没有任何数据,还处于无用状态,也不占用任何磁盘空间。

2.已缓存到物理存储器中。

3.已分配,但为缓存到物理存储器中。

值得注意的是,虚拟页与物理页并不是按照顺序对应的,可以说基本不可能按照顺序对应,也没有这个必要。任何两个进程的物理页可能都是交替分布在内存中的,我们无需考虑。


下面我们来谈谈页表(PT:page table),页表是用来表示每个虚拟页的实际状态。到底这块页是分配了没有,如果分配了缓存了没有,缓存在哪里?这些信息就记录在页表中,所以页表非常的重要。页表将虚拟页映射到物理页。每次地址翻译器(MMU)转换成物理地址时,都会读取页表。操作系统负责维护页表的内容,以及在磁盘与DRAM之间来回传送页。下面我们结合图来看一下:














页表的每个页表项(PTE)由一个有效位和一个N位地址字段组成。

1)当有效位是1时,表示为已缓存。后面是物理页地址。

2)当有效位是0时,后面字段非0,则为已分配,未缓存。

3)当有效位是0时,后面字段是0,则为为分配。


下面我们结合CPU送出虚拟地址到MMU,然后看MMU是如何构造出物理地址的。

有三种情况值得我们注意:

1.页命中情况

MMU接受CPU传来的虚拟地址后,将它作为一个索引,可以将地址的前几位做简单运算变为虚拟页,从而找到PT中的PTE2,有效位为1,表示已缓存,所以用PTE2中的物理存储器地址构造出该字的物理地址。具体的构造过程很简单:PTE2中的地址是物理页的首地址,只要将它加上CPU传来的虚拟地址在虚拟页中的偏移量就可以了。


2.缺页情况

如果DRAM主存不命中称为缺页。即该页还未被缓存。比如CPU引用VP0中的字,那么在页表中有效位为0,未缓存,则会触发一个缺页异常。缺页异常调用内核中的缺页异常处理程序,该程序根据一定的算法选出一个牺牲页,比如PP2中的VP2。当VP2发现已经被修改过了,那么内核就会将它拷贝回磁盘。并将PTE5修改有效位为0,将后面的地址该为磁盘的VP2地址。接下来将VP0拷贝到存储器中的PP2中,然后修改PTE6。现在VP0已经在主存中了。接着将导致缺页的虚拟地址重新发送给MMU,这样就可以页命中了。


3.分配页面

当操作系统分配一个新的虚拟存储页时,通过在磁盘上创建空间,并更新页表,使它指向磁盘上的这个新创建的页面,从而分配新的虚拟页。


有了虚拟存储的页机制,可以极大地

1)简化链接

每个进程拥有相同的存储格式(稍后我们会看到),不管代码和数据实际存放在物理存储器的何处。

文本区总是从虚地址的0x08048000处开始,栈总是从地址0xbfffffff开始,共享库代码总是从地址0x40000000开始,内核代码和数据总是从地址xc0000000开始。这样的一致性可以很大的简化了链接器的设计和实现,这些可执行文件是独立于物理存储器中代码和数据的最终位置的。

2)简化共享

这个很容易理解了,比如很多程序都有相同的代码,都同用一个内核代码,这样可以将各个进程中适当的虚拟页面映射到相同的物理页面上,达到共享的目标。

3)简化加载

ELF可执行文件中的.text和.data借是相邻的。为了加载这些节到新创建的进程中,可以将页表项指向目标文件中的适当位置,操作系统从进程虚存文本区分配一个连续的虚拟页面区域。值得注意的是,加载器并不讲磁盘中的代码区拷贝到物理存储器中,而只是简单修改页表指向磁盘文件特定区域。当文件执行时,再利用缺页机制将其拷贝到主存中。这就是所谓的懒人模式,极大的提高的系统的效率。


再谈页表

目前为止,我们只用一个进程的页表进行分析,实际上系统中可能有多个进程,每个进程都有属于自己的页表。下面我们来算一下32位地址空间,4KB页面,和一个4字节的PTE,我们需要4GB/4KB*4B = 4MB,一个进程就要用掉4MB的内存当页表。这显然是我们不能接受的,设想一下64位的地址空间需要多大的页表。。。。幸好unix系统为我们提供了多级页表机制。即将页表分成多级,我们已两级页表看一下具体的划分。我们结合图来看一下:
























第一级页表中每个页表项PTE负责4MB的组块,这里每个组块都是由1024个连续的4KB页面组成。如果一个组块没有被分配,那么该表项为0,如果组块中有一个页表分配,那么第一级页表中的该表项指向第二级页表基地址。第二级页表负责具体页表的映射状态。这样算一下第一级页表需要4KB,第二级页表也只需要4KB。一般只有一级页表才需要总是在主存中。VM可以在需要时创建第二级页表,这样减少了主存的压力。只有最经常使用的第二级页表才需要常驻内存中。


基本的地址翻译过程已经讲完了,不过需要告诉各位的是实际中系统的地址翻译要比这个更加复杂,因为加上了L1,L2,L3各级缓存,同时也有TLB页表项缓存器,下面贴一下貌似是core-i7的地址翻译过程:



































具体感兴趣的同学可以去了解一下大笑。有了刚刚讲的这些基础,看起来应该不困难。


五.Linux虚拟存储器区域

Linux将虚拟存储器组织成一下段的集合。段是一些虚存中连续的组块。有代码段,数据段,堆,共享库段,以及用户栈等段。每个分配的虚拟页总是存在在一个特定段中,不属于某个段的虚拟页是不存在的,而且不能被进程引用。

多说没用,我来贴一下著名的linux进程虚拟存储器:

































放这么大的图是让大家看清楚一点,我调整了大小,整好一个web页面可以看全(我用的是台式机偷笑)。

说到这个图,就不得不说一下linux内核中表示虚存的结构,了解了可以方便大家今后深入内核研究。

内核在系统中为每个进程维护一个单独的任务结构(tast_struct)。任务结构中的元素指向内核运行该进程所需的所有信息(如:PID等)。

task_struct中的一个条目指向mm_struct。它描述了虚存中的当前状态。我们最感兴趣的是两个字段:pgd和mmap,pgd指向页面目录表的基址,而mmap指向一个vm_area_structs的链表,其中每个vm_area_structs都描述了当前虚存空间中的一个区域。当内核运行这个进程时,它就将pgd存放在PDBR控制器中。

我在这里就说vm_area_structs中每个元素代表的意思了,我们看个图就一目了然。





















是不是很清楚大笑


六. 存储器映射

Linux通过将一个虚拟存储区域与一个磁盘上的对象关联起来,已初始化这个虚拟存储区域的内容,这个过程称为存储器映射(memory mapping)。

Linux中有两种映射方式:

1.文件系统中的普通文件:一个区域可以映射到一个普通磁盘文件的连续部分。文件被文成页面大小的片,每一个片包含一个虚拟页面的初始内容。因为懒人模式进行页调度,也就是要用的时候才换进主存中,所以初始化的虚拟页面并没有与主存有任何的交互。有人问,“万一区域没有页面大怎么办”,那么不足的部分就补0。


2.匿名文件:一个区域也可以映射到一个由内核创建的匿名文件上,匿名文件是由内核创建的,包含的全是二进制0。CPU第一次引用这样一个区域内的虚拟页面时,内核就在物理存储器中找到一个合适的牺牲页,如果该页面被修改过了,就将这个页面换出来,并修改这个牺牲页的PTE,然后用0覆盖牺牲页面,并更新虚拟页的PTE(有效位置1,后面地址更物理内存地址),表示这个页面驻留在存储器中的。注意和文件映射不同的是,在磁盘和存储器之间没有实际的数据传送,因为这个原因,所以被映射到匿名文件区域中的页面,也可以被叫成请求二进制0的页。


这段内容很重要!!!无论在哪种情况中,一旦一个虚拟页面被初始化了,它就在一个由内核维护的专门的交换空间(swap)之间换来换去。在任何一个时刻,交换空间都限制着当前运行着的进程能够分配的虚拟页面总数。(内存容量+swap空间大小)/4KB = 最大能分配的虚拟页面数。假如A进程malloc了1GB空间,B进程也malloc了1GB空间,如果内存大小为1.5GB,swap为1.5GB,那么B进程在申请堆空间的时候需要讲A进程中的一部分换出到swap(磁盘上)上面,然后申请1GB大小给B。再考虑极端一点的情况,如果swap大小为300MB,那么对不起,内存+swap才1.8G,肯定不够A,B进程的总虚拟页面数,结果可想而知,就是系统崩溃。。。。

如果大家对这部分有疑问的话,可以看一下这个帖子,是我以前问的,学校各种大神解答的非常完美,相信对你会有很大的帮助!存储器映射问题解答


七. 共享对象与私有对象

关于这个共享机制其实不用我多啰嗦了,它的必要性大家都知道。不过我要说的是在linux中对私有对象的共享是采用一种叫copy on write的巧妙技术实现的,有必要说一下。比如:父进程fork一个子进程后,父进程中原来的段子进程也完全一样。开始的时候子进程与父进程的某些虚地址对应相同的物理地址空间。当子进程对私有对象进行改变时,就会将原来私有对象先copy到别的物理地址上,再做修改。这个对父进程没有任何影响。















根据上图来理解一下上面的那段话吧,感觉会不会好一点。其实我说的只是个大概的实现,具体的实现如下:

一个私有对象在物理存储器中只保存有私有对象的一份拷贝。比如,图a中的情况,其中两个进程将一个私有对象映射到它们虚拟存储器的不同区域,但是共享这个对象同一个物理拷贝。对于每个映射私有对象的进程,相应私有区域的页表条目都被标记为只读,并且区域结构被标记为私有的写时拷贝。只要没有进程对它进行写访问,那么这些进程继续共享物理存储器中对象的一个单独拷贝。然而,只要一个进程试图写私有区域内的某个页面,那么这个写操作就会触发一个保护故障。

当故障处理程序注意到保护异常是由于进程试图写私有的写时拷贝区域中的一个页面而引起的,它会在物理存储器中创建这个页面的一个新拷贝,更新页表目指向这个新拷贝,然后恢复这个页面的可写权限,如图b所示。当故障处理程序返回时,CPU重新执行这个写操作,现在在新创建的页面上这个写操作就可以正常执行了。

这种写方式可以最充分地利用稀有的物理存储器资源。


好啦,码完了,大功告成。这里帮助大家建立一系列的基本概念,并没有深入内核分析,所以各位有时间的话一定极力推荐结合源码来看看具体的虚存实现。大笑



Hello, my name is Linus. And I am your God!








  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值