关键词:
目录
伙伴关系基础
free_area[]数组中各个元素的索引也解释为阶,用于指定对应链表中的连续内存区包含多少页帧。第0个链表包含的内存区为单页(2的0次方),第一个链表管理的内存区为两页(2的1次方),第三个管理的内存区为4页,依次类推。
- order阶通常设置为11,这就意味着一次可请求的最大页数是2的11方=2048
- free_area空闲页的列表;
- 伙伴不必是彼此连接的;
查看有关伙伴系统当前状态信息,各个内存区域中每个分配阶中空闲项的数目,从左到右,阶依次升高
# cat /proc/buddyinfo
Node 0, zone DMA 0 0 0 1 2 1 1 0 1 1 3
Node 0, zone DMA32 5202 1301 575 315 221 105 48 22 8 4 656
Node 0, zone Normal 6912 5437 143 5020 2337 954 400 91 24 10 1
避免碎片
1,依据可移动性组织页
无论空闲页在物理内存中是如何分布,应用程序看到内存似乎总是连续的。对于内核来说,碎片是一个问题。
反碎片的工作原理
- 不可移动页:在内存中有固定位置,不能移动到其他地方。核心内核分配的大多数内存属于该类别
- 可回收页:不能直接移动,但可以删除,其内容可以从某些源重新生成。例如,映射文件的数据类别属于该类别。kswapd守护进程会根据可回收页访问的频繁程度,周期性释放此类内存。另外,内存短缺时也可以发起页面回收。
- 可移动页:可以随意地移动。属于用户空间应用程序的页属于该类别,他们是通过页表映射的。
root@ubuntu:~# cat /proc/pagetypeinfo
Page block order: 9
Pages per block: 512
Free pages count per migrate type at order 0 1 2 3 4 5 6 7 8 9 10
Node 0, zone DMA, type Unmovable 0 0 0 1 2 1 1 0 1 0 0
Node 0, zone DMA, type Movable 0 0 0 0 0 0 0 0 0 1 3
Node 0, zone DMA, type Reclaimable 0 0 0 0 0 0 0 0 0 0 0
Node 0, zone DMA, type HighAtomic 0 0 0 0 0 0 0 0 0 0 0
Node 0, zone DMA, type CMA 0 0 0 0 0 0 0 0 0 0 0
Node 0, zone DMA, type Isolate 0 0 0 0 0 0 0 0 0 0 0
Node 0, zone DMA32, type Unmovable 5 3 4 5 6 1 0 0 1 1 0
Node 0, zone DMA32, type Movable 5075 1219 534 262 192 100 47 21 6 2 656
Node 0, zone DMA32, type Reclaimable 122 79 37 48 23 4 1 1 1 1 0
Node 0, zone DMA32, type HighAtomic 0 0 0 0 0 0 0 0 0 0 0
Node 0, zone DMA32, type CMA 0 0 0 0 0 0 0 0 0 0 0
Node 0, zone DMA32, type Isolate 0 0 0 0 0 0 0 0 0 0 0
Node 0, zone Normal, type Unmovable 0 10 0 1465 1433 744 391 85 19 10 1
Node 0, zone Normal, type Movable 6269 5299 115 3415 862 200 4 2 2 0 0
Node 0, zone Normal, type Reclaimable 335 137 30 126 42 10 5 4 3 0 0
Node 0, zone Normal, type HighAtomic 0 0 0 0 0 0 0 0 0 0 0
Node 0, zone Normal, type CMA 0 0 0 0 0 0 0 0 0 0 0
Node 0, zone Normal, type Isolate 0 0 0 0 0 0 0 0 0 0 0
Number of blocks type Unmovable Movable Reclaimable HighAtomic CMA Isolate
Node 0, zone DMA 1 7 0 0 0 0
Node 0, zone DMA32 12 1500 16 0 0 0
Node 0, zone Normal 505 1859 196 0 0 0
2、虚拟可移动内存域
根据可移动性组织页是防止物理内存碎片的一种可能方法,内核还提供了一种阻止该问题的手段:虚拟内存域ZONE_MOVABLE。 基本思想:可用的物理内存划分为两个内存域,一个用于可移动分配,一个用于不可移动分配。这会自动防止不可移动页向可移动内存域引入碎片。内核中不连续页的分配
物理上连续的映射对内核是最好的,但并不总能成功地使用。在分配一大块内存时,可能竭尽全力也无法找到连续的内存块。在用户空间中这不是问题,因为普通进程设计为使用处理器的分页机制,当然这会降低速度并占用TLB。在 IA-32 系统中,紧随直接映射的前 892 MiB 物理内存,在插入的 8 MiB安全隙之后,是一个用于管理不连续内存的区域。这一段具有线性地址空间的所有性质
每个vmalloc分配的子区域都是自包含的,与其他vmalloc子区域通过一个内存页分隔
用vmalloc分配内存
vmalloc是一个接口函数,内核代码使用它来分配在虚拟内存中连续但在物理内存中不一定连续的内存。 特别是在设备和声音驱动程序中void *vmalloc(unsigned long size);
数据结构
内核在管理虚拟内存中的vmalloc 区域时,内核必须跟踪哪些子区域被使用、哪些是空闲的。为此定义了一个数据结构,将所有使用的部分保存在一个链表中。struct vm_struct
struct vm_struct *next;
void *addr;
unsigned long size;
unsigned long flags;
struct page **pages;
unsigned int nr_pages;
phys_addr_t phys_addr;
const void *caller;
;
- addr:定义了分配的子区域在虚拟地址空间中的起始位置,size表示该子区域的长度。
- flags:存储了与该内存区关联的标志集合。它只用于指定内存区类型,可选值有以下3个VM_ALLOC指定由vmalloc产生的子区域;VM_MAP用于表示将现存的pages集合映射到连续的虚拟地址空间中; VM_IOREMAP:表示将几乎随机的物理内存区域映射到vmalloc区域中,这是一个特定于系统结构的操作。
- page是一个指针,指向page指针的数组,每个数组成员都表述一个映射到虚拟地址空间中物理内存页的page实例
- phys_addr仅当用ioremap映射了由物理地址描述的物理内存区域时才需要。
- next使得内核可以将vmalloc区域中的所有子区域保持在一个单链表上。
将物理内存页映射到vmalloc区域
vmalloc分配内存区的调用关系
核心源码
static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask,
pgprot_t prot, int node)
struct page **pages;
unsigned int nr_pages, array_size, i;
const gfp_t nested_gfp = (gfp_mask & GFP_RECLAIM_MASK) | __GFP_ZERO;
const gfp_t alloc_mask = gfp_mask | __GFP_NOWARN;
const gfp_t highmem_mask = (gfp_mask & (GFP_DMA | GFP_DMA32)) ?
0 :
__GFP_HIGHMEM;
nr_pages = get_vm_area_size(area) >> PAGE_SHIFT;
array_size = (nr_pages * sizeof(struct page *));
/* Please note that the recursion is strictly bounded. */
if (array_size > PAGE_SIZE)
pages = __vmalloc_node(array_size, 1, nested_gfp|highmem_mask,
PAGE_KERNEL, node, area->caller);
else
pages = kmalloc_node(array_size, nested_gfp, node);
if (!pages)
remove_vm_area(area->addr);
kfree(area);
return NULL;
area->pages = pages;
area->nr_pages = nr_pages;
for (i = 0; i < area->nr_pages; i++)
struct page *page;
if (node == NUMA_NO_NODE)
page = alloc_page(alloc_mask|highmem_mask);
else
page = alloc_pages_node(node, alloc_mask|highmem_mask, 0);
if (unlikely(!page))
/* Successfully allocated i pages, free them in __vunmap() */
area->nr_pages = i;
atomic_long_add(area->nr_pages, &nr_vmalloc_pages);
goto fail;
area->pages[i] = page;
if (gfpflags_allow_blocking(gfp_mask))
cond_resched();
atomic_long_add(area->nr_pages, &nr_vmalloc_pages);
if (map_vm_area(area, prot, pages))
goto fail;
return area->addr;
fail:
warn_alloc(gfp_mask, NULL,
"vmalloc: allocation failure, allocated %ld of %ld bytes",
(area->nr_pages*PAGE_SIZE), area->size);
__vfree(area->addr);
return NULL;
- 如果显式指定了分配页帧的结点,则内核调用alloc_pages_node。否则,使用alloc_page从当 前结点分配页帧。
- 分配的页从相关结点的伙伴系统移除。在调用时,vmalloc将gfp_mask设置为GFP_KERNEL | __GFP_HIGHMEM,内核通过该参数指示内存管理子系统尽可能从ZONE_HIGHMEM内存域分配页帧。原因:低端内存域的页帧更为宝贵,因此不应该浪费到vmalloc的分配中,在此使用高 端内存域的页帧完全可以满足要求。
- 内核调用map_vm_area将分散的物理内存页连续映射到虚拟的vmalloc区域。该函数遍历分配的物理内存页,在各级页目录/页表中分配所需的目录项/表项。
释放内存
vfree用于释放 vmalloc 和 vmalloc_32 分配的区域, vunmap用于释放由vmap或 ioremap 创建的映射。 这两个函数都会归结到__vunmap 。void __vunmap(void *addr, int deallocate_pages)
- addr表示要释放的区域的起始地址
- deallocate_pages指定了是否将与该区域相关的物理内存页返回给伙伴系统。
- 此__vunmap的第一个任务是在__remove_vm_area(由remove_vm_area在完成锁定之后调用)中扫描该链表,以找到相关项。
- unmap_vm_area使用找到的vm_area实例,从页表删除不再需要的项。
- 如果__vunmap的参数deallocate_pages设置为1(在vfree中),内核会遍历area->pages的所有元素,即指向所涉及的物理内存页的page实例的指针。然后对每一项调用__free_page,将页释放到伙伴系统。
- 释放用于管理该内存区的内核数据结构
参考
《深入Linux内核架构》
剖析Linux内核物理内存管理-大学生教程-腾讯课堂 (qq.com)
linux内核源码解析03–启动代码分析之主内核页表创建(代码片段)
...下页表映射:1.恒等映射:页表基地址idmap_pg_dir;2.粗粒度内核镜像映射:页表基地址init_pg_dir;3.fixmap映射:页表基地址为init_pg_dir,待paging_init之后为swapper_pg_end;4.细粒度内核镜像映射:页表基地址为swapper_pg_dir;5.线性映射:页表基... 查看详情
linux内核源码分析之页表(代码片段)
目录页表说明虚拟地址转换为物理地址过程内核中的宏以及说明页表用来把虚拟页映射到物理页,并且存放页的保护单位,即访问权限。页表说明5级页表如下1)页全局目录(PageGlobalDirectory,PGD)2) 页四级目录(Page4t... 查看详情
内核源码解读(10)percpu_page_set分析(代码片段)
...争激烈程度也会越来越大。为了改善这个问题,linux内核中引入了per_cpu_page 查看详情
linux内核源码分析之巨型页(代码片段)
...缺页异常的数量,大幅提高应用程序的性能。这才是内核引入巨型 查看详情
linux内核源码分析之页表缓存(代码片段)
处理器的内存单元(MMU)负责把虚拟地址转换成物理地址。为了改进虚拟地址到物理地址的转换速度,引入TLB的高速缓存。即页表缓存 页表缓存用来缓存最近使用过的页表项,有些处理器... 查看详情
内核解读之内存管理页分配器伙伴系统介绍(代码片段)
文章目录1分配器的需求2伙伴系统的内存组织2.1zonelist列表结构2.2zone的空闲内存结构2.3内存迁移类型2.4pcp页框1分配器的需求伙伴系统是linux的页框分配器,负责系统物理内存的分配工作。由于几乎所有模... 查看详情
内核解读之内存管理页分配器伙伴系统介绍(代码片段)
文章目录1分配器的需求2伙伴系统的内存组织2.1zonelist列表结构2.2zone的空闲内存结构2.3内存迁移类型2.4pcp页框1分配器的需求伙伴系统是linux的页框分配器,负责系统物理内存的分配工作。由于几乎所有模... 查看详情
linux内核源码分析之设备驱动(代码片段)
...为输入输出,一般都缩写为I/O实现外设的I/O时,内核必须处理3个可能出现问题的领域必须根据具体的设备类型和模型,使用各种方法对硬件寻址内核必须向用户应用程序和系统工具提供访问各种设备的方法用户空间... 查看详情
linux内核fork源码分析(代码片段)
内核版本:linux-4.4.18源码位置:这里fork相关的代码最终执行的函数为_do_fork(),下面按照顺序分析下_do_fork():首先判断是否需要trace(跟踪)这个进程,这一步主要与调试相关,GDB在x86-64Linux系统上... 查看详情
第一次作业:深入源码分析进程模型(代码片段)
...nux操作系统的简易介绍 Linux系统一般有4个主要部分:内核、shell、文件系统和应用程序。内核、shell和文件系统一起形成了基本的操作系统结构,它们使得用户可以运行程序、管理文件并使用系统。 (1)内核 内... 查看详情
linux内核源码分析之进程调度(代码片段)
文章目录一、进程优先级二、内核支持调度策略三、task_struct调度相关的成员四、调度类五、就绪队列六、调度实体调度策略通常在进程响应速度和最大系统利用率寻找平衡。一、进程优先级1)普通优先级:nice值范围-20~... 查看详情
linux内核源码分析之slab(代码片段)
目录创建对象obj释放对象销毁缓存创建对象objstaticinlinevoid*____cache_alloc(structkmem_cache*cachep,gfp_tflags) void*objp; structarray_cache*ac; check_irq_off(); //从per-CPU缓存中获取数据 ac=cpu_cache_get(cachep); if(likely(ac->avail)) ac->touched=1; objp=... 查看详情
linux内核获取初次编译源码目录分析(代码片段)
目录Linux内核获取Linux内核初次编译Linux内核源码目录分析1、arch目录2、block目录3、crypto目录4、Documentation目录5、drivers目录6、firmware目录7、fs目录8、include目录9、init目录10、ipc目录11、kernel目录12、lib目录13、mm目录Linux内核获取关... 查看详情
第一次作业:深入源码分析进程模型(代码片段)
...系统的简单介绍。linux本身不能算是操作系统,只是一个内核,基于linux内核的操作系统有很多,比如流行的android,ubuntu,红旗linux等等。Linux以它的高效性和灵活性著称。它能够在PC计算机上实现全部的Unix特性,具有多任务、多... 查看详情
linux内核内存管理mmap系统调用源码分析⑤(mmap_region函数执行流程|mmap_region函数源码)(代码片段)
文章目录一、mmap_region函数执行流程1、检查内存申请是否合法2、创建"虚拟内存区域"二、mmap_region函数源码调用mmap系统调用,先检查"偏移"是否是"内存页大小"的"整数倍",如果偏移是内存页大小的整数倍,则... 查看详情
linux系统之升级内核版本方法(代码片段)
Linux系统之升级内核版本方法一、检查本地系统环境1.检查系统版本2.检查系统内核版本二、小版本升级内核1.列出yum仓库的内核包版本2.升级内核3.重启并检查内核版本三、大版本升级内核1.导入公钥2.下载并安装elrepo仓库3.载入elr... 查看详情
《linux操作系统分析》之linux系统的理解及学习linux内核的心得
...行第二篇:《Linux操作系统分析》之分析精简的Linux的内核中断和时间片轮询第三篇:《Linux操作系统分析》之跟踪分析Linux内核的启动过程第四篇:《Linux操作系统分析》之使用库函数API和C代码中嵌入汇编代码两种方... 查看详情
linux内核源码分析方法
https://www.ibm.com/developerworks/cn/linux/l-vfs/http://blog.csdn.net/daniel_ice/article/details/8821444https://zhuanlan.zhihu.com/p/22092849https://zhuanlan.zhihu.com/p/22092849一、内核源码之我见Linux内核代码的庞大 查看详情