Chinaunix首页 | 论坛 | 博客
  • 博客访问: 325093
  • 博文数量: 100
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 665
  • 用 户 组: 普通用户
  • 注册时间: 2015-02-02 12:43
文章分类

全部博文(100)

文章存档

2015年(100)

我的朋友

分类: LINUX

2015-05-14 14:59:18

Linux Kernel: 3.4.0

         在linux Kernel中,一开始内存相关的信息是由struct meminfo来保存的,每个物理连续的内存区域被保存为meminfo中的一个元素,也就是说在Linux使用中,整块物理内存可能是不连续的,可能其 中某一中间区域是被其他cpu给使用掉了。

         那么内存相关信息又是从哪里收集到的呢,系统在boot阶段,如u-boot会将当前物理内存linux可以使用的部分通过TAG的形式传递给linux 内核。Qualcomm使用的是叫lk的boot,不管用的是哪种boot类型,使用TAG来传递参数的原理是一样的。

         下面我们看下Linux内核是如何收集内存信息的。

Meminfo信息收集

系统启动有如下流程:

start_kernel -> setup_arch -> setup_machine_tags-> parse_tags -> parse_tag.

[html] view plaincopy
  1. static int __init parse_tag(const struct tag *tag) 
  2.     extern struct tagtable __tagtable_begin, __tagtable_end; 
  3.     struct tagtable *t; 
  4.  
  5.     for (t = &__tagtable_begin; t < &__tagtable_end; t++) 
  6.         if (tag->hdr.tag == t->tag) { 
  7.             t->parse(tag); 
  8.             break; 
  9.         } 
  10.  
  11.     return t < &__tagtable_end; 
[html] view plaincopy
  1. static int __init parse_tag(const struct tag *tag)  
  2. {  
  3.     extern struct tagtable __tagtable_begin, __tagtable_end;  
  4.     struct tagtable *t;  
  5.   
  6.     for (t = &__tagtable_begin; t < &__tagtable_end; t++)  
  7.         if (tag->hdr.tag == t->tag) {  
  8.             t->parse(tag);  
  9.             break;  
  10.         }  
  11.   
  12.     return t < &__tagtable_end;  
  13. }  

__tagtable_begin被定义在kernel/arch/arm/kernel/vmlinux.lds.S中:

[html] view plaincopy
  1. .init.tagtable : { 
  2.         __tagtable_begin = .; 
  3.         *(.taglist.init) 
  4.         __tagtable_end = .; 
  5.     } 
[html] view plaincopy
  1. .init.tagtable : {  
  2.         __tagtable_begin = .;  
  3.         *(.taglist.init)  
  4.         __tagtable_end = .;  
  5.     }  

另外,在arch/arm/kernel/setup.c中有如下函数定义:

[html] view plaincopy
  1. static int __init parse_tag_mem32(const struct tag *tag) 
  2.     return arm_add_memory(tag->u.mem.start, tag->u.mem.size); 
  3. __tagtable(ATAG_MEM, parse_tag_mem32); 
[html] view plaincopy
  1. static int __init parse_tag_mem32(const struct tag *tag)  
  2. {  
  3.     return arm_add_memory(tag->u.mem.start, tag->u.mem.size);  
  4. }  
  5. __tagtable(ATAG_MEM, parse_tag_mem32);  

__tagtable是个宏定义:

[html] view plaincopy
  1. #define __tagtable(tag, fn) \ 
  2. static const struct tagtable__tagtable_##fn __tag = { tag, fn } 
[html] view plaincopy
  1. #define __tagtable(tag, fn) \  
  2. static const struct tagtable__tagtable_##fn __tag = { tag, fn }  
里面的__tag的宏定义又如下:
[html] view plaincopy
  1. #define __tag __used__attribute__((__section__(".taglist.init"))) 
[html] view plaincopy
  1. #define __tag __used__attribute__((__section__(".taglist.init")))  

         __attribute__是一个特殊的GNU关键字,在这里的用法是:告诉编译器需要将其作用的函数或者数据放入”.taglist.init”这一段区域。

也就是说由__tagtable定义的函数将会被放在section“.taglist.init” 这个区域,而且__tagtable_begin指向的就是这个区域的首地址。所以在parse_tag()做for循环调用的时候,

必然会调用到parse_tag_mem32()。

         其中一点要注意的是,parse_tag_mem32()的TAG为ATAG_MEM, 所以在boot传过来的TAG参数如果是要定义为memory参数的话TAG一定要定义为ATAG_MEM,否则parse_tag_mem32()是无 法解析到的!

         parse_tag_mem32()调用arm_add_memory().

/*start和size参数是从boot传过来的。*/
[html] view plaincopy
  1. int __init arm_add_memory(phys_addr_t start, unsigned long size) 
  2.     /*第一次进来meminfo.nr_banks值为0.*/ 
  3.     struct membank *bank = &meminfo.bank[meminfo.nr_banks]; 
  4.     /*最多能保存NR_BANKS个bank,本平台为8.*/ 
  5.     if (meminfo.nr_banks >= NR_BANKS) { 
  6.         printk(KERN_CRIT "NR_BANKS too low, " 
  7.             "ignoring memory at 0x%08llx\n", (long long)start); 
  8.         return -EINVAL; 
  9.     } 
  10.     /*页对齐后保存物理起始地址。*/ 
  11.     size -= start & ~PAGE_MASK; 
  12.     bank->start = PAGE_ALIGN(start); 
  13.     /*保存本bank size.*/ 
  14.     bank->size = size & PAGE_MASK; 
  15.  
  16.     /* 
  17.      * Check whether this memory region has non-zero size or 
  18.      * invalid node number. 
  19.      */ 
  20.     if (bank->size == 0) 
  21.         return -EINVAL; 
  22.     /*记录当前拥有bank数量。*/ 
  23.     meminfo.nr_banks++; 
  24.     return 0; 
  25. }  
[html] view plaincopy
  1. int __init arm_add_memory(phys_addr_t start, unsigned long size)  
  2. {  
  3.     /*第一次进来meminfo.nr_banks值为0.*/  
  4.     struct membank *bank = &meminfo.bank[meminfo.nr_banks];  
  5.     /*最多能保存NR_BANKS个bank,本平台为8.*/  
  6.     if (meminfo.nr_banks >= NR_BANKS) {  
  7.         printk(KERN_CRIT "NR_BANKS too low, "  
  8.             "ignoring memory at 0x%08llx\n", (long long)start);  
  9.         return -EINVAL;  
  10.     }  
  11.     /*页对齐后保存物理起始地址。*/  
  12.     size -= start & ~PAGE_MASK;  
  13.     bank->start = PAGE_ALIGN(start);  
  14.     /*保存本bank size.*/  
  15.     bank->size = size & PAGE_MASK;  
  16.   
  17.     /*  
  18.      * Check whether this memory region has non-zero size or  
  19.      * invalid node number.  
  20.      */  
  21.     if (bank->size == 0)  
  22.         return -EINVAL;  
  23.     /*记录当前拥有bank数量。*/  
  24.     meminfo.nr_banks++;  
  25.     return 0;  
  26. }   

Meminfo检查

         在meminfo信息收集完成之后,系统会先对它作一个检查:

Start_kernel -> setup_arch -> sanity_check_meminfo.

