Chipset:MSM8x25Q
Codebase:Android 4.1
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.
static int __init parse_tag(const struct tag *tag)
{
extern struct tagtable __tagtable_begin, __tagtable_end;
struct tagtable *t;
for (t = &__tagtable_begin; t < &__tagtable_end; t++)
if (tag->hdr.tag == t->tag) {
t->parse(tag);
break;
}
return t < &__tagtable_end;
}
__tagtable_begin被定义在kernel/arch/arm/kernel/vmlinux.lds.S中:
.init.tagtable : {
__tagtable_begin = .;
*(.taglist.init)
__tagtable_end = .;
}
另外,在arch/arm/kernel/setup.c中有如下函数定义:
static int __init parse_tag_mem32(const struct tag *tag)
{
return arm_add_memory(tag->u.mem.start, tag->u.mem.size);
}
__tagtable(ATAG_MEM, parse_tag_mem32);
__tagtable是个宏定义:
#define __tagtable(tag, fn) \
static const struct tagtable__tagtable_##fn __tag = { tag, fn }
里面的__tag的宏定义又如下:
#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,否则pa