linux内核解析之--内存管理
扫描二维码
随时随地手机看文章
本文主要介绍了linux内核的内存管理机制。什么是内存管理机制?内存管理主要负责完成当进程请求内存时给进程分配可用的内存,当进程释放内存时,回收相应的内存,同时负责跟踪系统中相应内存的使用状态。
Linux采用页式内存管理,页是物理内存管理的基本单位。但严格来说Linux采用的是段页式内存管理,既分段也分页。内存映射的时候,先确定对应的段,确定段基地址,段内分页,再找到对应的页表项,确定页基地址,再由逻辑地址的低位确定的页偏移量就能找到最终的物理地址。但Linux中的所有段地址都是0,即所有的段是相同的,之所以有段的概念是因为Linux为了符合硬件体系。所以Linux实际采用的是页式内存管理,但段的概念在内核中确实存在。
1、物理内存的管理
Linux中首先将内存分为若干个节点,每个节点下面又可分为1~3个区,每个区下面会有若干个页。
(1)节点
内存节点主要是依据CPU访问代价不同而划分的。一个CPU对应一个节点。内核数组node_data[]形式组织节点,存储的为struct page_data_t指针来描述内存分区。
(2)区
内核以struct_zone来描述内存分区。内核将所有的物理页分为3个区:ZONE_DMA、ZONE_NORMAL、ZONE_HIGHMEM。 ZONE_DMA区中包含的页可以用来进行DMA操作,即直接内存访问操作,通常为物理内存的起始16M。ZONE_NORMAL区包含的页是可以进行正常的内存映射的页物理内存为16~896M。ZONE_HIGHMEM区称为“高端内存”,该区所包含的页不可以进行永久映射,即不可以永久映射到内核地址,物理内存896M以后的。
高端内存的边界为896M的原因:32为Linux系统中虚拟内存空间为0-4G,3G-4G为内核态。为了应对内核映射超过1G,Linux采取的策略:内核地址空间的896M采用固定映射,映射方法:虚拟地址-3G=物理地址,只能映射896M,即3G~3G+896M,剩余的128M(3G+896M~4G)采用动态映射。 Linux下以struct zone结构体来表示一个区,在该结构体中变量struct page *zone_mem_map用来管理该区下的内存映射表。
(3)页
每一个物理页框都使用一个数据结构struct page来描述,该结构体中的lru变量构建用于LRU页面置换的链表。在页框空闲情况下,该成员变量用于构建伙伴算法、链表同等大小的空闲内存块。大多数32bit的操作系统的页大小为4KB。
2、伙伴算法
Linux采用的是伙伴(Buddy)算法对物理内存进行管理。伙伴机制是操作系统的一种动态存储管理算法,该算法通过不断平分较大的空闲内存块来获得较小的空闲内存块,直到获得所需的内存块。当内存释放时,该算法尽可能的合并空闲块。该算法要求内存块的分配和合并都是以2的幂次方为单位。 在“区”内存结构体struct zone中有一struct free_area类型的数组free_area[],数组最大为12个元素。数组的下标k对应着固定大小2^k个页框空闲内存区域的双向链表头。当需要空闲块为4(即2^2)个页框时则查找free_area[2],如果没有合适的,则查找free_area[3],直到找到合适的。
3、slab分配器
Linux中引入slab是为了减少对伙伴算法的调用,采用slab分配器来减少频繁分配和释放内存数据结构的开销,同时减少了碎片的产生。slab分配机制是基于伙伴算法之上实现。 slab是基于一组对象缓存,把不同对象划分为caches(物理内存),每个cache保存一种类型的对象,每个cache由一个或者多个slab组成,每个slab包含一个或者多个page组成。
每个slab处于3中状态之一,即full、partial和empty(分别是满、部分满、空),其中满状态的slab没有任何可分配的空闲对象。当请求空闲对象时则从部分满和空的slab中分配。
Linux内核中的cache以结构体kmem_cache_s来表示,结构体中变量lists中存储的为三个链表分别对应于slab的三种状态。 总结来说,当为一对象申请内存时,首先查找到该对象的cache,然后查找cache中的slab列表,分配空闲内存。当释放该对象内存时,则返回给该对象对应的slab。这样伙伴算法就不需要频繁的进行分配和合并操作。
4、虚拟内存
(1)逻辑地址->线性地址->物理地址的转换过程
逻辑地址即程序指令的地址,线性地址指页式转换前的地址(虚拟地址),物理地址则是物理内存中的地址。
一个逻辑地址由两部份组成,段标识符: 段内偏移量。段基址确定它所在的段居于整个存储空间的位置,偏移量确定它在段内的位置。Linux中由于段基址都是0,所以逻辑地址和线性地址相同。线性地址再通过MMU进行转换到物理地址,这个过程下面重点讲下。 Linux也是内存管理使用三级表结构:页目录、页中间目录、页表。一个活动任务都有一个页目录,大小一般为一页,页目录必须在内存中。页中间目录可以跨越多个页。页表同样可以跨越多个页,对应具体的页框。具体过程如下图:
(2)页面置换算法
Linux中页结构体的组织方式为双向循环链表。Linux中页面置换算法基于时钟算法机制实现,页结构体page中有一变量count专门用来计算页面被引用的次数。每当页面被访问一次时,count加1。在Linux后台,Linux周期性地扫描全局页池,并且当它在内存中的所有页间循环时,将扫描的每一页的count减1。age越大则使用频率越高。最终内核通过最近未使用(LRU)算法进行页面置换。
5、高速缓存
Linux使用了一系列的高速缓存相关的内存管理技术来提高性能。此处的高速缓存并非
是物理缓存,而是软件方法。Linux中主要包括以下几个缓存:
(1)Buffer Cache,包括了用于块设备驱动程序的数据缓冲区。这些缓存区固定(一般512B),包括从块设备要读取的数据和要写入块设备的数据。操作时先查看缓冲区。
(2)Page Cache,用来加快对磁盘上映像和数据的访问。用来缓存文件的逻辑内容,一次缓存一页。
(3)Swap Cache,只有改动过的(或脏)页才存在交换文件中,只要交换文件没有再次修改,下次这些页需要交换出时就不需要再写到交换文件中。
(4)Hardware Cache,常见方法是在处理器中PTE的高速缓存。这种情下处理器不需要直接读取页表,需要时把页表放在缓存区中。CPU有转换表缓冲区(TLB),来快速查找置换页表。