Linux kernel在引导过程中会在dmesg中报告如下的内存初始化信息,其实此时引导过程并未完成,initrd和init所占的内存尚未释放,最终kernel可用的内存比dmesg报告的available内存还会更多一点。
# dmesg | grep Memory
[ 0.000000] Memory: 3789320k/4915200k available (6243k kernel code, 795332k absent, 330548k reserved, 4180k data, 1604k init)
1
2
# dmesg | grep Memory
[0.000000]Memory:3789320k/4915200kavailable(6243kkernelcode,795332kabsent,330548kreserved,4180kdata,1604kinit)
要理解以上信息的含义,还是看源代码吧,这条信息是在 mem_init() 函数中输出的:
void __init mem_init(void)
{
long codesize, reservedpages, datasize, initsize;
unsigned long absent_pages;
pci_iommu_alloc();
/* clear_bss() already clear the empty_zero_page */
register_page_bootmem_info();
/* this will put all memory onto the freelists */
totalram_pages = free_all_bootmem();
absent_pages = absent_pages_in_range(0, max_pfn);
reservedpages = max_pfn - totalram_pages - absent_pages;
after_bootmem = 1;
codesize = (unsigned long) &_etext - (unsigned long) &_text;
datasize = (unsigned long) &_edata - (unsigned long) &_etext;
initsize = (unsigned long) &__init_end - (unsigned long) &__init_begin;
/* Register memory areas for /proc/kcore */
kclist_add(&kcore_vsyscall, (void *)VSYSCALL_START,
VSYSCALL_END - VSYSCALL_START, KCORE_OTHER);
printk(KERN_INFO "Memory: %luk/%luk available (%ldk kernel code, "
"%ldk absent, %ldk reserved, %ldk data, %ldk init)\n",
nr_free_pages() << (PAGE_SHIFT-10),
max_pfn << (PAGE_SHIFT-10),
codesize >> 10,
absent_pages << (PAGE_SHIFT-10),
reservedpages << (PAGE_SHIFT-10),
datasize >> 10,
initsize >> 10);
}
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
29
30
31
32
33
34
35
36
void__initmem_init(void)
{
longcodesize,reservedpages,datasize,initsize;
unsignedlongabsent_pages;
pci_iommu_alloc();
/* clear_bss() already clear the empty_zero_page */
register_page_bootmem_info();
/* this will put all memory onto the freelists */
totalram_pages=free_all_bootmem();
absent_pages=absent_pages_in_range(0,max_pfn);
reservedpages=max_pfn-totalram_pages-absent_pages;
after_bootmem=1;
codesize=(unsignedlong)&_etext-(unsignedlong)&_text;
datasize=(unsignedlong)&_edata-(unsignedlong)&_etext;
initsize=(unsignedlong)&__init_end-(unsignedlong)&__init_begin;
/* Register memory areas for /proc/kcore */
kclist_add(&kcore_vsyscall,(void*)VSYSCALL_START,
VSYSCALL_END-VSYSCALL_START,KCORE_OTHER);
printk(KERN_INFO"Memory: %luk/%luk available (%ldk kernel code, "
"%ldk absent, %ldk reserved, %ldk data, %ldk init)\n",
nr_free_pages()<
max_pfn<
codesize>>10,
absent_pages<
reservedpages<
datasize>>10,
initsize>>10);
}
以上代码中,free_all_bootmem()需要详细解释:在内核引导的初始阶段,buddy allocator和slab allocator等内存管理机制尚未就绪,所以使用的是bootmem allocator,到运行mem_init()的时候,bootmem的历史任务已经完成,所以调用free_all_bootmem()把它管理的内存中未分配的部分全都释放掉。
absent_pages_in_range()用于统计物理内存中对kernel不可用的部分,因为有一些物理内存是被BIOS保留的,kernel用不了。
在全部物理内存中,除掉bootmem释放出来的空闲内存和kernel无法使用的absent内存,剩下的就是在kernel引导过程中已经分配掉的内存,称为reserved内存。Reserved内存主要包括initrd、初始化代码init、内核代码及数据(内核数据是动态变化的,这里所说的是引导阶段截至mem_init为止所产生的数据,包括存放页描述符struct page的mem_map[]数组等)。
注:initrd和初始化代码init在引导完成之后会被释放掉,所以最终的可用内存会比dmesg显示的available更多一点,相应的源代码可参见:
arch/x86/mm/init.c: free_initrd_mem() 和 free_initmem()。
在dmesg中可以看到释放时输出的日志,注意看init的大小:
# dmesg
...
[ 0.000000] Memory: 3789320k/4915200k available (6243k kernel code, 795332k absent, 330548k reserved, 4180k data, 1604k init)
...
[ 0.008325] Freeing SMP alternatives: 24k freed
...
[ 0.541160] Freeing initrd memory: 18088k freed
...
[ 0.660810] Freeing unused kernel memory: 1604k freed
...
1
2
3
4
5
6
7
8
9
10
# dmesg
...
[0.000000]Memory:3789320k/4915200kavailable(6243kkernelcode,795332kabsent,330548kreserved,4180kdata,1604kinit)
...
[0.008325]FreeingSMPalternatives:24kfreed
...
[0.541160]Freeinginitrdmemory:18088kfreed
...
[0.660810]Freeingunusedkernelmemory:1604kfreed
...
正因如此,最终用”free”命令看到的total memory比dmesg里看到的available memory更多,以本系统为例,”free”命令看到的total 3809036k,比dmesg看到的available memory 3789320k更多,(3809036k – 3789320k) = (24k + 18088k + 1604k):
# free -k
total used free shared buff/cache available
Mem: 3809036 2473876 313400 175572 1021760 837288
1
2
3
# free -k
totalusedfreesharedbuff/cacheavailable
Mem:380903624738763134001755721021760837288
所以内存初始化信息的解读如下:
3789320k/4915200k available:
分母4915200k表示物理内存的大小,
分子3789320k表示可供kernel分配的 free memory的大小;
795332k absent:
表示不可用的物理内存大小。譬如有一些物理内存被BIOS保留、对kernel是不可用的,这部分物理内存被计入”absent”之中。
330548k reserved:
包括【initrd】和【内核代码及数据】等,详见上面的解释。其中内核代码和部分数据包含在下列统计值中:
6243k kernel code :
表示kernel的代码,属于reserved memory;
4180k data :
表示kernel的数据,属于reserved memory;
1604k init :
表示init code和init data,属于reserved memory,但引导完成之后会释放给free memory。
它们之间的关系如下:
available = 物理内存 – absent – reserved
reserved 包括 kernel code, data 和 init,由于它还包括initrd和其它更多的内容,所以reserved远远大于 kernel code + data + init 之和。