一、拆解:
本文以MTK平台2G RAM的设备为例,对RAM进行详细的拆解,对于2G的手机在设置->存储中,
会看到总内存的大小为2G,这里的2G也可以通过ADB命令来获取到:
(1)获取meminfo: cat /proc/meminfo
MemTotal: 1860024 kB
(2)获取reserve的内存:cat /proc/mtk_memcfg/total_reserve
237128 kB
上面两项的和: 1860024 kB + 237128 kB = 2097152kB = 2GB
二、reserve内存详细:
mtk reserve的内存大小,可以通过cat /proc/mtk_memcfg/目录下对应的节点来获取到,对应的代码在:
./kernel-4.9/drivers/misc/mediatek/mem/mtk_memcfg_reserve_info.c文件中,查看所有reserve的memory大小:
cat /proc/mtk_memcfg/total_reserve
237128 kB
查看reserve内存的详细:
# cat /proc/mtk_memcfg/reserve_memory (单位Byte)
*mblock-13-ccci: 69206016 //modem射频模块使用
ram_console-reserved-memory@47c80000: 65536 //用于保存ram_console临时log使用
pstore-reserved-memory@47c90000: 917504
minirdump-reserved-memory@47d70000: 65536
*mblock-4-atf-reserved: 262144
*mblock-7-atf-log-reserved: 262144
*mblock-6-teei-reserved: 62914560
*mblock-10-SPM-reserved: 65536
*mblock-14-ccci: 23068672
*mblock-12-ccci: 1507328
*mblock-8-framebuffer: 15466496
*mblock-5-SSPM-reserved: 1048576
mblock-3-log_store: 524288
*mblock-1-dramc-rk0: 4096
*reserve-memory-scp_share: 3670016
*soter-shared-mem: 5242880
*reserve-memory-sspm_share: 5308416
*mblock-11-SCP-reserved: 6291456
*consys-reserve-memory: 4194304
*wifi-reserve-memory: 3145728
*mblock-9-atf-ramdump-memory: 524288
*mblock-2-dramc-rk1: 4096
kernel(text): 12582912 //包含了编译后生成的代码
kernel(data): 9304788 //保存大部分内核的变量
kernel(page): 4259840 //用于page保存struct page对象数组等
kernel(other): 12911916
三、meminfo内存详细:
1、meminfo信息:
此部分的拆解可以先参考文章内存管理二 proc/meminfo 文件详解中部分。
# cat /proc/meminfo
MemTotal: 1860024 kB
MemFree: 67724 kB
MemAvailable: 913656 kB
Buffers: 15948 kB
Cached: 854160 kB
SwapCached: 0 kB
Active: 824052 kB
Inactive: 623456 kB
Active(anon): 345108 kB
Inactive(anon): 235808 kB
Active(file): 478944 kB
Inactive(file): 387648 kB
Unevictable: 1820 kB
Mlocked: 0 kB
HighTotal: 1366968 kB
HighFree: 16904 kB
LowTotal: 493056 kB
LowFree: 50820 kB
SwapTotal: 1048572 kB
SwapFree: 1023820 kB
Dirty: 224 kB
Writeback: 0 kB
AnonPages: 579104 kB
Mapped: 596700 kB
Shmem: 1696 kB
Slab: 154056 kB
SReclaimable: 35628 kB
SUnreclaim: 118428 kB
KernelStack: 13776 kB
PageTables: 31556 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 1978584 kB
Committed_AS: 50239164 kB
VmallocTotal: 499712 kB
VmallocUsed: 0 kB
VmallocChunk: 0 kB
CmaTotal: 0 kB
CmaFree: 0 kB
其中kernel 部分使用的内存可以统计如下:
kernel use = Slab(SReclaimable + SUnreclaim) + KernelStack + PageTables + 内存黑洞(alloc_pages)
因为alloc_pages这块内存没有被统计下来,不清楚具体的大小,剩余部分kernel 使用的内存大小也可以通过如下方式获取:
# dumpsys meminfo
Total RAM: 1,860,024K (status normal)
Free RAM: 605,066K ( 65,630K cached pss + 488,260K cached kernel + 51,176K free)
Used RAM: 1,228,266K (1,036,818K used pss + 191,448K kernel)
可以看到如上Used RAM中kernel = 191,448K = Slab(SReclaimable + SUnreclaim) + KernelStack + PageTables的大小;
2、实际案例分解:
(1)log信息
在内核RAM出现低的问题时(如Out of memory)或者内存紧张时,kernel log通常会打印出对应的内存信息:
kernel-4.9/mm/page_alloc.c
void show_free_areas(unsigned int filter)
{
for_each_populated_zone(zone) {
show_node(zone);
printk("%s"
" free:%lukB"
" min:%lukB"
" slab_reclaimable:%lukB"
" slab_unreclaimable:%lukB"
" kernel_stack:%lukB"
" pagetables:%lukB"
" unstable:%lukB"
......
printk("lowmem_reserve[]:");
for (i = 0; i < MAX_NR_ZONES; i++)
printk(" %ld", zone->lowmem_reserve[i]);
printk("\n");
}
for_each_populated_zone(zone) {
for (order = 0; order < MAX_ORDER; order++) {
printk("%lu*%lukB ", nr[order], K(1UL) << order);
if (nr[order])
show_migration_types(types[order]);
}
......
}
下面看对应的log:
//申请2^order * 4KB 的大小的内存失败
[457262.665292] (0)[11421:InputReader]InputReader invoked oom-killer: gfp_mask=0x240c2c0(GFP_KERNEL|__GFP_NOWARN|__GFP_COMP|__GFP_ZERO), nodemask=0, order=3, oom_score_adj=-900
//内存被分为不同的node,这里显示node0对应的内存分配状态
[457263.144951] (3)[11421:InputReader]Node 0 active_anon:55232kB inactive_anon:56972kB active_file:206756kB inactive_file:138728kB
unevictable:1820kB isolated(anon):0kB isolated(file):0kB mapped:160044kB dirty:0kB writeback:0kB
shmem:1328kB writeback_tmp:0kB unstable:0kB pages_scanned:0 all_unreclaimable? no
//node0节点对应Normal memory内存的情况
[457263.144964] Normal free:21892kB min:2796kB low:3492kB high:4188kB
active_anon:0kB inactive_anon:8kB active_file:188kB inactive_file:116kB unevictable:32kB writepending:0kB
present:532224kB managed:493076kB mlocked:0kB slab_reclaimable:15792kB slab_unreclaimable:415304kB kernel_stack:7448kB pagetables:9444kB
bounce:0kB free_pcp:360kB local_pcp:24kB free_cma:0kB
//node0节点对应HighMem的memory情况
[457263.144986] HighMem free:787836kB min:512kB low:2184kB high:3856kB
active_anon:55356kB inactive_anon:56964kB active_file:206568kB inactive_file:138516kB
unevictable:1788kB writepending:0kB present:1435064kB managed:1366648kB mlocked:0kB slab_reclaimable:0kB
slab_unreclaimable:0kB kernel_stack:0kB pagetables:0kB bounce:0kB free_pcp:1048kB local_pcp:636kB free_cma:0kB
//内存页的分配状态
[457262.665710] Normal: 1753*4kB (UMEH) 361*8kB (UMEH) 62*16kB (UMEH) 6*32kB (H) 0*64kB 0*128kB 0*256kB 0*512kB 0*1024kB 0*2048kB 0*4096kB = 11084kB
[457262.665744] HighMem: 19966*4kB (UH) 27391*8kB (UMH) 14524*16kB (UMH) 4594*32kB (UMH) 971*64kB (UMH) 281*128kB (UH) 32*256kB (UH) 1*512kB (U) 1*1024kB (U) 0*2048kB 0*4096kB = 786224kB
从上面的log可以看出node0分为Normal zone和HighMem zone,
Normal 和 HighMem的 managed对应大小相加:
Normal managed 493076kB + HighMem managed 1366648kB = 1859724kB = MemTotal
另外meminfo中:
HighTotal: 1366968 kB ===》 HighMem managed 1366648kB
LowTotal: 493056 kB ===》 Normal managed 493076kB
(2)页面类型介绍:
从上面触发out of memory的log中可以看到,剩余的内存还是比较大的,为何申请32Kb的内存会出现outmemory呢?
Normal free:11072kB
HighMem free:786120kB
这里就需要注意到页面的类型:
Normal: 1753*4kB (UMEH) 361*8kB (UMEH) 62*16kB (UMEH) 6*32kB (H) 0*64kB
0*128kB 0*256kB 0*512kB 0*1024kB 0*2048kB 0*4096kB = 11084kB
HighMem: 19966*4kB (UH) 27391*8kB (UMH) 14524*16kB (UMH) 4594*32kB (UMH)
971*64kB (UMH) 281*128kB (UH) 32*256kB (UH) 1*512kB (U) 1*1024kB (U) 0*2048kB 0*4096kB = 786224kB
可以看到上面的剩余的内存后面都带有UMEH这些字母,可以在如下代码中查询到这些字母的含义:
//kernel-4.9/mm/page_alloc.c
static void show_migration_types(unsigned char type)
{
static const char types[MIGRATE_TYPES] = {
[MIGRATE_UNMOVABLE] = 'U',
[MIGRATE_MOVABLE] = 'M',
[MIGRATE_RECLAIMABLE] = 'E',
[MIGRATE_HIGHATOMIC] = 'H',
#ifdef CONFIG_CMA
[MIGRATE_CMA] = 'C',
#endif
#ifdef CONFIG_MEMORY_ISOLATION
[MIGRATE_ISOLATE] = 'I',
#endif
};
char tmp[MIGRATE_TYPES + 1];
char *p = tmp;
int i;
for (i = 0; i < MIGRATE_TYPES; i++) {
if (type & (1 << i))
*p++ = types[i];
}
*p = '\0';
printk("(%s) ", tmp);
}
具体的含义如下:
enum {
MIGRATE_UNMOVABLE, /* 页框内容不可移动,在内存中位置必须固定,无法移动到其他地方,核心内核分配的大部分页面都属于这一类。 */
MIGRATE_RECLAIMABLE, /* 页框内容可回收,不能直接移动,但是可以回收,因为还可以从某些源重建页面,比如映射文件的数据属于这种类别,kswapd会按照一定的规则,周期性的回收这类页面。 */
MIGRATE_MOVABLE, /* 页框内容可移动,属于用户空间应用程序的页属于此类页面,它们是通过页表映射的,因此我们只需要更新页表项,并把数据复制到新位置就可以了
* 当然要注意,一个页面可能被多个进程共享,对应着多个页表项。
*/
MIGRATE_PCPTYPES, /* 用来表示每CPU页框高速缓存的数据结构中的链表的迁移类型数目 */
MIGRATE_RESERVE = MIGRATE_PCPTYPES,
#ifdef CONFIG_CMA
MIGRATE_CMA, /* 预留一段的内存给驱动使用,但当驱动不用的时候,伙伴系统可以分配给用户进程用作匿名内存或者页缓存。而当驱动需要使用时,就将进程占用的内存通过回收或者迁移的方式将之前占用的预留内存腾出来,供驱动使用。 */
#endif
#ifdef CONFIG_MEMORY_ISOLATION
MIGRATE_ISOLATE, /* 不能从这个链表分配页框,因为这个链表专门用于NUMA结点移动物理内存页,将物理内存页内容移动到使用这个页最频繁的CPU */
#endif
MIGRATE_TYPES
};
可见带有这些标志的内存都有对应的类型,不能随意的去分配使用,故触发out of memory。
作者:frank_zyp
您的支持是对博主最大的鼓励,感谢您的认真阅读。
本文无所谓版权,欢迎转载。