[html] view plaincopy
  1. void __init sanity_check_meminfo(void) 
  2.     int i, j, highmem = 0; 
  3. ~~snip 
  4.     /*对每个bank都做检查。*/ 
  5.     for (i = 0, j = 0; i < meminfo.nr_banks; i++) { 
  6.         struct membank *bank = &meminfo.bank[j]; 
  7.         *bank = meminfo.bank[i]; 
  8.         /*这里表示是PAE扩展的情况???*/ 
  9.         if (bank->start > ULONG_MAX) 
  10.             highmem = 1; 
  11.  
  12. #ifdef CONFIG_HIGHMEM 
  13.         /*如果物理地址比在vmalloc_min之上或者小于内核逻辑 
  14. 映射地址空间(俗称lowmem或者地段内存),那么就被认为是高端内存。 
  15. vmalloc_min被定义为vmalloc的最低地址。关于vmalloc可以了解下linux 
  16. 的虚拟内存空间布局划分。其实它和lowmem最高地址中间还留有8M的 
  17. 空间防止越界。*/ 
  18.         if (__va(bank->start) >= vmalloc_min || 
  19.             __va(bank->start) < (void *)PAGE_OFFSET) 
  20.             highmem = 1; 
  21.  
  22.         bank->highmem = highmem; 
  23.  
  24.         /* 
  25.          * Split those memory banks which are partially overlapping 
  26.          * the vmalloc area greatly simplifying things later. 
  27.          */ 
  28.         /*表示meminfo其中的一个bank的物理地址其中一部分处于 
  29. Lowmem,一部分却又处于Highmem,这种情况需要将bank再重新划分 
  30. 成两个bank。*/ 
  31.         if (!highmem && __va(bank->start) < vmalloc_min && 
  32.             bank->size > vmalloc_min - __va(bank->start)) { 
  33.             if (meminfo.nr_banks >= NR_BANKS) { 
  34.                 printk(KERN_CRIT "NR_BANKS too low, " 
  35.                          "ignoring high memory\n"); 
  36.             } else { 
  37.                 /*将当前跟着的bank元素都往后挪一个位置,以保存新划分出来的 
  38. Bank。*/ 
  39.                 memmove(bank + 1, bank, 
  40.                     (meminfo.nr_banks - i) * sizeof(*bank)); 
  41.                 meminfo.nr_banks++; 
  42.                 i++; 
  43.                 /*保存size和start,既然代码跑这里来了,肯定为highmem了。*/ 
  44.                 bank[1].size -= vmalloc_min - __va(bank->start); 
  45.                 bank[1].start = __pa(vmalloc_min - 1) + 1; 
  46.                 bank[1].highmem = highmem = 1; 
  47.                 j++; 
  48.             } 
  49.             /*lowmem的size, start保持不变。*/ 
  50.             bank->size = vmalloc_min - __va(bank->start); 
  51.         } 
  52. #else 
  53.         bank->highmem = highmem; 
  54.         /*系统没有enable high memory时直接忽略highmem.*/ 
  55.         /* 
  56.          * Highmem banks not allowed with !CONFIG_HIGHMEM. 
  57.          */ 
  58.         if (highmem) { 
  59.             printk(KERN_NOTICE "Ignoring RAM at %.8llx-%.8llx " 
  60.                    "(!CONFIG_HIGHMEM).\n", 
  61.                    (unsigned long long)bank->start, 
  62.                    (unsigned long long)bank->start + bank->size - 1); 
  63.             continue; 
  64.         } 
  65.         /*判断物理起始地址是不是落在vmalloc区域,或者小于lowmem区域。*/ 
  66.         /* 
  67.          * Check whether this memory bank would entirely overlap 
  68.          * the vmalloc area. 
  69.          */ 
  70.         if (__va(bank->start) >= vmalloc_min || 
  71.             __va(bank->start) < (void *)PAGE_OFFSET) { 
  72.             printk(KERN_NOTICE "Ignoring RAM at %.8llx-%.8llx " 
  73.                    "(vmalloc region overlap).\n", 
  74.                    (unsigned long long)bank->start, 
  75.                    (unsigned long long)bank->start + bank->size - 1); 
  76.             continue; 
  77.         } 
  78. /*判断物理结束地址是不是落在vmalloc区域*/ 
  79.         /* 
  80.          * Check whether this memory bank would partially overlap 
  81.          * the vmalloc area. 
  82.          */ 
  83.         if (__va(bank->start + bank->size) > vmalloc_min || 
  84.             __va(bank->start + bank->size) < __va(bank->start)) { 
  85.             unsigned long newsize = vmalloc_min - __va(bank->start); 
  86.             printk(KERN_NOTICE "Truncating RAM at %.8llx-%.8llx " 
  87.                    "to -%.8llx (vmalloc region overlap).\n", 
  88.                    (unsigned long long)bank->start, 
  89.                    (unsigned long long)bank->start + bank->size - 1, 
  90.                    (unsigned long long)bank->start + newsize - 1); 
  91.             bank->size = newsize; 
  92.         } 
  93. #endif 
  94.         /*当bank的结束地址比当前的arm_lowmem_limit 还要大的话重新更新。*/ 
  95.         if (!bank->highmem && bank->start + bank->size > arm_lowmem_limit) 
  96.             arm_lowmem_limit = bank->start + bank->size; 
  97.  
  98.         j++; 
  99.     } 
  100. #ifdef CONFIG_HIGHMEM 
  101.     if (highmem) { 
  102.         const char *reason = NULL; 
  103.     /*vipt属于arm cache的一种模式,如果alias了vipt,那么Highmem就 
  104. 不会被使用了。*/ 
  105.         if (cache_is_vipt_aliasing()) { 
  106.             /* 
  107.              * Interactions between kmap and other mappings 
  108.              * make highmem support with aliasing VIPT caches 
  109.              * rather difficult. 
  110.              */ 
  111.             reason = "with VIPT aliasing cache"; 
  112.         } 
  113.         if (reason) { 
  114.             printk(KERN_CRIT "HIGHMEM is not supported %s, ignoring high memory\n", 
  115.                 reason); 
  116.             while (j > 0 && meminfo.bank[j - 1].highmem) 
  117.                 j--; 
  118.         } 
  119.     } 
  120. #endif 
  121.     meminfo.nr_banks = j; 
  122.     /* arm_lowmem_limit 以上都被认为是高端内存了。*/ 
  123.     high_memory = __va(arm_lowmem_limit - 1) + 1; 
  124.     memblock_set_current_limit(arm_lowmem_limit); 
