Linux操作系统内存管理
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Linux操作系统内存管理
摘要:
Linux支持虚拟内存, 就是使用磁盘作为RAM的扩展,使可用内存相应地有效扩大。
核心把当前不用的内存块存到硬盘,腾出内存给其他目的。
当原来的内容又要使用时,再读回内存。
这对用户全透明:运行于Linux的程序只看到大量的可用内存而不甘心哪部分在磁盘上。
当然,读写硬盘比真的内存慢(慢千倍),所以程序运行较慢。
用做虚拟内存的这部分硬盘叫对换空间。
Linux可以使用文件系统中的普通文件或单独的分区作为对换空间。
对换分区更快,但对换文件更易于改变大小(无须对硬盘重分区)。
如果知道要多少对换空间,应该用对换分区;如果不能确认,可以先用对换文件,用一段时间后再根据所需空间建立对换分区。
Linux允许同时使用多个对换分区和/或对换文件。
即如果偶尔需要更多的对换空间,可以随时建立一个额外的对换文件。
Linux是一个遵循POSIX(Portable Operating System Interface)标准的操作系统,它继承了UNIX系统优秀的设计思想,拥有简练、容错强、高效而且稳定的内核。
此外Linux还具备其他操作系统所不能比拟的优点。
①:完全免费;
②:内核源代码完全公开。
Linux内核拥有一个功能完备的内存管理子系统,它增加了对NUMA(非均匀存储结构)体系结构的支持并且使用了基于区(ZONE)的物理内存管理方法,从而保持了物理上连续分布、而逻辑上统一的内存模式和传统的共享内存编程模型,使得系统的性能得以极大的扩展。
这样Linux不仅能够满足传统的桌面应用,而且还能满足高端服务器市场的需要。
目前,Linux不仅在Internet服务器上表现出色,而且还可以胜任大型数据库系统的服务器。
关键字:虚拟内存,物理内存管理,虚拟内存管理
一、Linux存储管理的基本框架
Linux内核采用虚拟页式存储管理,采用三次映射机制实现从线性地址到物理地址的映射。
其中PGD为页面目录,PMD为中间目录,PT为页面表。
具体的映射过程为:
⑴从CR3寄存器中找到PGD基地址;
⑵以线性地址的最高位段为下标,在PGD中找到指向PMD的指针;
⑶以线性地址的次位段为下标,在PMD中找到指向PT的指针;
⑷同理,在PT中找到指向页面的指针;
⑸线性地址的最后位段,为在此页中的偏移量,这样就完成了从线性地址到物理地址的映射过程。
32位的微机平台如Intel的X86采用段页式的两层映射机制,而64位的微处理器采用三级分页。
对于传统的32位平台,Linux采用让PMD(中间目录)全0来消除中间目录域,这样就把Linux逻辑上的三层映射模型落实到X86结构物理上的二层映射,从而保证了Linux对多种硬件平台的支持。
二、Linux对虚拟内存的管理
虚拟内存不仅可以解决内存容量的问题,还可以提供以下附加的功能:大地址空间;进程保护;内存映射;灵活的物理内存分配;共享虚拟内存。
Linux对虚拟内存的管理以进程为基础。
32位的线性地址映射的4G的虚拟空间中,从0XC0000000到0XFFFFFFFF的1G空间为所用进程所共享的内核空间,每个进程都有自己的3G用户空间。
Linux的虚拟内存管理需要各种机制的支持,首先内存管理程序通过映射机制把用户程序的逻辑地址映射到物理地址,在用户程序运行时时如果发现程序中要用的虚拟地址没有对应的物理地址,就发出请页要求①:如果有空闲的内存可供分配,就请求分配内存②,并把正在使用的物理页记录在页缓存中③,如果没有足够的内存分配,就调用交换机制,腾出一部分内存④⑤。
另外在地址映射中要通过TLB(翻译后援存储器)来寻找物理页⑧,交换机制中要用到交换缓存⑥,并且把物理页内容交换到交换文件中也要修改页表来映射文件地址⑦。
一个进程的虚拟地址映射靠三个数据结构来描述:mm_struct、
vm_area_struct、page。
其中mm_struct结构用来描述一个进程的虚拟内存;vm_area_struct描述一个进程的虚拟地址区域,在这个区域中的所有页面具有相同的访问权限和一些属性;page描述一个具体的物理页面。
当进程通过系统调用动态分配内存时,Linux首先分配一个vm_area_struct 结构,并链接到进程的虚拟内存链表,当后续指令访问这一内存区域时,产生缺页异常。
系统处理时,通过分析缺页原因、操作权限之后,如果页面在交换文件中,则进入do_page_fault()中恢复映射的代码,重新建立映射关系。
如果因为页面不再内存中,则Linux会分配新的物理页,并建立映射关系。
当物理内存出现不足时,就需要换出一些页面。
Linux采用LRU(Least Recently Used最近最少使用)页面置换算法选择需要从系统中换出的页面。
系统中每个页面都有一个“age”属性,这个属性会在页面被访问的时候改变。
Linux 根据这个属性选择要回收的页面,同时为了避免页面“抖动”(即刚释放的页面又被访问),将页面的换出和内存页面的释放分两步来做,而在真正释放的时候仅仅只写回“脏”页面。
这一任务由交换守护进程kswapd完成。
free_pages_high,free_pages_low是衡量系统中现有空闲页的标准,当系统中空闲页的数量少于free_pages_high,甚至少于free_pages_low时,kswapd进程会采用三种方法来减少系统正在使用的物理页的数量。
①调用shrink_mmap()减少buffer cache 和page cache的大小;②调用shm_swap()将system V共享内存页交换到物理内存;③调用swap_out()交换或丢弃页。
三、Linux对物理内存的管理
Linux2.4内核加入了对NUMA的支持,如果系统是NUMA结构的处理机系统,则物理内存被划分为三个层次来管理:存储节点(Node),管理区(Zone),页面(Page)。
处理器的本地内存组成的区域叫做一个节点(Node),它通过
pglist_data数据结构来描述。
各个节点的物理内存根据不同的作用又分为ZONE_DMA、ZONE_NORMAL、ZONE_HIGH,ZONE_DMA面积小,且专供DMA使用,ZONE_NORMAL则供大多数的程序使用,对于ZONE_HIGH仅仅只有页面缓存以及用户进程能够使用该区域的空间。
每个管理区对应一个free_area数组来组织空闲页面队列,该数组的每一项描述某一种页块的信息,第一个元素描述大小为1
页的内存块的信息,第二个元素描述大小为2 页的内存块的信息,依此类推,所描述的页块大小以 2 的倍数增加。
free_area 数组的定义如下:Typedef struct free_area_struct{
Struct list_head free_list;
Unsigned int *map;
}free_area_t;
list_head是一个双向指针结构,在这里用于将物理页块结构mem_map_t 连结成一个双向链表,而map则是记录这种页块组分配情况的位图,例如,位图的第N位为1,表明第N个页块是空闲的。
页分配代码使用向量表free_area来分配和回收物理页。
系统初始化时,free_area数组也被赋了初值。
也就是说,系统中所有可用的空闲物理页块都已经被加到了free_area数组中。
Linux使用Buddy最先匹配算法来进行页面的分配和回收,并且必须按2的幂次方进行分配。
比如要分配大小为2k的空闲块,如果系统中有足够的空闲块,页面分配代码首先在free_area中查找相应大小的空闲块,如果找到则分配。
如果没有则查找下一尺寸(2倍于请求大小)的页面块,继续这一过程直到找到可以分配的页面,按要求分配之后,将剩余的空闲块仍然按照2的幂次方划分后链入适当的空闲块中。
与分配算法相反,页面回收时总是试图将相邻的空闲页面组合成更大尺寸的空闲块。
这里先给出“伙伴”要满足的三个条件:①两个块大小相同;②两个块物理地址连续;③两个块从同一大块中分离出来。
在用户释放内存时,判断“伙伴”是否是空闲块。
若否,则只要将释放的空闲块简单的插入相应的free_area中。
若是,则需要在free_area中删除其伙伴关系,然后再判断合并后的空闲块的伙伴关系,依次重复,直到归并后的空闲块没有伙伴关系或合并到最大块时将其插入到free_area中。
四、缓存和刷新机制
与存取(真正的)内存相比,从磁盘读是很慢的另外,在相对短的一端时间里,多次读硬盘相同的部分是很常见的。
例如,你可能先读了一封电子邮件,然后回复时又将它读入编辑器,然后复制它到一个文件夹时又用邮件程序读它。
或者,考虑命令ls 可能被系统上的很多用户多么频繁地使用。
只从磁盘读一次信息,并保持在硬盘中,知道不再需要,除了第一次读,其他都会较快。
这就叫磁盘缓存disk buffering,用于此目的的内存叫buffer cache。
不幸的是,由于内存是有限且缺乏的资源,buffer cache一般不会足够大(大到能够装下所有人可能用到的数据)。
当cache满时,最长时间不用的数据将被丢弃,内存释放给最新的数据。
磁盘缓冲也用于写操作。
要写的数据经常马上又被读(例如一个源代码文件保存到文件中后又被编译器读出),所以将要写的数据放在缓冲里是个好主意。
另外,只将数据放如cache而不马上写到磁盘,写操作的程序执行速度更快。
写操作然后可以在后台完成,而不降低其他程序的速度。
许多操作系统有buffer caches (即使名称不同),但并非都根据上述原理。
有些是透写write-through: 数据马上写到磁盘(当然也同时写到cache) 不马上写的cache叫回写write-back。
回写比透写更有效,但也更容易出错:如果系统崩溃,或电源突然掉电,或软盘在cache回写前被取出,那么cache中改变的数据将丢失。
这可能意味着文件系统is not in full working order, 可能由于未写数据包含了系统记录信息的重要的变化。
因此,千万不要不经过正常的关闭过程直接关闭电源, 或没有unmount就取出软盘(如果是mount的),或什么程序还在用着软盘,或软盘灯还在闪。
sync 命令刷新缓冲,即强制将所有未写数据写回磁盘,如果要确保所有数据安全回写,可以用它。
传统的UNIX系统中,有个update 程序在后台运行,它每30秒运行一次 sync ,所以通常无须使用sync 。
Linux有一个另外的守侯程序bdflush ,它克服了sync 有时因磁盘I/O负荷太重(因为频繁的操作)而导致有时系统突然呆住的问题。
Linux下,bdflush 由update 启动。
一般无须考虑它,但如果bdflush 偶尔因为什么原因死了,核心会给出警告,此时应该手工启动它(/sbin/update )。
cache并不真正缓冲文件,而是块,就是磁盘I/O的最小单元(Linux下,一般是1kB)。
这样,所有的目录、超级块、其他文件系统记录数据和无文件系统磁盘都可以被缓冲。
cache的效果决定于其大小。
太小的cache几乎无用;它只能cache很少的数据,而可能在被重用前就被清除了。
大小有赖于有多少数据被读写,相同的数据的存取频度。
唯一的方法是实验。
如果cache是固定大小,那么不应该太大,否则,会由于空闲内存空间太小而使用swap(也很慢)。
为了最有效地使用真实内存,Linux自动使用所有空闲内存作为buffer cache,当程序需要更多内存时,自动减少cache。
Linux下,对cache使用无须做任何工作,它完全是自动的。
除了要正常关闭系统和取出软盘,无须关心cache。
为了更好的发挥系统性能,Linux采用了一系列和内存管理相关的高速缓存机制:
①缓冲区高速缓存:包含了从设备中读取的数据块或写入设备的数据块。
缓冲区高速缓存由设备标示号和块索引,因此可以快速找到数据块。
如果数据可以
在缓冲区中高速缓存中找到,则不需要从物理块设备上读取,从而加快了访问速度。
②页高速缓存:这一高速缓存用来加速对磁盘上的映像和数据访问,它用来缓存某个文件的逻辑内容,并通过文件VFS索引节点和偏移量访问。
当页从磁盘读到物理内存时,就缓存在页高速缓存。
③交换高速缓存:用于多个近程共享的页面被换出到交换区的情况。
当页面交换到交换文件之后,如果有进程再次访问,它会被重新调入内存。
五、小结
Linux是近年来应用的比较多的一个操作系统,广泛应用于各个行业。
而且由于全世界计算机爱好者的支持,Linux也成为世界上发展最快的操作系统。
在Linux2.6内核中,对存储管理子系统进行了一系列的改进,提高了系统的可扩展性,包含了对大型服务器如NUMA服务器和Intel服务器的良好支持。
此外,Linux2.6还提供了对无MMU的支持。
可见Linux正在不断的加强对高端服务器领域以及嵌入式领域的支持。
Linux在其发展过程中不断的在完善和优化内存管理单元的功能和性能。
针对具体领域,我们可以根据自己的需要定制Linux内核。
而内存管理单元作为Linux操作系统的核心部分,在整个系统的运行过程中发挥着举足轻重的作
用。
参考文献:
[1] 毛德操、胡希明 Linux内核源代码情景分析[M] 杭州:浙江大学出版社,2003;
[2] 陈莉君Linux操作系统内核分析[M] 北京:人民邮电出版社,2003;
[3] 彭晓鸣王强 Linux核心源代码分析[M] 北京:人民邮电出版社,2000;。