搞懂Linux内存管理,仅此一篇
在Linux内核中表示内存区域的数据结构是vm_area_struct,内核将每个内存区域作为单独的内存对象管理。采用面向对象方法使VMA结构体可以代表多种类型的内存区域,包括内存映射文件和进程用户空间栈等等,这些区域的操作方法也不尽相同。 vm_area_strcut结构比较复杂,关于它的详细结构请参阅相关资料。我们这里只对它的组织方法做一点补充说明。vm_area_struct是描述进程地址空间的基本管理单元,对于一个进程来说往往需要多个内存区域来描述它的虚拟空间,如何关联这些不同的内存区域呢?大家可能都会想到使用链表,的确vm_area_struct结构确实是以链表形式链接,不过为了方便查找,内核又以红黑树(以前的内核使用平衡树)的形式组织内存区域,以便降低搜索耗时。并存的两种组织形式,并非冗余:链表用于需要遍历全部节点的时候用,而红黑树适用于在地址空间中定位特定内存区域的时候。内核为了内存区域上的各种不同操作都能获得高性能,所以同时使用了这两种数据结构。 下图反映了进程地址空间的管理模型: 进程的地址空间对应的描述结构是“内存描述结构”,它表示进程的全部地址空间,包含了和进程地址空间有关的全部信息,其中当然包含进程的内存区域。 进程内存到底是怎样分配与回收? 我们知道的一些系统调用,例如:创建进程fork(),程序载入execve(),映射文件mmap(),动态内存分配brk()等等都是需要分配内存给进程。但是这时进程获取的还不是实际物理的内存,只是虚拟内存,其实在内核中只是表示的是“内存区域”。进程对内存区域的分配最终是在内核中的do_mmap()函数上执行的(brk除外)。 内核使用do_mmap()函数创建一个新的线性地址区间。然后会将一个地址区间加入到进程的地址空间中,可能是创建一个新的区域或者是扩展以存在的内存区域。当然释放对应的内存区域是使用函数do_ummap()。 内存如何由虚变实呢? 从上面已经看到进程所能直接操作的地址都为虚拟地址。当进程需要内存时,从内核获得的仅仅是虚拟的内存区域,而不是实际的物理地址,进程并没有获得物理内存(物理页面——页的概念请大家参考硬件基础一章),获得的仅仅是对一个新的线性地址区间的使用权。实际的物理内存只有当进程真的去访问新获取的虚拟地址时,才会由“请求页机制”产生“缺页”异常,从而进入分配实际页面的函数。 该异常是虚拟内存机制赖以存在的基本保证——它会告诉内核去真正为进程分配物理页,并建立对应的页表,这之后虚拟地址才实实在在地映射到了系统的物理内存上。(当然,如果页被换出到磁盘,也会产生缺页异常,不过这时不用再建立页表了) 这种请求页机制把页面的分配推迟到不能再推迟为止,并不急于把所有的事情都一次做完(这种思想有点像设计模式中的代理模式(proxy))。之所以能这么做是利用了内存访问的“局部性原理”,请求页带来的好处是节约了空闲内存,提高了系统的吞吐率。要想更清楚地了解请求页机制,可以看看《深入理解linux内核》一书。 这里我们需要说明在内存区域结构上的nopage操作。当访问的进程虚拟内存并未真正分配页面时,该操作便被调用来分配实际的物理页,并为该页建立页表项。在最后的例子中我们会演示如何使用该方法。 物理内存怎样管理? (编辑:西安站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |