
0. 前言
此节点是显示memblock的这部分内存的具体使用情况的。我们可以看到这部分内存很明显不属于虚拟地址,而是物理地址,和设备树中的地址保持一致!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| spring:/ 00208000-00208fff : 208000.qcom,ipcc qcom,ipcc@208000 00400000-00bfffff : 400000.pinctrl pinctrl@400000 01400000-015effff : 1400000.clock-controller clock-controller@1400000 01628000-01629fff : 1628000.qcom,msm-eud eud_base 0162a000-0162afff : 162b000.hsphy eud_enable_reg 0162b000-0162b113 : 162b000.hsphy hsusb_phy_base
...
82a00000-864fffff : System RAM 85200000-85efffff : reserved 8b41c000-8b7fffff : System RAM 9b800000-bb7fffff : System RAM a0010000-a1dcffff : Kernel code a1dd0000-a249ffff : reserved a24a0000-a331ffff : Kernel data a7fff000-a7ffffff : reserved af20b000-af27afff : reserved
|
那这个节点的信息是如何收集并打印出来的呢?
1. request_standard_resources
在bootmem_init
函数结束后,有一个函数request_standard_resources
。这个函数的功能如下:
- 将memblock.memory挂载到iomem_resource资源树下, 资源树是一颗倒挂的树
- request_resource:将设备实体登记注册到总线空间链
- 在遍历memblock.memory过程中,会检查kernel_code,kernel_data是否属于某region,如果是则挂载到该region下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| static void __init request_standard_resources(void) { struct memblock_region *region; struct resource *res; unsigned long i = 0; size_t res_size;
kernel_code.start = __pa_symbol(_stext); kernel_code.end = __pa_symbol(__init_begin - 1); kernel_data.start = __pa_symbol(_sdata); kernel_data.end = __pa_symbol(_end - 1);
num_standard_resources = memblock.memory.cnt; res_size = num_standard_resources * sizeof(*standard_resources); standard_resources = memblock_alloc(res_size, SMP_CACHE_BYTES); if (!standard_resources) panic("%s: Failed to allocate %zu bytes\n", __func__, res_size);
for_each_mem_region(region) { res = &standard_resources[i++]; if (memblock_is_nomap(region)) { res->name = "reserved"; res->flags = IORESOURCE_MEM; } else { res->name = "System RAM"; res->flags = IORESOURCE_SYSTEM_RAM | IORESOURCE_BUSY; } res->start = __pfn_to_phys(memblock_region_memory_base_pfn(region)); res->end = __pfn_to_phys(memblock_region_memory_end_pfn(region)) - 1;
request_resource(&iomem_resource, res);
if (kernel_code.start >= res->start && kernel_code.end <= res->end) request_resource(res, &kernel_code); if (kernel_data.start >= res->start && kernel_data.end <= res->end) request_resource(res, &kernel_data); #ifdef CONFIG_KEXEC_CORE if (crashk_res.end && crashk_res.start >= res->start && crashk_res.end <= res->end) request_resource(res, &crashk_res); #endif } }
|
这里就是建立资源树,有下面几个部分:
- 内核代码段和数据段
- 平台设备的资源
- crash kernel
2. /proc/iomem的注册
/proc/iomem的注册位于kernel/resource.c中
2.1 ioresources_init
1 2 3 4 5 6 7
| static int __init ioresources_init(void) { proc_create_seq_data("ioports", 0, NULL, &resource_op, &ioport_resource); proc_create_seq_data("iomem", 0, NULL, &resource_op, &iomem_resource); return 0; } __initcall(ioresources_init);
|
关键点分析
- proc_create_seq_data 函数:
- 用于创建 /proc 文件系统中的条目。
- 第一个参数 “iomem” 指定了条目的名称(即 /proc/iomem)。
- 最后一个参数 &iomem_resource 指定了该条目的数据来源,即全局的 iomem_resource 资源树。
- __initcall(ioresources_init):
- 指定 ioresources_init() 在内核初始化阶段的 init 部分运行,确保 /proc/iomem 在系统启动时被正确创建。
2.2 数据来源iomem_resource
iomem_resource 是一个全局变量,定义如下:
1 2 3 4 5 6
| struct resource iomem_resource = { .name = "PCI mem", .start = 0, .end = -1, .flags = IORESOURCE_MEM, };
|
iomem_resource 的作用:
- 它是内核的物理内存资源树的根节点,记录了系统中所有内存相关的物理地址范围。
- 所有资源(如 System RAM、设备寄存器等)都会通过 request_resource() 等接口被注册到该资源树中。
2.3 数据显示逻辑
当你查看 /proc/iomem 时,实际是内核通过以下逻辑从 iomem_resource 中读取并格式化数据:
2.3.1 资源树的遍历
核心遍历代码在 r_start()
和 r_next()
中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| static void *r_start(struct seq_file *m, loff_t *pos) { struct resource *p = PDE_DATA(file_inode(m->file)); loff_t l = 0;
read_lock(&resource_lock); for (p = p->child; p && l < *pos; p = next_resource(p)) l++; return p; }
static void *r_next(struct seq_file *m, void *v, loff_t *pos) { struct resource *p = v; (*pos)++; return (void *)next_resource(p); }
|
r_start():获取资源树的起始节点。
r_next():遍历资源树中的每个节点。
2.3.2 数据格式化输出
1 2 3 4 5 6 7 8 9 10 11 12 13
| static int r_show(struct seq_file *m, void *v) { struct resource *root = PDE_DATA(file_inode(m->file)); struct resource *r = v; unsigned long long start, end;
start = r->start; end = r->end;
seq_printf(m, "%08llx-%08llx : %s\n", start, end, r->name ? r->name : "<BAD>"); return 0; }
|
seq_printf:将资源的起始地址 (start)、结束地址 (end) 和名称 (r->name) 格式化并输出。
输出格式:每行输出一个资源的地址范围及其描述。例如:
1 2
| 00000000-0009fbff : System RAM 0009fc00-0009ffff : reserved
|
总结
- 注册过程:
- /proc/iomem 在系统启动时由
ioresources_init()
注册。
- 数据来源于
iomem_resource
资源树。
- 数据构建:
- 系统内存和设备寄存器等资源通过
request_resource()
或设备树解析注册到 iomem_resource
。
- 显示内容:
- /proc/iomem 显示的是
iomem_resource
中所有资源的地址范围和用途,便于查看和调试系统物理内存布局。
通过这些机制,/proc/iomem 提供了一个全局视图,方便开发者管理和调试资源分配问题。