平台: msm8x25 系统: android 4.1 内核: 3.4.0 原文: http://blog.csdn.net/kris_fei/article/details/17243527
概念 由于系统的连续物理内存有限,这使得非连续物理内存的使用在linux内核中出现, 这叫vmalloc机制。和前者一样,vmalloc机制中的虚拟地址也是连续的。
Vmallocinfo Vmalloc机制并不是狭义地指使用vmalloc函数分配,其他还有如ioremap, iotable_init等。可以从/proc/vmallocinfo获取到此信息: #cat /proc/vmallocinfo 0xf3600000-0xf36ff0001044480 binder_mmap+0xb0/0x224 ioremap ……….. 0xf6680000-0xf66c1000 266240 kgsl_page_alloc_map_kernel+0x98/0xe8 ioremap 0xf6700000-0xf67ff0001044480 binder_mmap+0xb0/0x224 ioremap ……………. 0xf6f00000-0xf6f41000 266240 kgsl_page_alloc_map_kernel+0x98/0xe8 ioremap 0xf7200000-0xf72ff0001044480 binder_mmap+0xb0/0x224 ioremap 0xfa000000-0xfa001000 4096 iotable_init+0x0/0xb0 phys=c0800000 ioremap …………….. 0xfa105000-0xfa106000 4096 iotable_init+0x0/0xb0 phys=a9800000 ioremap 0xfa200000-0xfa3000001048576 pmd_empty_section_gap+0x0/0x3c ioremap 0xfa300000-0xfa4000001048576 iotable_init+0x0/0xb0 phys=100000 ioremap 0xfa400000-0xfa5000001048576 iotable_init+0x0/0xb0 phys=aa500000 ioremap 0xfa500000-0xfa6000001048576 pmd_empty_section_gap+0x0/0x3c ioremap 0xfa701000-0xfa702000 4096 iotable_init+0x0/0xb0 phys=c0400000 ioremap ………….. 0xfa800000-0xfa9000001048576 pmd_empty_section_gap+0x0/0x3c ioremap 0xfa900000-0xfb60000013631488 iotable_init+0x0/0xb0 phys=ac000000 ioremap 0xfefdc000-0xff000000 147456 pcpu_get_vm_areas+0x0/0x56c vmalloc
上面的列数意思依次是:虚拟地址,分配大小,哪个函数分配的,物理地址,分配类型。 后面会提到vmalloc size的划分是按照此info来修改的。
分配标志 是否划分到vamlloc区域主要是以下重要的标志来决定的: File: kernel/include/linux/vmalloc.h /* bits in flags ofvmalloc's vm_struct below */ #defineVM_IOREMAP 0x00000001 /* ioremap()and friends */ #define VM_ALLOC0x00000002 /* vmalloc() */ #defineVM_MAP 0x00000004 /* vmap()ed pages */ #defineVM_USERMAP 0x00000008 /* suitable forremap_vmalloc_range */ #defineVM_VPAGES 0x00000010 /* buffer for pages was vmalloc'ed */ #defineVM_UNLIST 0x00000020 /* vm_struct is not listed in vmlist */ /* bits [20..32]reserved for arch specific ioremap internals */ Vmallocinfo中的函数,你可以对照源码看一下,在设置flag的时候就会有VM_IOREMAP, VM_ALLOC这些标志。
Vmalloc区域 Vmalloc的区域是由两个宏变量来表示: VMALLOC_START,VMALLOC_END. File: kernel/arch/arm/include/asm/pgtable.h #defineVMALLOC_OFFSET (8*1024*1024) #defineVMALLOC_START (((unsigned long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1)) #defineVMALLOC_END 0xff000000UL
VMALLOC_START:看上去会随着high_memory的值变化。 VMALLOC_OFFSET:系统会在low memory和VMALLOC区域留8M,防止访问越界。因此假如理论上vmalloc size有300M,实际可用的也是只有292M。
File: kernel/Documentation/arm/memory.txt有给出更好的解释: VMALLOC_START VMALLOC_END-1 vmalloc() / ioremap() space. Memory returned byvmalloc/ioremap will be dynamically placed in this region. Machine specificstatic mappings are also located here through iotable_init(). VMALLOC_START isbased upon the value of the high_memoryvariable, and VMALLOC_END is equal to 0xff000000.
下图摘自网络,看下VMALLOC_START和VMALLOC_END的位置。0xc0000000到VMALLOC_START为low memory虚拟地址区域。
Vmallocsize计算
有了以上知识后我们看下vmalloc size是如何分配的,目前有两种方法,kernel默认分配一个, 以及开机从cmdline分配。 1. 从cmdline分配 File: device/qcom/msm7627a/BoardConfig.mk BOARD_KERNEL_CMDLINE := androidboot.hardware=qcom loglevel=7vmalloc=200M 上面的值在build的时候会被赋值给kernel 的cmdline。 开机的时候early_vmalloc()会去读取vmalloc这个值。 File: kernel/arch/arm/mm/mmu.c static int__init early_vmalloc(char *arg) { /*cmdline中的vmalloc会被解析到vmlloc_reserve中。*/ unsigned long vmalloc_reserve = memparse(arg, NULL); /*小于16M则用16M。*/ if (vmalloc_reserve < SZ_16M) { vmalloc_reserve = SZ_16M; printk(KERN_WARNING "vmalloc area too small, limiting to %luMB\n", vmalloc_reserve >> 20); } /*大于可用虚拟地址内存则使用可用地址部分再减去32M。*/ if (vmalloc_reserve > VMALLOC_END - (PAGE_OFFSET + SZ_32M)) { vmalloc_reserve = VMALLOC_END - (PAGE_OFFSET + SZ_32M); printk(KERN_WARNING "vmalloc area is too big, limiting to %luMB\n", vmalloc_reserve >> 20); } /*计算偏移起始地址。*/ vmalloc_min = (void *)(VMALLOC_END - vmalloc_reserve);
return 0; } early_param("vmalloc",early_vmalloc);
vmalloc_min会影响arm_lowmem_limit,arm_lowmem_limit其实就是high_memory。因为此过程不是我们要分析的重点,如果有兴趣可分析kernel/arch/arm/mm/mmu.c中的sanity_check_meminfo()函数。 所以,VMALLOC_START受到了hight_memory的影响而发生了变化,最终使得vmalloc size也变化了!
2. 开机默认分配: File: kernel/arch/arm/mm/mmu.c static void * __initdata vmalloc_min = (void *)(VMALLOC_END - (240 << 20) - VMALLOC_OFFSET); 当cmdline无vmalloc参数传进来的时候,early_vmalloc()函数也不会调用到,vmalloc_min的值就会被默认传进来了,默认是240M。 后面的步骤和方法1一样了!
开机log有memory layout 信息: [ 0.000000] [cpuid: 0] Virtual kernelmemory layout: [ 0.000000] [cpuid:0] vector : 0xffff0000 - 0xffff1000 ( 4 kB) [ 0.000000] [cpuid:0] fixmap : 0xfff00000 - 0xfffe0000 (896 kB) [ 0.000000] [cpuid:0] vmalloc : 0xf3000000 - 0xff000000 ( 192MB) [ 0.000000] [cpuid:0] lowmem : 0xc0000000 - 0xf2800000 (808 MB) [ 0.000000] [cpuid:0] pkmap : 0xbfe00000 -0xc0000000 ( 2 MB) [ 0.000000] [cpuid:0] modules : 0xbf000000 - 0xbfe00000 ( 14 MB) [ 0.000000] [cpuid:0] .text : 0xc0008000 -0xc0893034 (8749 kB) [ 0.000000] [cpuid:0] .init : 0xc0894000 -0xc08cdc00 ( 231 kB) [ 0.000000] [cpuid:0] .data : 0xc08ce000 -0xc09f8eb8 (1196 kB) [ 0.000000] [cpuid:0] .bss : 0xc0a78eec -0xc0f427a8 (4903 kB)
其中看到vmalloc为192MB , cmdline中使用vmllaoc就是200M。 Lowmem为地段内存部分,可见lowmem和vmalloc中间有8M空隙。
Vmalloc该分配多大? Linux内核版本从3.2到3.3 默认的vmalloc size由128M 增大到了240M,3.4.0上的 修改Commit信息如下: To accommodate all static mappings on machines withpossible highmem usage, the default vmalloc area size is changed to 240 MB sothat VMALLOC_START is no higher than 0xf0000000 by default.
看其意思是因为开机的静态映射增加了,所以要扩大。 另外3.2到3.3版本的一个重大变化是将android引入到主线内核中。我想增大vmalloc size到240M是基于此考虑吧。当然,各家厂商都也可以基于自己平台来动态修改size的。
那么如何判断当前vmalloc size不足呢? /proc/meminfo中有vmalloc信息: VmallocTotal: 540672 kB VmallocUsed: 165268 kB VmallocChunk: 118788kB 事实上这里的VmallocUsed只是表示已经被真正使用掉的vmalloc区域,但是区域之前 的空隙也就是碎片没有被计算进去。 所以,回到前面说的/proc/vmallocinfo,假设我们的vmalloc size就是200M。那么区域 为0xf3000000- 0xff000000,从vmallocinfo中可以看到,前面大部分虚拟地址空间都用掉了,剩下0xfb600000到0xfefdc000这57M空间,假如申请了64M,那么就会失败了。
开机分配使用掉vmalloc之后到底该剩余多少目前没有具体依据,一般来说1GB RAM可以设置为400~600M。
Ref: http://www.ibm.com/developerworks/cn/linux/l-33linuxkernel/ http://www./wiki/Common_Problem:_vmalloc_too_small http://www./docs/4/4.5/Reference_Guide/s2-proc-meminfo.html
2013/11/14
|
|