深入linux内核35节.docx
《深入linux内核35节.docx》由会员分享,可在线阅读,更多相关《深入linux内核35节.docx(11页珍藏版)》请在冰豆网上搜索。
![深入linux内核35节.docx](https://file1.bdocx.com/fileroot1/2023-1/21/e2ece92f-bb12-4c61-8e2f-076cff92b28b/e2ece92f-bb12-4c61-8e2f-076cff92b28b1.gif)
深入linux内核35节
物理内存的管理
3.5.1伙伴系统的结构
系统中每个物理内存页都对应于一个structpage实例,每个内存域都关联一个structzone的实例。
order:
描述了内存分配的数量单位。
order范围0~MAX_ORDER
●第0个链表包含的内存区为单页,第1为2的一次方..........
●内存区中第1页内的链表元素,可用于将内存区维持在链表中,所以不必引入新的数据结构来管理,否则这些页不可能在同一内存区。
●主要优点之一:
管理工作较少。
●备用列表:
连接所有内存域和节点。
●内存分配原则:
在首选的内存域或节点无法满足内存分配请求时,首先尝试同一节点的另一个内存域,接下来再尝试另一个节点,直至满足要求。
3.5.2避免碎片
1、依据可移动性组织页
上图表示所有的空闲内存和非空闲内存都是连续的,而下面的图不是连续的,空闲内存最大只有一个页。
●内核的方法是反碎片。
不可移动页:
在内存中有固定位置,不能移动到其他地方。
可回收页:
不能直接移动,但可以删除,其内容可以从某些源重新生成。
可移动页:
可以随意移动。
●内核使用反碎片技术,基于将具有相同可移动性的页分组的思
●如图所示:
●数据结构:
内核定义的迁移类型
●初始化基于可移动性的分组
在内存子系统初始化期间,memmap_init_zone负责处理内存域的page实例,所有页最初都标记为可移动的!
分配内存时,如果必须“盗取”不同于预定迁移类型的内存区,内核在策略上倾向于“盗取”更大的内存区。
2、虚拟可移动内存域
●虚拟内存域ZONE_MOVABLE,必须由管理员显式激活。
●基本思想:
可用的物理内存分为两个内存域,一个用于可移动分配,一个用于不可移动分配。
●数据结构:
Kernelcore指定用于不可移动分配的内存数量;
Movablecore控制用于可移动内存分配的内存数量。
●物理内存域提取的用于ZONE_MOVABLE的内存数量必须考虑下面两种情况:
1 用于不可移动分配的内存会平均地分布到所有内存结点。
2 只使用来自最高内存域的内存。
在内存较多的32位系统上,通常为ZONE_HIGHMEM,但在64位系统上,将使ZONE_NORMAL或ZONE_DMA32。
3.5.3初始化内存域和结点数据结构
体系结构相关代码需要在启动期间建立以下信息:
1.系统中各个内存域的页帧边界,保存在max_zone_pfn中
2.个结点页帧的分配情况,保存在全局变量early_node_map中。
从内核版本2.6.10开始提供一个通用的框架,用于将上述信息转换为伙伴系统预期的结点和内存域数据结构。
在这以前,各个体系结构必须自行建立相关结构。
现在,体系结构相关代码只需要建立前述的简单结构,将繁重的工作留给free_area_init_nodes即可。
图给出了该过程概述和free_area_init_nodes的代码
3.5.4分配器API
●alloc_pages(mask,order)分配2order页并返回一个structpage的实例,表示分配的内存块的起始页
●get_zeroed_page(mask)分配一页并返还一个page实例,页对应的内存填充0。
●_get_free_pages(mask,order)和_get_free_page(mask)的工作方式与上述函数相同,但返回分配内存块的虚拟地址,而不是page实例。
●get_dma_pages(gfp_mask,order)用来获得适用于DMA的页。
注意:
分配失败的情况下,上述函数返回空指针或者0。
1、分配掩码
只要设置了_GPF_DMA就从_GPF_DMA内存域扫描。
内核定义的下列标志意义很明确:
2、内存分配宏
上述的函数主要是通过宏定义联系起来的,具体参考书本。
3.5.5分配页
alloc_pages_node只执行一个简单检查,避免分配过大的内存块。
主要工作委托给_alloc_pages,它是“伙伴系统的心脏”。
1、选择页
前几个标志判断页是否可分配时要考虑那些水印。
通过相应的标识,内存分配过程中的努力程度也有所不同。
主要包括:
内存足够时实现分配比较简单;内存不足就会唤醒守护程序kswapd,通过缩减内存缓存和页面回收获得等等;如果再次失败内核也会采取相应措施,比如采取OOMkiller机制。
2、移除选择的页
如果内核找到适当的内存域,并具有足够的空闲页可供支配,首先必须检查这些页是否是连续的,其次,必须按伙伴系统的方式从free_lists移除这些页,可能需要分解并重排内存区。
如果只分配一页,内核会进行优化,该页并不是从伙伴系统直接取得,而是取自per_cpu的页缓存。
●如果需要分配的内存块长度小于所选择的连续页范围,那么该内存块必须按照伙伴系统的原理分裂成小的块,通过expand函数。
●分配内存的最后手段:
如果遍历了所有分配阶和所有迁移类型,仍然无法满足分配请求,内核可以尝试从MIGRATE_RESERVE列表满足分配请求。
3.5.6释放页(__free_pages)
●可以看到,落脚点是__free_pages()这个函数,它执行的工作的流程图如下图所示
●可以通过一个简单的情形来模拟一下这个过程,假设现在有一个将要释放的页,它的order为0,page_idx为10。
则先计算它的伙伴10^(1<<0)=11,然后计算合并后的起始页偏移为10&~(1<<0)=10,现在就得到了一个order为1的块,起始页偏移为10,它的伙伴为10^(1<<1)=8,合并后的起始页偏移为10&~(1<<1)=8,如此推导下去,我们可以通过下图和下表更清晰地分析这个过程
3.5.7内核中不连续页的分配
3.5.8内核映射
内核提供了其它函数用于将ZONE_HIGHMEM页帧显式映射到内核空间,这些函数与vmalloc机制无关。
1.持久内核映射
图中vitualaddressspace里面的每一个格子的空间大小为4kB,及一个页的大小,该空间及虚拟空间。
pkmap_count所指代的数组的每个单位大小是4B,及int类型,该数组主要是为了对vitualaddressspace中的虚拟地址被映射多少次的计数。
2.临时内核映射原理
上文描述的持久映射因为不能用于中断处理程序,所以系统还需要一种原子执行的函数来完成任务,逻辑上称为kmap_atomic,他的主要优点是执行速度快,但是不能用于进入睡眠的代码。
但在描述临时映射之前,阐明固定映射是必要的,毕竟它是基于固定映射的。
固定映射在3.4节中有所提及。
在内核虚拟内存的划分中,最后一段FIXMAPS即为固定映射所处的虚拟地址段。
●虚拟地址的计算过程:
idx=type+KM_TYPE*smp_processor_id();
vaddr=__fix_to_virt(FIX_KMAP_BEGIN+idx);
FIX_KMAP_BEGIN来自固定映射的枚举类型
从上面的计算公式我们知道每一个临时内核映射对于不同的cpu是分开的。
如下图: