Linux进程内存管理之虚拟内存

引言

文章开始之前,我们先来思考以下两个问题:

问题1.服务器物理内存只有1个G,我的程序malloc申请超过1个G的内存能否成功,进程能否正常运行?

问题2物理内存的分配和释放是随机的,那么即使总的空余物理内存充足,连续空余的内存块不一定够大,那么程序malloc出来的连续内存是否受限?

不着急给出结论,先用示例代码跑一下,如下图所示,首先用free –m查看系统剩余内存。剩余的内存是121M。

编写简单代码,malloc 申请1GB的内存,成功则输出内存首地址,失败则输出内存不足。如下图所示,成功的申请了1GB的内存。

再次用free –m查看系统剩余内存情况。如图1中所示,第二次free –m的结果和第一次的输出完全一致。看不到有内存消耗。

用top –p [pid]命令查看进程的情况,可以看到VIRT(virtual memory usage 虚拟内存)是1035M,但是RES(resident memory usage 常驻内存)只有896KB。

 

虚拟内存

看到这里,我们要首先了解一个概念,什么是虚拟内存。参考百度百科的定义:虚拟内存计算机系统内存管理的一种技术。它使得应用程序认为它拥有连续的可用的内存(一个连续完整的地址空间),而实际上,它通常是被分隔成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换。目前,大多数操作系统都使用了虚拟内存,如Windows家族的“虚拟内存”;Linux的“交换空间”等。

实际上,为了解决上面的两个问题,Linux给每个进程都提供了一个独立的虚拟地址空间,并且这个地址空间是连续的。这样进程就可以很方便地访问内存,更确切地说是访问虚拟内存。Linux系统通过虚拟地址这一层转换机制,解决了物理内存碎片化的问题。另外,为了解决内存不足的问题,虚拟地址还支持映射到磁盘中,即Linux的swap分区。

 

Swap分区

Swap分区是指在系统的物理内存不够用的时候,把内存中的一部分空间释放出来,以供当前运行的程序使用。那些被释放的空间可能来自一些很长时间没有什么操作的程序,这些被释放的空间被临时保存到磁盘的Swap分区中,等到那些程序要运行时,再从Swap分区中恢复保存的数据到内存中。通过这种机制,程序就可以使用比实际物理内存更大的虚拟内存空间。当然,如果申请的虚拟内存地址大于剩余物理内存和剩余swap分区,会申请失败。

 

回到上面的例子,free –m显示系统还有122M的剩余内存,832M的swap分区剩余空间。加起来也只有122+832 = 954M的内存可以申请,为什么我们的程序还可以malloc成功呢?答案就在free命令的-/+ buffers/cache 里面,如下图所示,换算了buffers和cache占用的内存后,实际已使用的内存减少到了3406M,空余内存增加到了425M。

Linux系统为了提高磁盘IO效率,也设置了一层缓存。读写磁盘的内容,均会占用一部分内存空间作为磁盘内容的缓存,以便于提高下次访问的速度。而buffers就表示了CPU写到物理内存中并即将刷入磁盘的空间(cpu可以写快些,异步刷磁盘),cached就表示刚从磁盘中读入的内容占用的内存空间(供cpu下次读可以快点)。必要时,这两部分内存占用都可以释放出来供其他程序使用,故某种程序上也可以认为是空闲内存。所以实际我们可以申请的内存应该是425+832=1257M.

通过程序验证,若malloc申请1300M的内存,则会申请失败。

看到这里,最初的两个问题的答案可以得出了。

问题1答案:如果系统的剩余swap空间足够大,还是可以malloc申请成功的。如果剩余swap空间不足,则无法申请成功。

问题2答案:不会受限,进程使用的是连续的虚拟内存地址,操作系统会做虚拟地址到物理地址的转换。

 

前文中还有个疑问没有解开:为什么程序malloc了一个G的内存空间,top命令看到虚拟内存VIRT,而真实的常驻内存RES却只有800多K呢。

这个是Linux的内存延时分配机制导致的,说白了就是已申请但是未使用的内存,只不会给虚拟内存映射真实的物理内存地址的。只有当程序真的使用了虚拟内存时,才会产生一个pagefault中断,由操作系统建立虚拟内存页到物理内存页的映射关系。

我们通过以下例子来验证下这个延时分配机制。代码如下:程序申请一个G的内存,隔30s后为首地址赋值0x2,再隔10s再为偏移4000处赋值0x2,再隔10s为偏移量5000处赋值0x2。

刚malloc时占用的RES如下是896KB

首地址赋值后RES是908KB,偏移4000处赋值后还是908KB。

为偏移量5000处赋值后,RES升到了912KB.

解释如下:

操作系统管理内存的最小单位是页(page),一般操作系统默认的page大小是4096字节,即4K。可以通过命令getconf PAGESIZE进行确认。

由于内存延迟分配的缘故,刚malloc时并未对物理内存进行映射,而为首地址赋值时,操作系统分配了一个物理page,而偏移4000位置和首地址属于同一个Page范围,故占用的是同一个物理页面,不产生额外的内存占用(物理page的分配)。而偏移量5000已经是新的一个虚拟内存页了(超过了4096),故操作系统会新对应一个新的物理Page映射,故又多占用了一个Page的内存(4K)。至于为什么首地址赋值后,直接多了12KB,即3个Page的内存占用,还没有弄清楚,等高人指点。

 

 

 

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值