[html] view plaincopy
  1. void __init sanity_check_meminfo(void)  
  2. {  
  3.     int i, j, highmem = 0;  
  4. ~~snip  
  5.     /*对每个bank都做检查。*/  
  6.     for (i = 0, j = 0; i < meminfo.nr_banks; i++) {  
  7.         struct membank *bank = &meminfo.bank[j];  
  8.         *bank = meminfo.bank[i];  
  9.         /*这里表示是PAE扩展的情况???*/  
  10.         if (bank->start > ULONG_MAX)  
  11.             highmem = 1;  
  12.   
  13. #ifdef CONFIG_HIGHMEM  
  14.         /*如果物理地址比在vmalloc_min之上或者小于内核逻辑  
  15. 映射地址空间(俗称lowmem或者地段内存),那么就被认为是高端内存。  
  16. vmalloc_min被定义为vmalloc的最低地址。关于vmalloc可以了解下linux  
  17. 的虚拟内存空间布局划分。其实它和lowmem最高地址中间还留有8M的  
  18. 空间防止越界。*/  
  19.         if (__va(bank->start) >= vmalloc_min ||  
  20.             __va(bank->start) < (void *)PAGE_OFFSET)  
  21.             highmem = 1;  
  22.   
  23.         bank->highmem = highmem;  
  24.   
  25.         /*  
  26.          * Split those memory banks which are partially overlapping  
  27.          * the vmalloc area greatly simplifying things later.  
  28.          */  
  29.         /*表示meminfo其中的一个bank的物理地址其中一部分处于  
  30. Lowmem,一部分却又处于Highmem,这种情况需要将bank再重新划分  
  31. 成两个bank。*/  
  32.         if (!highmem && __va(bank->start) < vmalloc_min &&  
  33.             bank->size > vmalloc_min - __va(bank->start)) {  
  34.             if (meminfo.nr_banks >= NR_BANKS) {  
  35.                 printk(KERN_CRIT "NR_BANKS too low, "  
  36.                          "ignoring high memory\n");  
  37.             } else {  
  38.                 /*将当前跟着的bank元素都往后挪一个位置,以保存新划分出来的  
  39. Bank。*/  
  40.                 memmove(bank + 1, bank,  
  41.                     (meminfo.nr_banks - i) * sizeof(*bank));  
  42.                 meminfo.nr_banks++;  
  43.                 i++;  
  44.                 /*保存size和start,既然代码跑这里来了,肯定为highmem了。*/  
  45.                 bank[1].size -= vmalloc_min - __va(bank->start);  
  46.                 bank[1].start = __pa(vmalloc_min - 1) + 1;  
  47.                 bank[1].highmem = highmem = 1;  
  48.                 j++;  
  49.             }  
  50.             /*lowmem的size, start保持不变。*/  
  51.             bank->size = vmalloc_min - __va(bank->start);  
  52.         }  
  53. #else  
  54.         bank->highmem = highmem;  
  55.         /*系统没有enable high memory时直接忽略highmem.*/  
  56.         /*  
  57.          * Highmem banks not allowed with !CONFIG_HIGHMEM.  
  58.          */  
  59.         if (highmem) {  
  60.             printk(KERN_NOTICE "Ignoring RAM at %.8llx-%.8llx "  
  61.                    "(!CONFIG_HIGHMEM).\n",  
  62.                    (unsigned long long)bank->start,  
  63.                    (unsigned long long)bank->start + bank->size - 1);  
  64.             continue;  
  65.         }  
  66.         /*判断物理起始地址是不是落在vmalloc区域,或者小于lowmem区域。*/  
  67.         /*  
  68.          * Check whether this memory bank would entirely overlap  
  69.          * the vmalloc area.  
  70.          */  
  71.         if (__va(bank->start) >= vmalloc_min ||  
  72.             __va(bank->start) < (void *)PAGE_OFFSET) {  
  73.             printk(KERN_NOTICE "Ignoring RAM at %.8llx-%.8llx "  
  74.                    "(vmalloc region overlap).\n",  
  75.                    (unsigned long long)bank->start,  
  76.                    (unsigned long long)bank->start + bank->size - 1);  
  77.             continue;  
  78.         }  
  79. /*判断物理结束地址是不是落在vmalloc区域*/  
  80.         /*  
  81.          * Check whether this memory bank would partially overlap  
  82.          * the vmalloc area.  
  83.          */  
  84.         if (__va(bank->start + bank->size) > vmalloc_min ||  
  85.             __va(bank->start + bank->size) < __va(bank->start)) {  
  86.             unsigned long newsize = vmalloc_min - __va(bank->start);  
  87.             printk(KERN_NOTICE "Truncating RAM at %.8llx-%.8llx "  
  88.                    "to -%.8llx (vmalloc region overlap).\n",  
  89.                    (unsigned long long)bank->start,  
  90.                    (unsigned long long)bank->start + bank->size - 1,  
  91.                    (unsigned long long)bank->start + newsize - 1);  
  92.             bank->size = newsize;  
  93.         }  
  94. #endif  
  95.         /*当bank的结束地址比当前的arm_lowmem_limit 还要大的话重新更新。*/  
  96.         if (!bank->highmem && bank->start + bank->size > arm_lowmem_limit)  
  97.             arm_lowmem_limit = bank->start + bank->size;  
  98.   
  99.         j++;  
  100.     }  
  101. #ifdef CONFIG_HIGHMEM  
  102.     if (highmem) {  
  103.         const char *reason = NULL;  
  104.     /*vipt属于arm cache的一种模式,如果alias了vipt,那么Highmem就  
  105. 不会被使用了。*/  
  106.         if (cache_is_vipt_aliasing()) {  
  107.             /*  
  108.              * Interactions between kmap and other mappings  
  109.              * make highmem support with aliasing VIPT caches  
  110.              * rather difficult.  
  111.              */  
  112.             reason = "with VIPT aliasing cache";  
  113.         }  
  114.         if (reason) {  
  115.             printk(KERN_CRIT "HIGHMEM is not supported %s, ignoring high memory\n",  
  116.                 reason);  
  117.             while (j > 0 && meminfo.bank[j - 1].highmem)  
  118.                 j--;  
  119.         }  
  120.     }  
  121. #endif  
  122.     meminfo.nr_banks = j;  
  123.     /* arm_lowmem_limit 以上都被认为是高端内存了。*/  
  124.     high_memory = __va(arm_lowmem_limit - 1) + 1;  
  125.     memblock_set_current_limit(arm_lowmem_limit);  
  126. }  

