前世今生之linux内存管理


     笔者觉得学一门技术应弄清技术的历史,此话怎讲?应明白技术产生的背景,技术每次进化是为了解决哪些问题,目前是否已经够成熟等等。这样才能比较清晰掌握某一项技术。但目前太多的资料一上来便介绍技术的概念意义,原理操作,对其发展一字不提,这样就会让读者只知晓“这小子是谁”,但却不知“是谁怀胎十月生了这小子”。好的,回头来讲讲我们今天的主题:前因后果来进一步理解linux的内存管理。

一、背景
     在早期的计算机,所有的程序都是直接运行在物理内存上,需要内存便直接在物理内存分配一大块。只要所需的内存不超过计算机的物理内存,那么程序便能安全运行。但现在流行的是多任务、多进程,这种内存使用方式就很难去适应,如何更高效的分配内存,以及回收,这些问题的解决便是“内存管理”的产生的原因。

     下面举一个早期计算机系统的内存分配管理例子,以便大家更为清晰地理解以上内容:
     一台计算机物理内存为128MB,三个程序A、B、C运行在其上。A程序需要10MB内存,B程序需要110MB内存,而C程序则需20MB;假如系统同时运行A/B,那么系统则将0~10MB内存地址供A程序使用,10MB~120MB则供B程序使用。A程序时有能力直接操作B程序所占的内存,这样一来,各自都无法保证自己的运行环境是完全安全的。另外,此时如果运行C程序,那么就出现内存不够的问题(仅120MB~128MB空闲),这样一来C程序将无法被运行。

     “怎么办呢?”,“有人会说各个程序在跑,把一些暂时不被使用的数据存到硬盘中,腾出空间给C程序用,当该暂存起来的数据被使用了再从磁盘读回”。(swap技术)

     这样的内存使用方式明显导致以下问题:
      1. 进程地址空间不能隔离
     由于程序直接访问的是物理地址,那么程序所使用的内存空间都是透明的,使用0~10MB空间的A程序仍有能力访问10MB~120MB的地址,如果A程序意外对其修改,那么B程序就有可能崩溃。这样很多木马程序就可以利用这点随意篡改,系统安全性无从保障。
      2.内存使用效率低
     嗯,“把一些暂时不被使用的数据存到硬盘中,腾出空间给C程序用,当该暂存起来的数据被使用了再从磁盘读回”貌似这方法可行哦。但是仔细想想,属于A程序的0~10MB空间不够C程序使用,只能暂存B程序的内容,那么这样就带来一个新的问题“要把110MB的数据写到磁盘中,又在某一时刻再将其读回来”,众所周知,硬盘I/O效率相比内存是低下的,这样每次切换程序带来的时延是无法忍受的。     
      3.程序运行的地址无法确定
     每次程序运行,都需在内存中为其分配一块足够大的空闲空间。这样每次程序运行的地址就不是唯一,这就带来一些重定位的问题,重定位的问题确定就是程序中引用的变量和函数的地址随每次运行而变化(这点如果不明白或想更深入去了解,可阅读编译原理方面的资料)

二、产生与演变
     内存管理便是为此而生,”计算机系统的任何问题都是引入中间层来解决“,隔离进程空间, 高效管理内存分配回收, 解决程序运行重定位。
     目前主流的内存管理方法即在程序与物理内存之间引入”虚拟内存“这个概念。虚拟内存位于程序和物理内存之间,程序只能看到虚拟内存,而无法直接访问物理内存。每个程序都有自己独立的物理空间,这样起到进程隔离的效果。进程地址空间是指虚拟地址。
     既然在程序与物理地址之间增加”虚拟地址“,那么就要解决”虚拟地址“到”物理地址“的映射;因为程序最终肯定是要运行在物理内存上。对此主要有”分段“和分页两种技术。
     分段(Segmentation):最开始使用的一种方法,基本思路是将程序所需的内存地址空间大小的虚拟空间映射到某段物理地址空间。如下图:


     每个程序都有独立的虚拟的进程地址空间,程序A和程序B的虚拟地址都是从0x00000000开始,我们将两块大小形同的虚拟地址空间和实际的物理空间一一映射,即虚拟地址空间中的每一字节对应实际物理地址的每一字节,这个映射需要硬件来完成,中间需要软件去配置这些硬件。

     ”进程地址空间隔离“、”程序运行重定位“的问题就可以得到解决,A/B程序都有自己的虚拟地址空间(在32bit linux系统中,虚拟地址空间未0~4GB),各自映射到不同的物理地址,这样程序各自运行在自己的0~4GB空间内,数据和代码存放的位置不需要重定位。

     很明显,分段机制是一个很大的进步,解决了前两个问题。但对于内存效率问题仍无促进作用。这种内存映射机制仍以程序为单位,但内存不足时仍需将整个程序交换到磁盘,效率低下。”怎么才能让内存使用高效起来?“,根据程序的局部性运行原理,程序运行时,在某一时刻只使用很少量的数据(内存空间),我们可以将管理的对象单位切割成更小粒度并改变映射方法;此时也许就会想到Linux中常提到的Buddy算法和Slab内存分配机制,另一种”虚拟地址“到”物理地址“的映射方法随之诞生。

     分页:该机制将内存地址空间分割成若干个大小很小且固定的单位”页“,每一页的大小由内存决定;举个例子,linux的EXT文件系统将磁盘分割成一个个block单位,以提高内存与磁盘存储空间的利用率。假如每个block大小为1MB,当存储1KB的文件时,就不得不分配1mB的空间,这样明显造成很大浪费,合理的设置block大小在一定程度上可提高磁盘利用率;同理,内存管理也一样:

     Linux中”页“大小一般为4KB,我们将进程地址空间按页分割,把常用的数据和代码页表加载到内存,不常用的代码和数据保存至磁盘,以下图为例:

     进程A和进程B的虚拟地址空间被映射到不连续的物理地址空间(这意味着某一时刻连续物理地址空间不够,存在大量不连续的地址空间,及内存碎片化时,如果没有这种机制,程序就无法正常运行);除此之外,两进程的虚拟地址空间也可映射到同一物理地址,以实现数据的共享。
     进程A的虚拟页VP2和VP3被交换至磁盘;当需被使用时,linux内核产生缺页中断异常,异常响应程序将其重新读到内存以供使用。
这是分页机制的概貌。实际上,linux中的分页机制实现远比这复杂,涉及到页全局目录,页中级目录,页表等几级的分页来实现,但基本原理便是这样;同时,分页机制需要硬件MMU(Memory Manage Unit)支持,主要负责虚拟地址转换为虚拟页基址+虚拟页偏移量,从而找到对应的物理页。
     好了,今天的内容到这里,内存管理的前世今生大家应该有个清晰的理解了吧。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值