博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
物理内存的管理
阅读量:2243 次
发布时间:2019-05-09

本文共 4801 字,大约阅读时间需要 16 分钟。

物理内存的管理

在内核初始化完成后,内存管理的责任由伙伴系统承担。伙伴系统基于一种相对简单而令人吃惊的强大算法,已经伴随我们几乎40年。他结合了优先内存分配器的俩额关键特征:速度和效率。

伙伴系统的结构

nr_free指定当前内存区中空闲页块的书。free_list用于连接空闲页的。

避免碎片

1.依据可移动性组织页反碎片的工作原理如何?*不可移动页:在内存中固定位置,不能移动到其他地方。核心内存分配的大多数内存术语gia类呗*可回收页:不能直接移动,但可以删除,其内容可以从某些源重新生成。(kswapd守护进程)*可移动页可以随意地移动。注:内存并未划分可移动性不同的区。这些是在运行时形成的。

*数据结构

初始化内存域和结点数据结构

体系结构相关代码需要在启动期间建立以下信息:

*系统中各个内存域的页帧边界,保存在max_zone_pfn
*各结点帧的分配情况,保存在全局变量early_node_map中。

1.管理数据结构的创建

free_area_init_nodes首先必须分析并改写特定体系结构的代码提供的信息。
2.对各个结点创建数据结构
allocl_node_mem_map负责初始化一个简单非常中重要的数据结构。
static void __init_refok alloc_node_mem_map(struct pglist_data *pgdat);

mm/memory.c

structt page *mem_map:
初始化内存域数据结构设计的繁重工作有free_area_init_core执行,他会一次遍历结点的所有内存域。
static void __init free_area_init_core()
free_area_init_core剩余部分的任务是初始化zone结构中的各个表头,并将各个结构成员初始化为0。
*zone_pcp_init初始化该内存域的per_CPU缓冲,
*init_currently_empty_zone初始化free_area列表,并将术语该内存域所有的page实例都设置为初始默认值。

mm/page_alloc.c

static void __meminit zone_init_free_lists;
空闲页的数目(nr_free)当前仍然规定为0,折显然没有放映真实情况。

分配器API

就伙伴系统的接口而言,NUMA或UMA体系结构是没有差别的。*alloc_pages(mask, order)*get_zeroed_page(mask)*__get_free_pages(mask, order)*get_dma_pages(gfp_mask, order)有4个函数用于释放不再使用的页*free_page() 和free_pages()用于将一个或2页返回给内存管理子系统。*__free_page(addr)和__free_pages(addr,order).1.分配掩码2.内存分配宏通过使用标志、内存域修饰符和各个分配函数,内核提供了一种非常灵活的内存分配体系。get_zeroed_page的实现也没有什么困难。page_cache_alloc和page_cache_alloc_cold也是两个便捷函数。

分配页

所有API函数都追溯到alloc_pages_node,从某种意义上说,该函数是伙伴系统主要实现的发射台。

内核源代码将 __alloc_pages称之为“伙伴系统的心脏”,因为它处理的实质性的内存分配。由于“心脏”的重要性。
1.选择页
*辅助函数
设置的标志在zone_watemark_ok函数中检查,该函数根据设置的标志判断是否能从给定的内存域分配内存。
mm/page_alloc.c
int zone_watermark_ok()
try_to_free_pages只作用于包含预期内存域的结点。忽略所有其他结点。
2.移除选择的页
如果内核找到适当的内存域,具有足够的空闲也可供分配,那么还有两件事情需要完成。首先他必须检查这些也是否是连续的。
mm/page_alloc.c
static int prep_new_page()
*如果设置了__GFP_ZERO,prep_zero_page使用一个特定于体系结构的高效函数将也填充字节0.
*如果设置了_GFP_COMP并请求了多个页,内核必须将这些也组成复合页(compound page).第一个页称作首页(head page),而所有其余各页称为尾页(tail page)
本质上,在释放符合页时,改函数通过lru.prev确定也的分配阶,兵依次释放各页。
mm/page_alloc.c
static struct page *__rmqueue() {}
根据传递进来的分配阶、用于获取也的内存域、迁移类型,__rmqueue_smalles扫描页的列表,知道找到适当的连续内存块。
__rmqueue_smallest的实现不是很长。本质上,他又一个循环组成,按递增顺序遍历内存域的各个特定迁移类型的空闲也列表,知道找到合适的一项。
搜索从指定分配对应的项开始。小的内存区无用,因为分配的也必须时连续的。rmv_page_order是一个辅助函数,从业标志删除PG_buddy为,表示该页不再包含于伙伴系统中,兵将struct page的private成员设置为0
struct free_area的nr_free成员减1.

释放页

__free_pages首先判断所需释放的内存是单页还是较大的内存块?如果释放单页,则不还给伙伴系统,而是置于per-CPU缓冲中,对很可能出现在CPU高速缓存的页,则放置到热也的列表中。出于该目的,内核提供了free_hot_page辅助函数,该函数只是做一下参数转换,接下来调用free_hot_cold_page.如果free_hot_cold_page判断per-CPU缓冲中页的数目超出pcp->count,则将数量为pcp->batch的一批内存页还给伙伴系统。该策略称之为惰性合并(lazy coalescing).如果单页直接但会给伙伴系统,那么会发生合并,而为了满足后来的分配请求又需要进行拆分。因而惰性合并策略阻止了大量可能白费时间的合并操作。free_pages_bulk用于将也还给伙伴兄系统。

mm/page_alloc.c
static inline struct page * __page_find_buddy();
在__find_combined_index计算合并内存块的缩影,得结果10,因为这个2页的伙伴。

内核中部连续页的分配

