Linux 内存源码初步阅读

几个数据结构

  1. pg_data_t
    这个数据结构代表每一个NUMA节点,关于NUMA可以稍微看看这篇了解下
  2. zonelist
    在这里插入图片描述
    内存域的列表,按照内存域的优先级排列。
  3. zone
    内存域,内存域常见类型有几种:
    在这里插入图片描述
  4. page
    代表一个物理内存页,页帧。
  5. free_area
    这个跟伙伴内存分配相关。
    在这里插入图片描述
    放一个自己画的图
    在这里插入图片描述
    从上面的图里面大致可以反映出代码的结构,对照代码看的话更清晰一些。

初始化内存

直接贴书上的图吧
在这里插入图片描述

  1. setup_arch: 函数内部会去获取机器内存信息,这个跟特定的机器相关,我看到好多都是e820,关于e820可以参照WiKi这篇博客
    在这里插入图片描述
  2. setup_per_cpu_areas:拷贝一些变量到每个CPU
  3. build_all_zonelists: 构建每个内存区域的优先级,分配内存的时候按照这个优先级去zone里面找。
  4. setup_per_cpu_pageset: 初始化每个CPU的pageset

伙伴系统

初始化

把系统的可用内存跟数据结构关联起来的函数是:free_area_init_nodes
在这里插入图片描述
对于每个NUMA节点,调用了free_area_init_node,这个函数里面调用calculate_node_totalpages计算这个节点页的总量,调用alloc_node_mem_map来初始化struct page所需要的内存并初始化mem_map。最后再调用free_area_init_core,这个函数里面需要关注下面三个函数:

  1. zone_pcp_init:初始化这个内存域的per-CPU缓存
  2. init_currently_empty_zone:初始化free_area
  3. memmap_init这个是个宏,后面的函数是memmap_init_zone
  4. memmap_init_zone:这个函数里面会调用:
    1. pfn_to_page:物理页面序号转换成页帧
    2. set_page_links:设置这个页属于哪个内存域,属于哪个NUMA节点。
    3. SetPageReserved:设置这个页成Reserved。

伙伴系统分配

在这里插入图片描述
所有的函数最后都归到alloc_pages_node这个函数,这个函数又会调用__alloc_pages_nodemask这个函数,这个函数就是核心分配了,内部主要调用了两个函数:

get_page_from_freelist
__alloc_pages_slowpath

第一个函数先检查内存水位线,再尝试分配合适的内存,如果可以分配内存,那么调用buffered_rmqueue这个函数获取页,然后就结束分配。
如果第一次尝试不成功的话,会调用下面那个函数,下面这个就相当于进入了分配的“slowpath”,slowpath里面会先唤醒kswapd进程看看有没有能交换到swap的页,然后再调用第一个函数去分配,这次会比第一次积极一些,会放宽一些条件限制。

buffered_rmqueue这个函数会检查这些可分配的页是否是连续的,然后再移除这些页并重排内存区。
__rmqueue这个函数会调用 __rmqueue_smallest来获取指定节点,指定内存域,指定阶数,指定迁移类型的页。最后调用expand函数重排内存区。

好了,经过这一系列的函数,已经获取了特定数量的页了。

slab/slub/slob

通过malloc分配用户空间内存的时候,页单位太大,需要把页分成更小的单位来管理。
slab:默认分配器
slub:针对大型计算机的分配器
在这里插入图片描述
slab里面关键的概念就是“缓存”和“对象”。

  1. 缓存
    对应的数据结构是kmem_cache,每种对应大小的对象都对应一个kmem_cache,相当于一个篮子。
  2. 对象
    篮子里面的果子。
    在这里插入图片描述

分配对象步骤

在这里插入图片描述
这篇博文很好。

总结

获取内存布局[e820],并初始化页帧 --> 页的分配[伙伴系统] --> 小对象的分配[slab系列]

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章