Vmalloc_min一开始编译的时候就被初始化的:

[html] view plaincopy
  1. static void * __initdata vmalloc_min = 
  2.          (void*)(VMALLOC_END - (240 << 20) - VMALLOC_OFFSET); 
[html] view plaincopy
  1. static void * __initdata vmalloc_min =  
  2.          (void*)(VMALLOC_END - (240 << 20) - VMALLOC_OFFSET);  
VMALLOC_END:表示vmalloc区域结束地址。

240<<20:vmalloc区域有240M大小。

VMALLOC_OFFSET:为8M。vmalloc区域和lowmem区域有8M的空闲区间,防止访问越界。

当然,vamlloc_min也可以通过cmdline的方式传到kernel中作修改。

[html] view plaincopy
  1. static int __init early_vmalloc(char *arg) 
  2.     /*将vmalloc size解析成unsigned long类型。*/ 
  3.     unsigned long vmalloc_reserve = memparse(arg, NULL); 
  4.  
  5.     if (vmalloc_reserve < SZ_16M) { 
  6.         vmalloc_reserve = SZ_16M; 
  7.         printk(KERN_WARNING 
  8.             "vmalloc area too small, limiting to %luMB\n", 
  9.             vmalloc_reserve >> 20); 
  10.     } 
  11.  
  12.     if (vmalloc_reserve > VMALLOC_END - (PAGE_OFFSET + SZ_32M)) { 
  13.         vmalloc_reserve = VMALLOC_END - (PAGE_OFFSET + SZ_32M); 
  14.         printk(KERN_WARNING 
  15.             "vmalloc area is too big, limiting to %luMB\n", 
  16.             vmalloc_reserve >> 20); 
  17.     } 
  18.     /*改变vmalloc_min变量,这样就得到了自己想要的vmalloc size了。*/ 
  19.     vmalloc_min = (void *)(VMALLOC_END - vmalloc_reserve); 
  20.     return 0; 
  21. early_param("vmalloc", early_vmalloc); 
