Android计算开机完成之后kernel所消耗的总的内存(不可回收)

1. 开机后各可用内存和实际物理内存之间的关系

要计算开机之后剩余的总内存,首先必须弄清楚kernel在开机之后出现的几个可用内存变量之间的关系,今天以一个项目的log来分析开机之后memory的计算方式和各可用内存和总的实际内存之间的关系:

我们在开机之后kernel会有一句log如下:

[html]  view plain  copy
  1. [    0.000000] <0>-(0)[0:swapper]Memory: 3919424K/4027408K available (12244K kernel code, 1856K rwdata, 5636K rodata, 1056K init, 13278K bss, 107984K reserved)  

内存初始化信息的解读如下:

absent:表示不可用的物理内存大小。譬如有一些物理内存被BIOS保留、对kernel是不可用的,这部分物理内存被计入”absent”之中,这里没有出现。

107984k reserved:包括【initrd】和【内核代码及数据】等,其中内核代码和部分数据包含在下列统计值中:

12244k kernel code :表示kernel的代码,属于reserved memory;

1856k rwdata和5636K rodata :表示kernel的可读可写的数据和只读数据大小,属于reserved memory;

1056k init :表示init code和init data,属于reserved memory,但引导完成之后会释放给free memory;

13278k bss:表示kernel bss段的大小;

它们之间的关系如下:

reserved 包括 kernel code, data 和 init,由于它还包括initrd和其它更多的内容,所以reserved远远大于 kernel code + data + init 之和;

此时会有以下几个疑惑:

1. 这里的4027408K是从哪里计算出来的,他与实际总内存4G之间差值包含了哪些内容?

2. 3919424K又是如何计算出来的?

经过查看kernel code可以回答上面的两个问题:

[cpp]  view plain  copy
  1. void __init mem_init_print_info(const char *str)  
  2. {  
  3.     unsigned long physpages, codesize, datasize, rosize, bss_size;  
  4.     unsigned long init_code_size, init_data_size;  
  5.   
  6.     physpages = get_num_physpages();  
  7.     codesize = _etext - _stext;  
  8.     datasize = _edata - _sdata;  
  9.     rosize = __end_rodata - __start_rodata;  
  10.     bss_size = __bss_stop - __bss_start;  
  11.     init_data_size = __init_end - __init_begin;  
  12.     init_code_size = _einittext - _sinittext;  
  13. ……  
  14. printk("Memory: %luK/%luK available "  
  15.            "(%luK kernel code, %luK rwdata, %luK rodata, "  
  16.            "%luK init, %luK bss, %luK reserved"  
  17. #ifdef  CONFIG_HIGHMEM  
  18.            ", %luK highmem"  
  19. #endif  
  20.            "%s%s)\n",  
  21.            nr_free_pages() << (PAGE_SHIFT-10), physpages << (PAGE_SHIFT-10),  
  22. codesize >> 10, datasize >> 10, rosize >> 10,  
  23.            (init_data_size + init_code_size) >> 10, bss_size >> 10,  
  24.            (physpages - totalram_pages) << (PAGE_SHIFT-10),  
  25. #ifdef  CONFIG_HIGHMEM  
  26.            totalhigh_pages << (PAGE_SHIFT-10),  
  27. #endif  
  28.            str ? ", " : "", str ? str : "");  
  29. kernel_reserve_meminfo.available = nr_free_pages() << (PAGE_SHIFT - 10);  
  30.         kernel_reserve_meminfo.total = physpages << (PAGE_SHIFT - 10);  
  31.         kernel_reserve_meminfo.kernel_code = codesize >> 10;  
  32.         kernel_reserve_meminfo.rwdata = datasize >> 10;  
  33.         kernel_reserve_meminfo.rodata = rosize >> 10;  
  34.         kernel_reserve_meminfo.init = (init_data_size + init_code_size) >> 10;  
  35. kernel_reserve_meminfo.bss = bss_size >> 10;  
  36.         kernel_reserve_meminfo.reserved =  
  37.             (physpages - totalram_pages) << (PAGE_SHIFT-10);  
  38.   
  39. #ifdef CONFIG_HIGHMEM  
  40.         kernel_reserve_meminfo.highmem = totalhigh_pages << (PAGE_SHIFT - 10);  
  41. #endif  
  42. }  