我们知道物理上连续的映射对内核是最好的,但并不总能成功地使用。在分配一大块内存时,可能竭尽全力也无法找到连续的内存块。在用户空间中这不是问题,因为普通进程设计为使用处理器的分页机制,当然会降低速度并占用TLB.1.用vmalloc分配内存vmalloc是一个借口函数,内核代码使用他来分配的虚拟内存中连续但在物理内存中不一定连续的内存。*数据结构内核在管理虚拟内存中的vmalloc区域时,内核必须跟踪那些字区域被使用、那些是空闲的。
vmalloc();*addr定义了分配的字区域在虚拟地址空间中的起始地址。size表示该子区域的长度。可以根据该信息来勾画出vmalloc区域的完整分配方案。*flags存储了与该内存区关联的标志集合,这几乎是不可避免的。*VM_ALLOC指定由vmalloc产生的子区域。*VMMAP用于表示将现存pages集合映射到连续的虚拟地址空间中*VM_IOREMAP表示将几乎随机的物理内存区域应黑色到vmalloc区域中。*pages是一个指针,执行page指针的数组。*phys_addr仅当用ioremap映射了有物理地址描述的物理内存区域时才需要。*next使用内核可以将vmalloc区域中的所有子区域保存在一个单链表上。内核将其看作起始于VMALLOC_START+100的连续内存区。*创建vm_area在创建一个新的虚拟内存去之前,必须找到一个适当的位置。vm_area实例组成的一个链表,管理vmalloc区域中已经建立的各个子区域。在创建一个新的虚拟内存区之气那,必须找到一个适当的位置。mm/vmalloc.cstruct vm_struct *__get_vm_area_node();从伙伴系统分配内存时,是逐页分配,而不是一次分配一大块。这是vmalloc的一个关键方面。如果可以确信能够分配连续内存区。内核调用map_vm_area将分散的物理内存页连续映射到虚拟的vmalloc区域。该函数遍历分配物理内存页,在各级也目录/页表中分配所需的目录项/表项。flush_cache_vmap,其定义是特定于体系接哦古的。2.备选映射方法*vmalloc_32的工作方式与vmalloc相同,但会确保所有的物理内存总是可以用普通32位指针寻址。*vmap使用一个page数组作为起点,来创建虚拟连续内存区。*不同于上述的所有映射方法,ioremap是一个特定于处理器的函数,必须在所有体系结构上实现。3 释放内存有两个函数用于内核释放内存,vfree用于释放vmalloc和vmalloc_32分配的区域,而vunmap用于释放有vmap或ioremap创建的映射。void __vunmap(void *addr, int deallocate_pages);addr表示要释放的区域的起始地址,deallocate_pages直盯盯了是否与该区域相关的物理内存也返回给伙伴系统。vfree将后一个参数设置为1.,而vunmap设置为0.

内核映射

重要的是强调一下事实:内核提供了其他函数用于将ZONE_HIGH页帧显式映射到内核空间,这些函数与vmalloc机制无关。1.持久内核映射如果需要将高端页帧长期映射到内核地址空间中,必须使用kmap函数。需要映射的页用指向page的指针指定,作为函数的参数。该函数在有必要时创建一个映射,并返回数据的地址。*数据结构mm/higmem.cstruct page_address_map {}该结构用于建立page->virtual的映射。page是一个指向全局mem_map数组中的page实例的指针,virtuall指定该也在讷河虚拟地址空间中分配的位置。mm/highmem.cvoid *page_address(struct page *查找页地址page_address首先检查传递进来的Page实例在普通内存还是在高端内存。(1)从最后使用的位置喀什,反向扫描pkmap_count数组,直至找到以恶搞空闲位置。(2)修改内核的页表,将该也映射到指定为位置,但尚未更新TLB。(3)新位置的使用计算器设置为1。(4)set_page_address将该也添加到持久内核映射的数据结构。static inline void *kmap();*解除映射用kmap映射的也,如果不在需要,必须用kunmap接触映射。(1)flush_cache_kmaps在内核映射上执行刷出(2)扫描整个pkmap_count数组。(3)最后,使用flush_tlb_kernel_range函数刷出所有与PKMAP区域相关的TLB想。

2.临时内核映射

刚才描述额kmap函数不能用于中断处理程序,因为他可能进入睡眠状态。如果pkmap数组中没有空闲位置,该还拿书会进入睡眠状态,直至情形有所改善。

3.没有高端内存的计算机的映射函数

许多体系结构不支持高端内存。

转载地址:http://ciwdb.baihongyu.com/

你可能感兴趣的文章
【LEETCODE】240-Search a 2D Matrix II
查看>>
【LEETCODE】53-Maximum Subarray
查看>>
【LEETCODE】215-Kth Largest Element in an Array
查看>>
【LEETCODE】241-Different Ways to Add Parentheses
查看>>
【LEETCODE】312-Burst Balloons
查看>>
【LEETCODE】232-Implement Queue using Stacks
查看>>
【LEETCODE】225-Implement Stack using Queues
查看>>
【LEETCODE】155-Min Stack
查看>>
【LEETCODE】20-Valid Parentheses
查看>>
【LEETCODE】290-Word Pattern
查看>>
【LEETCODE】36-Valid Sudoku
查看>>
【LEETCODE】205-Isomorphic Strings
查看>>
【LEETCODE】204-Count Primes
查看>>
【LEETCODE】228-Summary Ranges
查看>>
【LEETCODE】27-Remove Element
查看>>
【LEETCODE】66-Plus One
查看>>
【LEETCODE】26-Remove Duplicates from Sorted Array
查看>>
【LEETCODE】118-Pascal's Triangle
查看>>
【LEETCODE】119-Pascal's Triangle II
查看>>
word2vec 模型思想和代码实现
查看>>