系统初始化的时候需要执行一些内存管理 内存分配任务就会需要内存管理器
先说说这个memblock分配器 有三个重要的结构体
struct memblock 表示这个分配器 内核初始化的时候 有个全局变量struct memblock
因为一个物理内存(节点node) 划分了不同的区域(zone) 所以这边的分配器根据类型也有个结构体 struct memblock_type
最后用 strcut_memblock_region 描述页的基地址 和大小 和对应的flag
初始化的时候 内核先解析设备树二进制节点 把所有物理内存加载到mem_block这里
在源文件"mm/memblock.c定义全局变量memblock,把成员bottom_ up初始化为假,表示从高地址向下分配。
具体分配的过程在memblock.c 直接一上来就弄了个全局变量在这
看看内核初始化过程
Init/main.c
start_kernel()
set_arch(&command_line)
fdt_ enforce_ memory_ region() ;//解析设备树二进制文件,得到可用内存范围,把超出物理内存的范围找出来,从memblock删除
//全局变量memstart_ addr记录内存的起始物理地址
memstart_ addr = round_ down (memblock_ start_ of_ DRAM() ,ARM64_ MEMSTART_ ALIGN) ;
设备树上的二进制节点 对应的命令行比如 mem内存方位可用内存大小 如果指定了内存大小 把超出长度的物理内存从memblock进行删除 因为内核镜像也被加载到了高地址 并且内核镜像可以通过线性映射区访问 所以要把内核镜像占用物理内存的范围重新添加
struct memblock {
bool bottom up; // 表示分配内存的方式,值为真表示从低地址向上分配,值为假表示从高地址向下分配
phys_ addr_ t current_ limit; //可分配内存的最大物理地址
struct memblock_ type memory; //内存类型(包括已分配的内存和未分配的内存)
struct memblock_ type reserved; //预留类型(已分配的内存)
#ifdef CONFIG_ HAVE_ MEMBLOCK_ PHYS_ MAP
struct memblock_ type physmem; //物理 内存类型,
#endif
};
物理内存类型和内存类型区别:内存类型是物理内存类型的子集,在引导内核时可以使用内核参数"mem= nn[KMG]“,指定可用内存的大小,导致内核不能看见所有的内存;物理内在类型总是包含所有内存范围,内存类型只包含内核参数” mem="指定的可用内在范围。
结构体 memblock_ _type
struct memblock_ _type {
unsigned long cnt;
// 当前管理集合中记录的内存区域个数
unsigned long max;
//当前管理集合中记录的内存区域的最大个数,最大值是INIT_ PHYSMEM REGIONS
phys_ _addr_ _t total_ size; //所有内存块区域的总长度
struct memblock_ region *regions; //执行内存区域结构的指针
char *name; //内存块类型的名称
};
//内存块区域的数据结构
struct memblock_ region {
phys_ _addr_ _t base; //起始物理地址
phys_ _addr_ _t size; //长度
unsigned long flags; //成员falgs是标志
#i fdef CONFIG HAVE MEMBLOCK NODE MAP
int nid; //节点编号
#endi f
};
// memblock分配器标志位定义
enum {
//表示没有特殊要求区域
MEMBLOCK_ NONE
= 0x0, /* No special request */
//表示可以热插拔的区域,即在系统运行过程中可以拔出或插入物理内存
MEMBLOCK_ HOTPLUG
= 081, /* hotpluggable region */
//表示镜像的区域。将内存数据做两个复制,分配放在在主内存和镜像内存中
MEMBLOCK_ MIRROR
= 0x2,
/* mirrored region */
//表示不添加到内核直接映射区域(即线性映射区域)
MEMBLOCK_ NOMAP
= 0x4, /* don't add to kernel direct mapping */
};
虚拟地址
cpu看见的: 虚拟ram内存地址 需要mmu把(物理ram+外围设备)内存地址进行映射给cpu看
ram物理地址
外围设备和物理ram内存使用统一 的物理地址空间
之前有个图又放来这边用用
RAM中表示物理ram的一些结构体-
页:struct page ,如下图所示,x86架构下一般为4K为大小
分区:struct zone ,把整个内存划分为不同区域,x86架构下分为三个区 ZONE_DMA,ZONE_NORMAL,ZONE_HIGHMEM+
子结构体 struct free_area 区中空闲的区域
本文重点在分区
内存节点:struct node。对于一个简单的嵌入式系统只有一个node,
ZONE_DMA,一般由于内存碎片,有可能申请不到连续的一片物理内存,而DMA需要连续的物理内存,所以在X86下给DMA大概会留一块连续的16M的物理内存。
伙伴分配器用在ram的物理内存管理 为什么有ram内存管理
长时间使用会有内存碎片化 引入内存管理系统
物理内存被分为几个区域。每一个区域都有一个伙伴系统。每个伙伴系统都管理着每个zone的物理内存。但是他们的原理一样。
伙伴系统(Buddy System)是一种管理物理内存的算法,主要于管理大块的内存区域,如页框。
伙伴系统通过将大的内存块不断分割成两个较小的伙伴块,然后按照块大小将伙伴块归入不同的空闲链表中,以便快速找到符合要求的内存块。
伙伴系统通常用于分配物理内存,由于内核中的物理内存总量比较有限,因此在分配物理内存时需要考虑内存碎片的问题。
而slab则是用于管理内核对象的内存分配器。
它的设计目的是为了提高内核对象的内存分配效率,减少内存碎片,提高内存分配的速度。slab分配器将内存分为不同的slab,每个slab管理一 类内核对象,slab中的页框可以被分配给该类对象进行使用,从而提高了内存的利用率和访问速度。
在实现上,伙伴系统和slab通常是一起使用的。当需要为内核对象分配内存时,slab会先从伙伴系统中分配出一块合适大小的物理内存,然后将该物理内存划分为slab,并在其中管理内核对象。因此,伙伴系统和slab可以说是相互配合的内存管理机制。
看的是zone结构体 表示物理ram区域的结构体 因为伙伴系统需要的属性都保存在zone结构体里。
zone结构体是描述物理内存区域的数据结构,所以能理解为每个zone都有一个自己的伙伴分配器
其中包含了伙伴系统需要的所有属性。下面是 zone中伙伴系统需要使用到的属性:
struct zone {
unsigned long watermark[NR_WMARK];//伙伴系统需要使用水位线来控制内存的分配和回收,其中
//watermark表示内存空闲的水位线,watermark boost表示需要提高的水位线。
long lowmem_reserve[MAX_NR_ZONES];
unsigned long zone_start_pfn;//表示该区域的起始页框号。
unsigned long managed_pages;//表示该区域管理的页框总数,包括已分配和未分配的页框。
unsigned long spanned_pages;//示该区域所管理的所有物理页框的数量。
unsigned long present_pages;//
struct free_area free_area[MAX_ORDER]; //伙伴系统需要在每个区域内维护可用内存块的链表,这些链表就存储在free _area数组中。
unsigned long flags;//存储了该区域的一些状态信息,例如该区域是否允许交换、是否允许内存高端地址分配等。
...
} ____cacheline_internodealigned_in_smp;
struct free_area {
struct list_head free_list[MIGRATE_TYPES];
unsigned long nr_free;
};
结构体free_ area 是伙伴系统中管理每个块大小的可用空闲页框链表的数据结构。它定义了一个数组free_ list, 每个元素都是一个链表头, 代表了一个特定大小的空闲页框的链表。
nr_ free记录了该数组中链表中可用的空闲页框数量。nr_ free记录的是free_ list数组中存储的空闲页的数量。因为每个空闲块都对应一个空闲页,所以nr_ free等于所有free_ list数组中链表节点个数之和。free_ area 是作为一个zone结构体中的一部分来使用的。
zone是Linux内核中的一个管理物理内存的单元,一个物理内存可以被划分为多个zone,每个zone中包含了一定数量的物理页框。
在一个zone中,free_ area 数组中的每个元素都代表了该zone中的一一个固定大小的页框。
zone中的页框大小按照2的幂次方增加,即从小到大排列,如free_ area[0] 示一个页框大小为2^0个物理页框的链表,
free_ area[1] 示-个页框大小为2^1个物理页框的链表,
依此类推,直到最大的页框大小2^{MAX_ ORDER}。
在伙伴系统中,每个可用的空闲页框都被划分到适当大小的free_ area 链表中。
例如,当系统需要一个大小为2^3个物理页框的连续空间时,会在free_ area[3] 的链表中查找。
如果free_ area[3]中没有可用的空闲页框,系统会向较大的链表free_ area[4] 甚至更大的链表中寻找可用空闲页框。
如果所有的free_ area链表中都没有可用的空闲页框,那么系统就需要等待一段时间,直到有足够数量的页框空闲为止。
在释放一个空闲页框时,它会被添加到对应的free_ area链表中,等待被再次分配。
通过这种方式,伙伴系统可以高效地管理可用的空闲页框,同时避免了内存碎片化的问题。
free_ area中free_ list链表下一个结点对应的就是page中的buddy_ list,
再通过计算地址即可得到需要的page,后续的笔记会记录Linux的数据机构。
当前这个物理区域会根据水位线(下面会说)来判断当前区域的内存是否足够
内存不足的话会考虑去备用区域寻找页 进行分配
先看看内存节点结构体
struct page
{
//节点结构体里面还有个数据结构体来表示它的zone
typedef struct pglist_ data {
struct zone node_ zones [MAX_ NR_ ZONES]; //内存区域数组
struct zonelist node_ zonelists[MAX_ ZONELISTS]; //备用区域列表
int nr_ _zones; //该节点包含的内存区域数量
}
};
两个借用规则 如果能满足当前zone的借用规则 就被放到了这个zone的备用区域列表中
借用必须遵守规则: 节点规则(嵌入式可以不关心 我们就一个节点)
一个内存节点的某个区域类型可以从另-一个内存节点的相同区域类型借用物理页,比如节点0的普通区
域可以从节点1的普通区域借用物理页;
借用必须遵守规则: 高低区域规则
高区域类型可以从低区域类型借用物理页,比如普通区域可以从DMA区域借用物理页; .
低区域类型不能从高区域类型借用物理页。比如DMA区域不能从普通区域借用物理页。
上面的区域结构体中 有个数组变量来表示水位线
首选的内存区域什么情况下从备用区域借用物理页呢?每个内存区域有3个水线
a.高水线(high) :如果内存区域的空闲页数大于高水线,说明内存区域的内存充足;
b.低水线(low) :如果内存区域的空闲页数小于低水线,说明内存区域的内存轻微不足;
C.最低水线(min) : 如果内存区域的空闲页数小于最低水线,说明廖内存区域的内存严重不足。
第一次尝试使用低水线 如果首选内存区域的空闲页的数量小于低水线 就从备用区域借用物理页 如果还是失败 就换起所有内存节点的页回收线程 用异步的方式进行回收页 再一次尝试最低水线
如下能看到每个节点 对应的每个区域 里面分别的水线
han@ubuntu:~$ cat /proc/zoneinfo
Node 0, zone DMA
per-node stats
nr_inactive_anon 10820
nr_active_anon 320858
nr_inactive_file 231022
nr_active_file 234555
nr_unevictable 16
nr_slab_reclaimable 44884
nr_slab_unreclaimable 18674
nr_isolated_anon 0
nr_isolated_file 0
workingset_nodes 0
workingset_refault 0
workingset_activate 0
workingset_restore 0
workingset_nodereclaim 0
nr_anon_pages 320578
nr_mapped 153680
nr_file_pages 476705
nr_dirty 26878
nr_writeback 0
nr_writeback_temp 0
nr_shmem 11129
nr_shmem_hugepages 0
nr_shmem_pmdmapped 0
nr_file_hugepages 0
nr_file_pmdmapped 0
nr_anon_transparent_hugepages 0
nr_unstable 0
nr_vmscan_write 0
nr_vmscan_immediate_reclaim 0
nr_dirtied 287542
nr_written 244390
nr_kernel_misc_reclaimable 0
pages free 3968
min 77
low 96
high 115
spanned 4095
present 3997
managed 3976
protection: (0, 2445, 3369, 3369, 3369)
nr_free_pages 3968
nr_zone_inactive_anon 0
nr_zone_active_anon 0
nr_zone_inactive_file 0
nr_zone_active_file 0
nr_zone_unevictable 0
nr_zone_write_pending 0
nr_mlock 0
nr_page_table_pages 0
nr_kernel_stack 0
nr_bounce 0
nr_zspages 0
nr_free_cma 0
numa_hit 1
numa_miss 0
numa_foreign 0
numa_interleave 0
numa_local 1
numa_other 0
pagesets
cpu: 0
count: 0
high: 0
batch: 1
vm stats threshold: 6
cpu: 1
count: 0
high: 0
batch: 1
vm stats threshold: 6
cpu: 2
count: 0
high: 0
batch: 1
vm stats threshold: 6
cpu: 3
count: 0
high: 0
batch: 1
vm stats threshold: 6
node_unreclaimable: 0
start_pfn: 1
Node 0, zone DMA32
pages free 43834
min 12202
low 15252
high 18302
spanned 1044480
present 782288
managed 759709
protection: (0, 0, 924, 924, 924)
nr_free_pages 43834
nr_zone_inactive_anon 8454
nr_zone_active_anon 245858
nr_zone_inactive_file 181846
nr_zone_active_file 177532
nr_zone_unevictable 4
nr_zone_write_pending 26534
nr_mlock 4
nr_page_table_pages 5641
nr_kernel_stack 8472
nr_bounce 0
nr_zspages 0
nr_free_cma 0
numa_hit 4159869
numa_miss 0
numa_foreign 0
numa_interleave 192366
numa_local 4159869
numa_other 0
pagesets
cpu: 0
count: 50
high: 378
batch: 63
vm stats threshold: 36
cpu: 1
count: 335
high: 378
batch: 63
vm stats threshold: 36
cpu: 2
count: 300
high: 378
batch: 63
vm stats threshold: 36
cpu: 3
count: 121
high: 378
batch: 63
vm stats threshold: 36
node_unreclaimable: 0
start_pfn: 4096
Node 0, zone Normal
pages free 5754
min 4615
low 5768
high 6921
spanned 262144
present 262144
managed 236760
protection: (0, 0, 0, 0, 0)
nr_free_pages 5754
nr_zone_inactive_anon 2366
nr_zone_active_anon 75000
nr_zone_inactive_file 49176
nr_zone_active_file 57023
nr_zone_unevictable 12
nr_zone_write_pending 344
nr_mlock 12
nr_page_table_pages 5556
nr_kernel_stack 7096
nr_bounce 0
nr_zspages 0
nr_free_cma 0
numa_hit 839519
numa_miss 0
numa_foreign 0
numa_interleave 223408
numa_local 839519
numa_other 0
pagesets
cpu: 0
count: 253
high: 378
batch: 63
vm stats threshold: 24
cpu: 1
count: 202
high: 378
batch: 63
vm stats threshold: 24
cpu: 2
count: 325
high: 378
batch: 63
vm stats threshold: 24
cpu: 3
count: 291
high: 378
batch: 63
vm stats threshold: 24
node_unreclaimable: 0
start_pfn: 1048576
Node 0, zone Movable
pages free 0
min 0
low 0
high 0
spanned 0
present 0
managed 0
protection: (0, 0, 0, 0, 0)
Node 0, zone Device
pages free 0
min 0
low 0
high 0
spanned 0
present 0
managed 0
protection: (0, 0, 0, 0, 0)
han@ubuntu:~$