linux内核如何获取当前运行平台下的内存大小
注1:本文属于《linux内存管理》模块系列的文章
注2:本文中所有源码出自linux内核版本:4.1.15
注3:本文内容基于ARM架构
在支持设备树(DTS)的linux版本下,可以通过设备树将重要参数传递给linux内核。查看一下linux内核源码的设备树目录,如下图所示(/arch/arm/boot/dts):
在该目录下,后缀为dtsi的是设备树头文件,后缀为dts是具体的设备树文件(其中dts文件将使用#include "xxx"
类似语法包含dtsi设备树头文件)。其中大多都是关于具体芯片、厂商的设备树文件。本文以imx6sll-evk.dts
的板卡级设备树描述文件为例,在该文件中用于描述具体硬件板卡内存大小的设备节点如下所示:
memory@80000000 {
reg = <0x80000000 0x80000000>;
};
可见,该内存起始地址是:0x80000000
,内存大小是:0x80000000
(即2G大小的内存空间)。
这里以ARM架构为例,在linux内核启动过程中,需要对设备树文件进行解析,需要解析出的参数很多,其中linux内核使用early_init_dt_scan_memory()
函数查找并解析memory
节点。如下代码:
int __init early_init_dt_scan_memory(unsigned long node, const char *uname,
int depth, void *data)
{
const char *type = of_get_flat_dt_prop(node, "device_type", NULL);
const __be32 *reg, *endp;
int l;
if (type == NULL) {
if (!IS_ENABLED(CONFIG_PPC32) || depth != 1 || strcmp(uname, "memory@0") != 0)
return 0;
} else if (strcmp(type, "memory") != 0)
return 0;
reg = of_get_flat_dt_prop(node, "linux,usable-memory", &l);
if (reg == NULL)
reg = of_get_flat_dt_prop(node, "reg", &l);
if (reg == NULL)
return 0;
endp = reg + (l / sizeof(__be32));
pr_debug("memory scan node %s, reg size %d,\n", uname, l);
while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) {
u64 base, size;
base = dt_mem_next_cell(dt_root_addr_cells, ®);
size = dt_mem_next_cell(dt_root_size_cells, ®);
if (size == 0)
continue;
pr_debug(" - %llx , %llx\n", (unsigned long long)base,
(unsigned long long)size);
early_init_dt_add_memory_arch(base, size);
}
return 0;
}
上述第8-12行判断是否是memory内存设备树节点,如果不是,函数则返回,否则将继续后续的解析操作。
上述14 - 18行功能是解析出memory设备树节点的字符串信息。
第24 - 36行while循环代码功能是:通过解析出的字符串信息reg
获取内存的开始地址base
和内存大小size
。然后调用early_init_dt_add_memory_arch()
函数,当该函数运行完成后,base和size的信息就被添加到memblock
子系统去了。early_init_dt_add_memory_arch
函数定义(/drivers/of/fdt.c):
void __init __weak early_init_dt_add_memory_arch(u64 base, u64 size)
{
const u64 phys_offset = __pa(PAGE_OFFSET);
if (!PAGE_ALIGNED(base)) {
if (size < PAGE_SIZE - (base & ~PAGE_MASK)) {
pr_warn("Ignoring memory block 0x%llx - 0x%llx\n",
base, base + size);
return;
}
size -= PAGE_SIZE - (base & ~PAGE_MASK);
base = PAGE_ALIGN(base);
}
size &= PAGE_MASK;
if (base > MAX_MEMBLOCK_ADDR) {
pr_warning("Ignoring memory block 0x%llx - 0x%llx\n",
base, base + size);
return;
}
if (base + size - 1 > MAX_MEMBLOCK_ADDR) {
pr_warning("Ignoring memory range 0x%llx - 0x%llx\n",
((u64)MAX_MEMBLOCK_ADDR) + 1, base + size);
size = MAX_MEMBLOCK_ADDR - base + 1;
}
if (base + size < phys_offset) {
pr_warning("Ignoring memory block 0x%llx - 0x%llx\n",
base, base + size);
return;
}
if (base < phys_offset) {
pr_warning("Ignoring memory range 0x%llx - 0x%llx\n",
base, phys_offset);
size -= phys_offset - base;
base = phys_offset;
}
//将base和size添加到memblock子系统
memblock_add(base, size);
}
early_init_dt_scan_memory()
的调用关系:
搜索关注【嵌入式小生】wx公众号获取更多精彩内容>>>>