有上面的代码可以看到:

3919424K = nr_free_pages() * 4;

4027408K = get_num_physpages() * 4;

这里的get_num_physpages()是遍历memory node上面所有的page得出的总的内存大小值,具体可以参见代码:

[cpp]  view plain  copy
  1. static inline unsigned long get_num_physpages(void)  
  2. {  
  3.     int nid;  
  4.     unsigned long phys_pages = 0;  
  5.   
  6.     for_each_online_node(nid)  
  7.         phys_pages += node_present_pages(nid);  
  8.   
  9.     return phys_pages;  
  10. }  
那么这里就有一个疑问,既然是遍历所有memory node上的page得出的总的内存大小,那为什么不等于总的实际物理内存4G的大小呢?刚开始在这里也是百思不得其解,最后透过整理整个memory layout发现结论,这里讲一下如何整理整个项目的memory layout。

首先在kernel log中搜索PHY layout,得出如下结果:

[html]  view plain  copy
  1. [    0.000000] <0>-(0)[0:swapper][PHY layout]atf-reserved-memory@44600000   :   0x44600000 - 0x4460ffff (0x10000)  
  2. [    0.000000] <0>-(0)[0:swapper][PHY layout]atf-ramdump-memory@44610000   :   0x44610000 - 0x4463ffff (0x30000)  
  3. [    0.000000] <0>-(0)[0:swapper][PHY layout]cache-dump-memory@44640000   :   0x44640000 - 0x4466ffff (0x30000)  
  4. [    0.000000] <0>-(0)[0:swapper][PHY layout]consys-reserve-memory   :   0xbde00000 - 0xbdffffff (0x200000)  
  5. [    0.000000] <0>-(0)[0:swapper][PHY layout]spm-reserve-memory   :   0xbe180000 - 0xbe195fff (0x16000)  
  6. [    0.000000] <0>-(0)[0:swapper][PHY layout]reserve-memory-scp_share   :   0x8f000000 - 0x8fffffff (0x1000000)  
  7. [    0.000000] <0>-(0)[0:swapper][PHY layout]spi-reserve-memory   :   0xbe160000 - 0xbe175fff (0x16000)  
  8. [    0.000000] <0>-(0)[0:swapper][PHY layout]kernel   :   0x40000000 - 0x445fffff (0x4600000)  
  9. [    0.000000] <0>-(0)[0:swapper][PHY layout]kernel   :   0x44670000 - 0x87ffffff (0x43990000)  
  10. [    0.000000] <0>-(0)[0:swapper][PHY layout]kernel   :   0x88200000 - 0x8effffff (0x6e00000)  
  11. [    0.000000] <0>-(0)[0:swapper][PHY layout]kernel   :   0x90000000 - 0xb3ffffff (0x24000000)  
  12. [    0.000000] <0>-(0)[0:swapper][PHY layout]kernel   :   0xbb000000 - 0xbddfffff (0x2e00000)  
  13. [    0.000000] <0>-(0)[0:swapper][PHY layout]kernel   :   0xbe000000 - 0xbe15ffff (0x160000)  
  14. [    0.000000] <0>-(0)[0:swapper][PHY layout]kernel   :   0xbe176000 - 0xbe17ffff (0xa000)  
  15. [    0.000000] <0>-(0)[0:swapper][PHY layout]kernel   :   0xbe196000 - 0xbe19ffff (0xa000)  
  16. [    0.000000] <0>-(0)[0:swapper][PHY layout]kernel   :   0xc0000000 - 0x13fffffff (0x80000000)  
  17. [    0.468130] <4>.(4)[1:swapper/0][PHY layout]tee_reserved_mem   :   0xbff80000 - 0xbffbffff (0x40000)  
  18. [    0.469290] <4>[PHY layout]FB (dt) :  0xbe3a0000 - 0xbff7ffff  (0x1be0000)  
  19. [    1.613119] <4>.(4)[1:swapper/0][PHY layout]ccci_md0 at LK  :  0xb4000000 - 0xbaffffff  (0x7000000)  
  20. [    1.615123] <4>.(4)[1:swapper/0][PHY layout]ccci_share_mem at LK  :  0x88000000 - 0x881fffff  (0x200000)  
