Kernel源码分析-内存管理
整理破碎的知识体系……
根据这个大佬的公众号👉#聊聊Linux内核
内核版本5.19(buddy system前),5.4(buddy system即之后)
虚拟内存管理
先放一张结构图
- task_struct的mm指向mm_struct结构体
- mm_struct的mmap成员指向vm_area_struct双向链表的头节点
- vm_area_struct通过双向链表串联
- vm_area_struct的vm_mm指向所属的mm_struct
- mm_struct的mm_rb通过红黑树组织所有vm_area_struct
- vm_area_struct的vm_rb指向所属红黑树
新版本的vm_area_struct改用maple_tree组织了
mm_struct结构体
1 |
|
- task_size:用户空间虚拟地址大小,等于TASK_SIZE宏,64位是0x00007ffffffff000
- 定义内存区域的成员:
- start_code,end_code:代码段
- start_data,end_data:数据段
- start_brk,brk:堆地址起始地址,结束地址
- mmap_base:内存映射区起始地址
- start_stack:栈起始位置(栈底)
- arg_start,arg_end:参数列表
- env_start,env_end:环境变量
- 物理内存映射内容相关统计变量:
- total_vm:进程虚拟内存空间中映射物理内存页的总数
- locked_vm:锁定不能换出的内存页总数
- pinned_vm:既不能换出,也不能移动的内存页总数
- data_vm:数据段中映射的内存页数目
- exec_vm:代码段中存放可执行文件的内存页数目
- stack_vm:栈中所映射的内存页数目
vm_area_struct结构体
1 |
|
vm_start,vm_end:虚拟地址范围(左闭右开)
- vm_page_prot:页级别访问控制
- vm_flags:虚拟内存访问控制
1
vma->vm_page_prot = vm_get_page_prot(vma->vm_flags) // 转换
vm_file:关联被映射的文件
vm_pgoff:映射进虚拟内存中的文件内容,在文件中的偏移
anon_vma:匿名映射
vm_private_data:用于存储VMA中的私有数据
vm_ops:针对虚拟内存区域VMA的相关操作的函数指针
1
2
3
4
5struct vm_operations_struct {
void (*open)(struct vm_area_struct * area);
void (*close)(struct vm_area_struct * area);
vm_fault_t (*fault)(struct vm_fault *vmf);
};- open:指定的虚拟内存区域被加入到进程虚拟内存空间中时调用
- close:虚拟内存区域VMA从进程虚拟内存空间中被删除时调用
- fault:发生缺页异常时调用(未分配物理页或被换出)
- page_mkwrite:当一个只读的页面将要变为可写时调用
虚拟内存空间布局
32位
还是先放一张图
前896M物理内存直接映射到3G—3G+896M
- 前1M被系统启动占用(BIOS什么的)
- 后面是内核代码段,数据段,BSS段
- 进程相关的数据结构,内核栈也会存放在物理内存前896M的这段区域中
X86 体系结构下,ISA总线的DMA(直接内存存取)控制器,只能对内存的前16M 进行寻址,所以直接映射区又分为DMA映射区和NORMAL映射区
剩下的物理内存动态映射到vmalloc动态映射区,使用vmalloc进行分配,分配的物理页是不连续的
永久映射区允许建立与物理高端内存(896M以上)的长期映射关系,比如内核通过 alloc_pages() 函数在物理内存的高端内存中申请获取到的物理内存页,这些物理内存页可以通过调用 kmap 映射到永久映射区中
固定映射区类似永久映射区,但映射的物理页是固定的,在编译期间就已经确定
临时映射区用于拷贝物理页时临时将物理内存页映射到虚拟内存
64位
依然先放一张图
大部分在32位都有
- 虚拟内存映射区用于存放page结构体
- 代码段用于映射内核代码
物理内存管理
物理内存模型
FLATMEM平坦内存模型
一个page全局数组mem_map管全部
pfn和page的转换
1 |
|
DISCONTIGMEM非连续内存模型
pglist_data表示node
1 |
|
node_mem_map数组管理page
pfn和page的转换多了一步
- arch_pfn_to_nid根据物理页的pfn定位到所在node
- page_to_nid根据page定位到所在node
之后一样
1 |
|
SPARSEMEM稀疏内存模型
mem_section管理
1 |
|
section_mem_map其实是个指针,指向管理的page数组
用一个全局数组管理所有section
1 |
|
pfn和page的转换更复杂了
- 如果有vmemmap直接使用vmemap(64位空间多随意挥霍( •̀ ω •́ )✧)
- page_to_pfn先根据page定位到section,再通过section_mem_map定位到pfn
- pfn_to_page先根据pfn定位到section,再通过pfn从section_mem_map指向的数组定位到page
1 |
|
物理内存架构
UMA架构(一致性内存访问)
NUMA架构(非一致性内存访问)
访问本地内存节点很快,访问其他内存节点很慢
NUMA的分配策略
- MPOL_BIND:必须在绑定的节点进行分配,内存不足则进行swap
- MPOL_INTERLEAVE:本地节点和远程节点都可以进行分配
- MPOL_PREFERRED:优先在指定节点分配内存,当指定节点内存不足时,选择离指定节点最近的节点分配内存
- MPOL_LOCAL(默认):优先在本地节点分配,当本地节点内存不足时,可以在远程节点分配内存
NUMA节点管理
物理内存在内核中管理的层级关系为:Node→Zone→page
一个node表示一个NUMA节点
先放一张总体结构图
使用一个pglist_data的全局数组node_data管理所有node
1
2struct pglist_data *node_data[MAX_NUMNODES] __read_mostly;
EXPORT_SYMBOL(node_data);NUMA节点描述结构体pglist_data
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28typedef struct pglist_data {
struct zone node_zones[MAX_NR_ZONES];
struct zonelist node_zonelists[MAX_ZONELISTS];
int nr_zones; /* number of populated zones in this node */
unsigned long node_start_pfn;
unsigned long node_present_pages; /* total number of physical pages */
unsigned long node_spanned_pages; /* total size of physical page
range, including holes */
int node_id;
wait_queue_head_t kswapd_wait;
wait_queue_head_t pfmemalloc_wait;
struct task_struct *kswapd; /* Protected by
mem_hotplug_begin/end() */
int kswapd_order;
enum zone_type kswapd_highest_zoneidx;
int kswapd_failures; /* Number of 'reclaimed == 0' runs */
#ifdef CONFIG_COMPACTION
int kcompactd_max_order;
enum zone_type kcompactd_highest_zoneidx;
wait_queue_head_t kcompactd_wait;
struct task_struct *kcompactd;
#endif
} pg_data_t;node_id:NUMA节点id
node_mem_map:page类型数组,包含NUMA中的所有物理页
node_start_pfn:指向NUMA节点内第一个物理页的PFN(PFN全局唯一)
node_present_pages:可用的物理页面数量(不包含空洞)
node_spanned_pages:所有物理页面数量(包含空洞)
nr_zones:用于统计NUMA节点内包含的物理内存区域个数
注:只有第一个NUMA节点可以包含所有种类的zone,比如DMA必须从物理内存起点开始
node_zones:zone数组,包含NUMA节点中的所有物理内存区域
node_zonelists:zonelist数组,包含了备用NUMA节点和这些备用节点中的物理内存区域(备用节点按访问距离远近排列)
kswapd:一个用于回收不经常使用的页面的进程
kcompactd:一个用于内存的规整避免内存碎片的进程
NUMA节点物理内存区域的划分
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18enum zone_type {
#ifdef CONFIG_ZONE_DMA
ZONE_DMA,
#endif
#ifdef CONFIG_ZONE_DMA32
ZONE_DMA32,
#endif
ZONE_NORMAL,
#ifdef CONFIG_HIGHMEM
ZONE_HIGHMEM,
#endif
ZONE_MOVABLE,
#ifdef CONFIG_ZONE_DEVICE
ZONE_DEVICE,
#endif
__MAX_NR_ZONES
};- ZONE_DMA:用于那些无法对全部物理内存进行寻址的硬件设备,进行 DMA 时的内存分配(如ISA只能寻址前16M)
- ZONE_DMA32:提供给32位设备(只能寻址4G物理内存)执行DMA操作时使用的(只在64位系统中起作用)
- ZONE_NORMAL:直接映射的896M剩下的部分
- ZONE_HIGHMEM:剩下的高端内存
- ZONE_DEVICE:为支持热插拔设备而分配的非易失性内存(也可用于内核崩溃时保存相关的调试信息)
- ZONE_MOVABLE:内核定义的一个虚拟内存区域,该zone中的物理页可以来自于上边介绍的几种真实的物理区域,页都是可以迁移的,主要是为了防止内存碎片和支持内存的热插拔
node_states:NUMA节点不止一个的时候使用,位图,用于维护各个NUMA节点的状态信息(只有一个NUMA节点时没有)
1
2typedef struct { DECLARE_BITMAP(bits, MAX_NUMNODES); } nodemask_t;
extern nodemask_t _unused_nodemask_arg_;节点状态
1
2
3
4
5
6
7
8
9
10
11
12
13
14enum node_states {
N_POSSIBLE, /* The node could become online at some point */
N_ONLINE, /* The node is online */
N_NORMAL_MEMORY, /* The node has regular memory */
#ifdef CONFIG_HIGHMEM
N_HIGH_MEMORY, /* The node has regular or high memory */
#else
N_HIGH_MEMORY = N_NORMAL_MEMORY,
#endif
N_MEMORY, /* The node has memory(regular, high, movable) */
N_CPU, /* The node has one or more cpus */
N_GENERIC_INITIATOR, /* The node has one or more Generic Initiators */
NR_NODE_STATES
};- N_POSSIBLE:节点随时会上线
- N_ONLINE:节点已上线
- N_NORMAL_MEMORY:节点没有高端内存,只有ZONE_NORMAL
- N_HIGH_MEMORY:节点有ZONE_NORMAL或者有ZONE_HIGHMEM
- N_MEMORY:节点有ZONE_NORMAL,ZONE_HIGHMEM,ZONE_MOVABLE
- N_CPU:表示节点包含一个或多个 CPU
NUMA节点中的物理内存管理
zone和node的关系
zone结构体
1 |
|
lock:防止并发访问
name:内存区域名称,Normal / DMA / HighMem
zone_pgdat:指向所属的NUMA节点
zone_start_pfn:属于该zone中的第一个物理页PFN
spanned_pages:zone中所有物理页个数(包括空洞)
present_pages:zone中可用物理页个数(不包括空洞)
managed_pages:buddy system管理的物理页个数
free_area:buddy system的核心数据结构
vm_stat:该zone使用的统计信息
nr_reserved_highatomic:该zone预留内存的大小[128KB, 65536KB]
lowmem_reserve:规定每个内存区域必须为自己保留的物理页数量,防止更高位的内存区域对自己的内存空间进行过多的侵占挤压
_watermark:物理内存区域中的水位线
- 物理内存剩余容量大于_watermark[WMARK_HIGH] → 内存充足,分配无压力
- 大于_watermark[WMARK_LOW],小于_watermark[WMARK_HIGH] → 分配有压力但可接受
- 大于_watermark[WMARK_MIN],小于_watermark[WMARK_LOW] → 唤醒kswapd进程开始异步回收
- 小于_watermark[WMARK_MIN] → 阻塞请求分配的进程,唤醒kswapd进程进行回收,回收完毕唤醒阻塞的进程
watermark_boost:优化内存碎片对内存分配的影响,可以动态改变内存区域的基准水位线
pageset:per_cpu_pageset,管理冷热页(__percpu变量)
1
2
3struct per_cpu_pageset {
struct per_cpu_pages pcp;
};用一个双向列表管理per_cpu_pages,热页在前,冷页在后
per_cpu_pages结构体
1
2
3
4
5
6
7
8struct per_cpu_pages {
int count; /* number of pages in the list */
int high; /* high watermark, emptying needed */
int batch; /* chunk size for buddy add/remove */
/* Lists of pages, one per migrate type stored on the pcp-lists */
struct list_head lists[MIGRATE_PCPTYPES];
};- lists:双向列表
- count:物理页数量
- high:count超过了high,那么表示list中的页面太多了,内核会从高速缓存中释放batch个页面到物理内存区域中的伙伴系统中
- batch:见上
物理内存页描述
page结构体
1 |
|
mapping:page cache(高速页缓存)结构体address_space,被文件的inode持有
- page为文件页:mapping最低位为0,指向页关联文件的address_space
- page为匿名页:mapping最低位为1,指向匿名页在进程虚拟内存空间中的匿名映射区域anon_vma结构,用于物理内存到虚拟内存的反向映射
index:pgoff_t
- page为文件页:表示该内存页中的文件数据在文件内部的偏移offset,偏移单位为页大小
- page为匿名页:表示匿名页在对应进程虚拟内存区域VMA中的偏移
_mapcount:表示该page映射了多少个进程的虚拟内存空间,一个page可以被多个进程映射
lru:指向物理页被放置在了哪个链表上(active,inactive)
_refcount:用来记录内核中引用该物理页的次数,表示该物理页的活跃程度
flags:
- 高8位储存定位信息(section,zone,node)
- 储存访问相关,换入换出相关等标志
复合页相关
- flags:如果是首页会设置PG_head
- compound_head:复合页的尾页用来指向首页
- compound_dtor:用于释放复合页的析构函数
- compound_order:复合页的order
- compound_mapcount:使用复合页的进程个数,内存页反向映射的概念,首页中保存
- compound_pincount:复合页使用计数,首页中保存
匿名页的反向映射
在物理页需要被迁移或者回收时使用,此时需要找到物理页映射的虚拟地址,并断开连接
page:结构体表示一个物理页
anon_vma:表示一个匿名页(仅用于反向映射)
anon_vma_chain:表示一个匿名页和一段虚拟内存的关系
匿名页和虚拟内存是一对多的关系,因为一个匿名页可能映射到很多进程的虚拟空间 → anon_vma和anon_vma_chain也是一对多的关系
vm_area_struct:表示一段虚拟内存
page中的mapping指向anon_vma结构体
anon_vma结构体中的rb_root红黑树储存了所有与匿名页相关的anon_vma_chain
1
2
3
4
5
6
7
8struct anon_vma {
struct anon_vma *root; /* Root of this anon_vma tree */
struct rw_semaphore rwsem; /* W: modification, R: walking the list */
atomic_t refcount;
unsigned degree;
struct anon_vma *parent; /* Parent of this anon_vma */
struct rb_root_cached rb_root;
};anon_vma_chain结构体串联vm_area_struct和anon_vma
- vma指向相关的vm_area_struct
- anon_vma指向相关的虚拟页
- same_vma双向链表串联了vma的所有匿名页
1
2
3
4
5
6
7
8
9
10struct anon_vma_chain {
struct vm_area_struct *vma;
struct anon_vma *anon_vma;
struct list_head same_vma; /* locked by mmap_lock & page_table_lock */
struct rb_node rb; /* locked by anon_vma->rwsem */
unsigned long rb_subtree_last;
#ifdef CONFIG_DEBUG_VM_RB
unsigned long cached_vma_start, cached_vma_last;
#endif
};vm_area_struct表示VMA
1
2
3
4struct vm_area_struct {
struct list_head anon_vma_chain;
struct anon_vma *anon_vma;
};- anon_vma_chain双向链表串连了VMA的所有匿名页
- anon_vma用于快速判断 VMA 有没有对应的匿名 page
物理内存分配
物理内存分配接口
alloc_pages函数用于请求2的order次幂个page
1 |
|
- order:分配阶
- gfp:用于规范物理内存分配行为的修饰符
返回的是page结构体,需要转换成虚拟地址使用,__get_free_pages函数可以返回虚拟地址
1 |
|
内部也是调用了alloc_pages,进行了地址转换
- 不能分配高端内存,高端内存不适用于直接地址转换
- 返回地址位于直接映射区
get_zeroed_page会把page内容清零
1 |
|
__get_dma_pages只分配DMA内存页
1 |
|
两个内存释放函数
1 |
|
- __free_pages对应alloc_pages,使用page结构体
- free_pages对应__get_free_pages,使用虚拟内存地址
物理内存分配源码实现
大佬写的很详细了所以就放两张图doge
函数调用图
__alloc_pages函数的逻辑
buddy system
zone中buddy system相关成员
free_area的下标表示order
1 |
|
free_area,不同迁移类型的page用不同的链表管理
1 |
|
迁移类型
1 |
|
- UNMOVABLE:一般位于直接映射区,内核所需要的核心内存一般从这分配
- MOVABLE:一般用于在进程用户空间中分配,因为在用户空间中虚拟内存与物理内存都是通过页表来动态映射的,物理页移动之后,只需要改变页表中的映射关系即可,而虚拟内存地址并不需要改变
- PCPTYPES:里面包含了高速缓存中的冷页和热页
- RECLAIMABLE:不能移动但可以回收,比如文件缓存页,之后再从文件中读取就行
- CMA:contiguous memory allocator,是一个分配连续物理内存页面的分配器,用于分配连续的物理内存
- ISOLATE:一个虚拟区域,用于跨越NUMA节点移动物理内存页,内核可以将物理内存页移动到使用该页最频繁的CPU 所在的NUMA节点中
长这样
如果对应order,migratetype的page不够,从其他migratetype中申请的顺序,注意fallbacks分配的顺序和正常分配的顺序是反的
1 |
|
放一张get_page_from_freelist函数的逻辑
- for_next_zone_zonelist_nodemask遍历当前NUMA节点以及备用节点的所有zone(zonelist)
- zone_watermark_fast检查zone中的剩余内存是否在指定水位线上,是则跳转至try_this_zone调用rmqueue进入buddy system进行内存分配,分配成功后调用prep_new_page初始化分配好的page,否则继续
- node_reclaim触发内存回收,回收后通过zone_watermark_ok检查回收的内存是否满足本次分配需要,是则跳转至try_this_zone在zone中分配内存
rmqueue包括了buddy system的主逻辑
- __rmqueue_smallest封装buddy system的核心流程
- __rmqueue底层调用__rmqueue_smallest,还包括fallback的过程
一次分配的流程图
内存释放源码逻辑
页物理视图
一次释放的过程
slab
内核版本5.4,slub
slab数据结构体
slab结构图
kmem_cache结构体
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26struct kmem_cache {
struct kmem_cache_cpu __percpu *cpu_slab;
/* Used for retrieving partial slabs, etc. */
slab_flags_t flags;
unsigned int size; /* The size of an object including metadata */
unsigned int object_size;/* The size of an object without metadata */
unsigned int offset; /* Free pointer offset */
struct kmem_cache_order_objects oo;
/* Allocation and freeing of slabs */
struct kmem_cache_order_objects max;
struct kmem_cache_order_objects min;
gfp_t allocflags; /* gfp flags to use on each alloc */
int refcount; /* Refcount for slab cache destroy */
void (*ctor)(void *);
unsigned int inuse; /* Offset to metadata */
unsigned int align; /* Alignment */
unsigned int red_left_pad; /* Left redzone padding size */
const char *name; /* Name (only for display!) */
struct list_head list; /* List of slab caches */
unsigned int useroffset; /* Usercopy region offset */
unsigned int usersize; /* Usercopy region size */
struct kmem_cache_node *node[MAX_NUMNODES];
};flags:管理标志位
SLAB_HWCACHE_ALIGN:需要进行cache line(64位)对齐
SLAB_POISON:需要填充特殊字节表示状态
SLAB_RED_ZONE:需要插入red zone防止越界读写
SLAB_CACHE_DMA:slab中内存来自哪个内存区域
SLAB_STORE_USER:追踪对象的分配释放信息,追加两个track块
size:slab对象真实大小
object_size:使用的内存大小(上图object size蓝色部分)
offset:freepointer的偏移
oo:储存slab需要的page个数
max:oo的最大值,初始化为oo
min:oo的最小值,初始化为1
allocflags:分配时所用的标志位
inuse:word size对齐后的大小
align:综合word size,cache line,align计算一个合理的对齐尺寸
name:slab cache的名称
refcount:引用计数
list:kmem_cache用双向链表串联
cpu_slab:每个cpu有一个slab本地缓存
1
2
3
4
5
6
7
8struct kmem_cache_cpu {
void **freelist; /* Pointer to next available object */
unsigned long tid; /* Globally unique transaction id */
struct page *page; /* The slab from which we are allocating */
#ifdef CONFIG_SLUB_CPU_PARTIAL
struct page *partial; /* Partially allocated frozen slabs */
#endif
};- freelist:该slab第一个空闲对象
- tid:cpu编号
- page:slab所在的page
- partial:备用的slab
node:备用slab(每个NUMA节点一个)
1
2
3
4
5
6
7
8
9
10
11
12
13struct kmem_cache_node {
spinlock_t list_lock;
#ifdef CONFIG_SLUB
unsigned long nr_partial;
struct list_head partial;
#ifdef CONFIG_SLUB_DEBUG
atomic_long_t nr_slabs;
atomic_long_t total_objects;
struct list_head full;
#endif
#endif
};- nr_patial:该NUMA节点备用slab个数
- patial:备用slab用一个patial双链表串联起来
- full:串联所有分配完的slab
page结构体中slab相关成员(高版本另有slab结构体),一个page是一个slab
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29struct page {
struct { /* slab, slob and slub */
union {
struct list_head slab_list;
struct { /* Partial pages */
struct page *next;
#ifdef CONFIG_64BIT
int pages; /* Nr of pages left */
int pobjects; /* Approximate count */
#else
short int pages;
short int pobjects;
#endif
};
};
struct kmem_cache *slab_cache; /* not slob */
/* Double-word boundary */
void *freelist; /* first free object */
union {
void *s_mem; /* slab: first object */
unsigned long counters; /* SLUB */
struct { /* SLUB */
unsigned inuse:16;
unsigned objects:15;
unsigned frozen:1;
};
};
};
}- slab_list:slab所在管理链表
- next:partial使用
- pages:slab所在管理链表中的包含的slab总数
- pobjects:slab所在管理链表中包含的对象总数
- slab_cache:指向kmem_cache
- freelist:指向slab中第一个空闲对象,slab放进cpu_slab后将这个指针赋给cpu_slab,然后置空
- inuse:使用的空间
- objects:包含的对象个数
- frozen:1表示在本地cpu缓存中
分配逻辑
从本地cpu缓存中分配(fast path):查看freelist是否有空闲对象
从本地cpu缓存partial列表中分配:本地cpu缓存的slab(page)中没有空闲对象
- 遍历partial列表,找一个满足分配的slab
- 将这个slab从partial中摘下,提升为本地cpu缓存
从NUMA节点缓存中分配:遍历kmem_cache_node->partial列表,将链表中的slab摘下来填充到本地cpu缓存的partial链表中(最多cpu_partial / 2个)
从buddy system中重新申请slab:最开始slab是空的
初始化新申请的slab,提升为cpu本地缓存的page
函数调用关系图
释放逻辑
- 释放对象所属slab在cpu本地缓存中(fast path):直接放回cpu本地缓存的slab
- 释放对象所属slab在cpu本地缓存partial链表中:直接释放
- 释放对象所属slab从full变成partial(slab不在cpu本地缓存中)
- 对象放回slab并将slab放入本地cpu缓存的partial链表中(partial中slab个数未超标)
- 将partial链表中的所有slab转移至对应NUMA节点的node->partial链表的尾部,再将slab插入本地缓存的partial链表(partial中slab个数超标)
- 释放对象所属slab从partial变成empty
- 不是活跃slab,放回kmem_cache_node->partial链表(partial中slab个数未超标,min_partial)
- 放回buddy system(partial中slab个数超标)
函数调用关系图
slab内存池的创建初始化流程
就放一张函数调用关系图
slab内存池的销毁
kmalloc体系
kmalloc体系基于slab allocator体系,本质是不同尺寸的通用slab cache
kmalloc内存块尺寸
slab cache信息储存在kmalloc_info数组中,由kmalloc_info_struct结构体表示
1 |
|
- name:slab cache名字
- size:内存块大小
1 |
|
- index > 2 时size为2 ^ index
- 由于内核大部分申请大小都在192和96左右所以单独提供这两种大小的slab cache
size_index[24]数组用于定义小于192大小的内存块的大小选取规则
1 |
|
注释内是申请大小,数组元素是slab cache对应下标
申请尺寸大于192时使用fls函数计算,fls可以获取参数的最高有效bit的位数
1 |
|
kmalloc架构
所有的kmem_cache储存在一个全局数组中
1 |
|
第一个下标表示物理内存区域类型
1
2
3
4
5
6
7
8enum kmalloc_cache_type {
KMALLOC_NORMAL = 0,
KMALLOC_RECLAIM,
#ifdef CONFIG_ZONE_DMA
KMALLOC_DMA,
#endif
NR_KMALLOC_TYPES
};第二个下标表示slab cache
kmalloc体系的创建
函数调用流程
1 |
|
1 |
|
主要就是两个函数
- setup_kmalloc_cache_index_table:初始化size_index数组
- create_kmalloc_caches:创建初始化kmalloc_caches二维数组
kmalloc内存池的分配和回收
kmalloc分配
kfree回收(过于简单甚至不配拥有一张图)
1 |
|
- 使用virt_to_head_page函数将虚拟地址转化为page结构体
- 通过PageSlab查看page的是否设置PG_slab标识
- 没有则是从buddy system中分配的,使用__free_pages放回buddy system
- 否则调用slab_free放回对应slab
页表体系
就放两张图,调试启动流程的时候对这玩意有深刻的认识了(ˉ▽ˉ;)…