[html] view plaincopy
  1. static int __init early_vmalloc(char *arg)  
  2. {  
  3.     /*将vmalloc size解析成unsigned long类型。*/  
  4.     unsigned long vmalloc_reserve = memparse(arg, NULL);  
  5.   
  6.     if (vmalloc_reserve < SZ_16M) {  
  7.         vmalloc_reserve = SZ_16M;  
  8.         printk(KERN_WARNING  
  9.             "vmalloc area too small, limiting to %luMB\n",  
  10.             vmalloc_reserve >> 20);  
  11.     }  
  12.   
  13.     if (vmalloc_reserve > VMALLOC_END - (PAGE_OFFSET + SZ_32M)) {  
  14.         vmalloc_reserve = VMALLOC_END - (PAGE_OFFSET + SZ_32M);  
  15.         printk(KERN_WARNING  
  16.             "vmalloc area is too big, limiting to %luMB\n",  
  17.             vmalloc_reserve >> 20);  
  18.     }  
  19.     /*改变vmalloc_min变量,这样就得到了自己想要的vmalloc size了。*/  
  20.     vmalloc_min = (void *)(VMALLOC_END - vmalloc_reserve);  
  21.     return 0;  
  22. }  
  23. early_param("vmalloc", early_vmalloc);  

vmalloc_min的变化也会导致lowmem也就是低端的内存大小的变化。所以实际应用中,high memory的定义并非一定像书上所说的为896M之上。

Meminfo使用:

做完了检查之后就是使用了,在使用部分,meminfo的信息其实都传给了一个叫structmemblock的结构,后续由它来完成内存区域信息 保存的责任。它会将一些必要的区域给保留出来供系统使用,例如kernel的text, code段。其他未使用部分系统才能使用。来看看实现函数arm_memblock_init().

[html] view plaincopy
  1. void __init arm_memblock_init(struct meminfo *mi, struct machine_desc *mdesc) 
  2.     int i; 
  3.     /*将struct meminfo的信息都放入到了struct memblock中去,它会将保留的区域和空闲的区域用memory 和reserved变量分别保存。*/ 
  4.     for (i = 0; i < mi->nr_banks; i++) 
  5.         memblock_add(mi->bank[i].start, mi->bank[i].size); 
  6.  
  7.     /* kernel的text段需要作为保留部分。其实看system.map会发现 
  8. _stext为symbol里的其实地址,而_end为结束地址。所以这块memblock 
  9. Region包括了virtual memory layout中的.init, .bss, .data, .text这几个区域。*/ 
  10.     memblock_reserve(__pa(_stext), _end - _stext); 
  11. /* 本平台的phys_initrd_start 这里为0.*/ 
  12. #ifdef CONFIG_BLK_DEV_INITRD 
  13.     if (phys_initrd_size && 
  14.         !memblock_is_region_memory(phys_initrd_start, phys_initrd_size)) { 
  15.         pr_err("INITRD: 0x%08lx+0x%08lx is not a memory region - disabling initrd\n", 
  16.                phys_initrd_start, phy
阅读(1850) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~