将所有的地址按照顺序连接起来,发现少了两块内存,在kernel log中搜索确实内存块的起始地址的地址可以发现是被mblock_reserve所保留(注:在搜索PHY layout中所缺少的memory块都可以通过在kernel log中搜索相应的缺失块的起始地址而找到相应的reserve的块以及用途) ,最后得出memory layout如下:

end 0x13fffffff kernel size: 0x80000000
start 0xc0000000
end 0xbfffffff mblock reserve size: 0x40000
start 0xbffc0000
end 0xbffbffff tee_reserved_mem(security) size: 0x40000
start 0xbff80000
end 0xbff7ffff Frame Buffer size: 0x1be0000
start 0xbe3a0000
end 0xbe39ffff mblock reserve size: 0x200000
start 0xbe1a0000
end 0xbe19ffff kernel size: 0xa000
start 0xbe196000
end 0xbe195fff spm-reserve-memory size: 0x16000
start 0xbe180000
end 0xbe17ffff kernel size: 0xa000
start 0xbe176000
end 0xbe175fff spi-reserve-memory size: 0x16000
start 0xbe160000
end 0xbe15ffff kernel size: 0x160000
start 0xbe000000
end 0xbdffffff consys-reserve-memory(BT&WIFI) size: 0x200000
start 0xbde00000
end 0xbddfffff kernel size: 0x2e00000
start 0xbb000000
end 0xbaffffff ccci_md0(Modem) size: 0x7000000
start 0xb4000000
end 0xb3ffffff kernel size: 0x24000000
start 0x90000000
end 0x8fffffff reserve-memory-scp_share size: 0x1000000
start 0x8f000000
end 0x8effffff kernel size: 0x6e00000
start 0x88200000
end 0x881fffff ccci_share_mem(Modem) size: 0x200000
start 0x88000000
end 0x87ffffff kernel size: 0x43990000
start 0x44670000
end 0x4466ffff cache-dump-memory size: 0x30000
start 0x44640000
end 0x4463ffff atf-ramdump-memory(security) size: 0x30000
start 0x44610000
end 0x4460ffff atf-reserved-memory(Security) size: 0x10000
start 0x44600000
end 0x445fffff kernel size: 0x4600000
start 0x40000000

我们用总的内存4G减去所有reserve的内存:

4*1024*1024*1024-(0x10000 + 0x30000 + 0x30000 + 0x200000 + 0x1000000 + 0x7000000 + 0x200000 + 0x16000 + 0x16000 + 0x200000 + 0x1be0000 + 0x40000 + 0x40000) = 4027408K,这回答了文章开头的第一个问题,所以开机get_num_physpages()所获得的总的内存是实际的物理内存减掉kernel所reserve的所有的内存块之后剩余的内存。

第二个问题3919424K,他等于nr_free_pages() * 4,这个值其实就是4027408K减掉kernel此时用于保留reserved(initrd、初始化代码init、内核代码及数据)的数据之后所剩余的内存。即4027408K - (107984K + absent(如果有)) = 3919424K,这个值就是实际开机运行之后所能使用的最大内存,讲到这里就会有一个疑问,在执行cat /proc/meminfo所得到的结果中:

[cpp]  view plain  copy
  1. cat proc/meminfo  
  2. MemTotal:        3922896 kB  
  3. MemFree:         1629172 kB  
  4. MemAvailable:    2602560 kB  
  5. Buffers:           13848 kB  
  6. Cached:           994316 kB  
  7. SwapCached:            0 kB  
此时的MemTotal的值大于3919424K,此时3922896K - 3919424K = 3472K.

那么这里的3472K去了是从哪里回收回来的呢?通过查阅相关资料,发现:

initrd和初始化代码init在引导完成之后会被释放掉,所以最终的可用内存会比dmesg显示的available更多一点,相应的源代码可参见函数:

free_initrd_mem() 和 free_initmem();

查看代码之后再kernel log中搜索Freeing得出如下结论:

[cpp]  view plain  copy
  1. [    2.321286] <4>.(4)[1:swapper/0]Freeing initrd memory: 2340K (ffffffc005000000 - ffffffc005249000)  
  2. [    5.318178] <4>.(4)[1:swapper/0]Freeing unused kernel memory: 1056K (ffffffc0011f9000 - ffffffc001301000)  
  3. [    5.319419] <4>.(4)[1:swapper/0]Freeing alternatives memory: 76K (ffffffc001301000 - ffffffc001314000)  
这里的76K + 1056K + 2340K = 3472K,而3472K + 3919424K = 3922896K,刚好就是meminfo里面的MemTotal的大小。 (注:大多数log开始打出来的可用内存和后面free掉的内存之和相加等于meminfo的MemTotal值,有部分时候这两者之和可能略小于meminfo的MemTotal的值,这是由于log打印问题导致log未完全打印出所有的freeing的memory块(大多数情况如果按照以上方式计算出来的结果如果不等于meminfo的MemTotal可以大胆的怀疑是log未打全),但是meminfo里面统计到了这部分内存)

到这里这几个总的可用内存和实际的物理内存之间的关系算是理清了。

2. 计算Android开机之后kernel消耗的物理内存

通过第一节的介绍我们可以得出在开机之后kernel所reserve的内存计算公式就是:

实际的物理内存 - meminfo的Memtotal值,争对此例就是:

4*1024*1024K - 3922896K = 271408K

而Android开机之后kernel实际消耗的物理内存还得加上kernel stack,slab,shmem,pagetables,vmalloc_used的大小,当然以上的变量大小是变化的。

所以这里仅仅只是值开机之后的kernel消耗的内存大小,而以上各变量之和可以用dumpsys meminfo中的一个值来概括:

[cpp]  view plain  copy
  1. Total RAM: 3,922,896K (status normal)  
  2. Free RAM: 2,290,037K (  129,229K cached pss +   913,696K cached kernel +   999,820K free +    51,584K ion cached +   195,708K gpu cached)  
  3. Used RAM: 1,635,654K (1,184,850K used pss +   366,316K kernel +    40,960K trace buffer +    43,528K ion disp +         0K cma usage)  
  4. Lost RAM:    -2,803K  
  5.      ZRAM:         8K physical used for         0K in swap (1,961,444K total swap)  
  6. Tuning: 192 (large 384), oom   322,560K, restore limit   107,520K (high-end-gfx)  

就是Used RAM中的kernel选项的值,在framework中的定义如下:

[cpp]  view plain  copy
  1. public long getKernelUsedSizeKb() {  
  2.     return mInfos[Debug.MEMINFO_SHMEM] + mInfos[Debug.MEMINFO_SLAB]  
  3.             + mInfos[Debug.MEMINFO_VM_ALLOC_USED] + mInfos[Debug.MEMINFO_PAGE_TABLES]  
  4.             + mInfos[Debug.MEMINFO_KERNEL_STACK];  
  5. }  
这个值在使用当中是不断变化的,只有kernel reserve部分的值是恒定不变的,无论Android使用多长时间, 所以这里特指在Android开机之后的时刻的值:

即4*1024*1024K - 3922896K + 366316K = 637724K

讲到这里基本就搞清楚了开机kernel所消耗内存大小的计算方法。

猜你喜欢

转载自blog.csdn.net/CodingNotes/article/details/80306341