Armv8 Linux 内存管理(1)
来源:互联网 发布:肖凯农村淘宝 编辑:程序博客网 时间:2024/05/17 07:59
1. Buddy system之前的内存管理
Linux系统内存管理最著名的就是Buddy system(伙伴系统),linux系统是如何建立起这个系统的呢?在Buddy system之前内存是如何管理的呢?
在理解了一定的软件和硬件工作原理之后,代码是最好的指导老师。它能够告诉你如何把硬件的功能使用起来,如何实现你了解的软件设计。内存管理
也不例外。
start_kernel->setup_arch();
arch/arm64/kernel/setup.c
285 setup_arch(char **cmdline_p)
286 {
287 setup_processor();
288
289 setup_machine_fdt(__fdt_pointer);
290
291 init_mm.start_code = (unsigned long) _text;
292 init_mm.end_code = (unsigned long) _etext;
293 init_mm.end_data = (unsigned long) _edata;
294 init_mm.brk = (unsigned long) _end;
295
296 *cmdline_p = boot_command_line;
297
298 parse_early_param();
299
300 arm64_memblock_init();
301
302 paging_init();
303 request_standard_resources();
304
305 unflatten_device_tree();
306
307 psci_init();
308
309 cpu_logical_map(0) = read_cpuid_mpidr() & MPIDR_HWID_BITMASK;
310 #ifdef CONFIG_SMP
311 smp_init_cpus();
312 #endif
313
314 #ifdef CONFIG_VT
315 #if defined(CONFIG_VGA_CONSOLE)
316 conswitchp = &vga_con;
317 #elif defined(CONFIG_DUMMY_CONSOLE)
318 conswitchp = &dummy_con;
319 #endif
320 #endif
321 }
289, 300,302是建立简单内存管理系统的核心函数。setup_machine_fdt建立memblock数据,从device tree内读到。
125 static void __init setup_machine_fdt(phys_addr_t dt_phys)
126 {
127 struct boot_param_header *devtree;
128 unsigned long dt_root;
129
130 /* Check we have a non-NULL DT pointer */
131 if (!dt_phys) {
132 early_print("\n"
133 "Error: NULL or invalid device tree blob\n"
134 "The dtb must be 8-byte aligned and passed in the first 512MB of memory\n"
135 "\nPlease check your bootloader.\n");
136
137 while (true)
138 cpu_relax();
139
140 }
141
142 devtree = phys_to_virt(dt_phys);
143
144 /* Check device tree validity */
145 if (be32_to_cpu(devtree->magic) != OF_DT_HEADER) {
146 early_print("\n"
147 "Error: invalid device tree blob at physical address 0x%p (virtual address 0x %p)\n"
148 "Expected 0x%x, found 0x%x\n"
149 "\nPlease check your bootloader.\n",
150 dt_phys, devtree, OF_DT_HEADER,
151 be32_to_cpu(devtree->magic));
152
153 while (true)
154 cpu_relax();
155 }
156
157 initial_boot_params = devtree;
158 dt_root = of_get_flat_dt_root();
159
160 machine_name = of_get_flat_dt_prop(dt_root, "model", NULL);
161 if (!machine_name)
162 machine_name = of_get_flat_dt_prop(dt_root, "compatible", NULL);
163 if (!machine_name)
164 machine_name = "<unknown>";
165 pr_info("Machine: %s\n", machine_name);
166
167 /* Retrieve various information from the /chosen node */
168 of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);
169 /* Initialize {size,address}-cells info */
170 of_scan_flat_dt(early_init_dt_scan_root, NULL);
171 /* Setup memory, calling early_init_dt_add_memory_arch */
172 of_scan_flat_dt(early_init_dt_scan_memory, NULL);
173 }
172行遍历device tree,建立memblock数据
134 void __init arm64_memblock_init(void)
135 {
136 u64 *reserve_map, base, size;
137
138 /* Register the kernel text, kernel data and initrd with memblock */
139 memblock_reserve(__pa(_text), _end - _text);
140 #ifdef CONFIG_BLK_DEV_INITRD
141 if (phys_initrd_size) {
142 memblock_reserve(phys_initrd_start, phys_initrd_size);
143
144 /* Now convert initrd to virtual addresses */
145 initrd_start = __phys_to_virt(phys_initrd_start);
146 initrd_end = initrd_start + phys_initrd_size;
147 }
148 #endif
149
150 /*
151 * Reserve the page tables. These are already in use,
152 * and can only be in node 0.
153 */
154 memblock_reserve(__pa(swapper_pg_dir), SWAPPER_DIR_SIZE);
155 memblock_reserve(__pa(idmap_pg_dir), IDMAP_DIR_SIZE);
156
157 /* Reserve the dtb region */
158 memblock_reserve(virt_to_phys(initial_boot_params),
159 be32_to_cpu(initial_boot_params->totalsize));
160
161 /*
162 * Process the reserve map. This will probably overlap the initrd
163 * and dtb locations which are already reserved, but overlapping
164 * doesn't hurt anything
165 */
166 reserve_map = ((void*)initial_boot_params) +
167 be32_to_cpu(initial_boot_params->off_mem_rsvmap);
168 while (1) {
169 base = be64_to_cpup(reserve_map++);
170 size = be64_to_cpup(reserve_map++);
171 if (!size)
172 break;
173 memblock_reserve(base, size);
174 }
175
176 memblock_allow_resize();
177 memblock_dump_all();
178 }
arm64_memblock_init 主要是设置预留块。
2. 建立预留快数据
drivers/of/fdt.c
618 int __init early_init_dt_scan_memory(unsigned long node, const char *uname,
619 int depth, void *data)
620 {
621 char *type = of_get_flat_dt_prop(node, "device_type", NULL);
622 __be32 *reg, *endp;
623 unsigned long l;
624
625 /* We are scanning "memory" nodes only */
626 if (type == NULL) {
627 /*
628 * The longtrail doesn't have a device_type on the
629 * /memory node, so look for the node called /memory@0.
630 */
631 if (depth != 1 || strcmp(uname, "memory@0") != 0)
632 return 0;
633 } else if (strcmp(type, "memory") != 0)
634 return 0;
635
636 reg = of_get_flat_dt_prop(node, "linux,usable-memory", &l);
637 if (reg == NULL)
638 reg = of_get_flat_dt_prop(node, "reg", &l);
639 if (reg == NULL)
640 return 0;
641
642 endp = reg + (l / sizeof(__be32));
643
644 pr_debug("memory scan node %s, reg size %ld, data: %x %x %x %x,\n",
645 uname, l, reg[0], reg[1], reg[2], reg[3]);
646
647 while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) {
648 u64 base, size;
649
650 base = dt_mem_next_cell(dt_root_addr_cells, ®);
651 size = dt_mem_next_cell(dt_root_size_cells, ®);
652
653 if (size == 0)
654 continue;
655 pr_debug(" - %llx , %llx\n", (unsigned long long)base,
656 (unsigned long long)size);
657
658 early_init_dt_add_memory_arch(base, size);
659 }
660
661 return 0;
662 }
658行向内存块添加内存。
arch/arm64/kernel/setup.c
175 void __init early_init_dt_add_memory_arch(u64 base, u64 size)
176 {
177 base &= PAGE_MASK;
178 size &= PAGE_MASK;
179 if (base + size < PHYS_OFFSET) {
180 pr_warning("Ignoring memory block 0x%llx - 0x%llx\n",
181 base, base + size);
182 return;
183 }
184 if (base < PHYS_OFFSET) {
185 pr_warning("Ignoring memory range 0x%llx - 0x%llx\n",
186 base, PHYS_OFFSET);
187 size -= PHYS_OFFSET - base;
188 base = PHYS_OFFSET;
189 }
190 memblock_add(base, size);
191 }
192
190行掉用memblock_add。
mm/memblock.c
445 int __init_memblock memblock_add(phys_addr_t base, phys_addr_t size)
446 {
447 return memblock_add_region(&memblock.memory, base, size, MAX_NUMNODES);
448 }
364 static int __init_memblock memblock_add_region(struct memblock_type *type,
365 phys_addr_t base, phys_addr_t size, int nid)
366 {
367 bool insert = false;
368 phys_addr_t obase = base;
369 phys_addr_t end = base + memblock_cap_size(base, &size);
370 int i, nr_new;
371
372 if (!size)
373 return 0;
374
375 /* special case for empty array */
376 if (type->regions[0].size == 0) {
377 WARN_ON(type->cnt != 1 || type->total_size);
378 type->regions[0].base = base;
379 type->regions[0].size = size;
380 memblock_set_region_node(&type->regions[0], nid);
381 type->total_size = size;
382 return 0;
383 }
384 repeat:
385 /*
386 * The following is executed twice. Once with %false @insert and
387 * then with %true. The first counts the number of regions needed
388 * to accomodate the new area. The second actually inserts them.
389 */
390 base = obase;
391 nr_new = 0;
392
393 for (i = 0; i < type->cnt; i++) {
394 struct memblock_region *rgn = &type->regions[i];
395 phys_addr_t rbase = rgn->base;
396 phys_addr_t rend = rbase + rgn->size;
397
398 if (rbase >= end)
399 break;
400 if (rend <= base)
401 continue;
402 /*
403 * @rgn overlaps. If it separates the lower part of new
404 * area, insert that portion.
405 */
406 if (rbase > base) {
407 nr_new++;
408 if (insert)
409 memblock_insert_region(type, i++, base,
410 rbase - base, nid);
411 }
412 /* area below @rend is dealt with, forget about it */
413 base = min(rend, end);
414 }
415
416 /* insert the remaining portion */
417 if (base < end) {
418 nr_new++;
419 if (insert)
420 memblock_insert_region(type, i, base, end - base, nid);
421 }
422
423 /*
424 * If this was the first round, resize array and repeat for actual
425 * insertions; otherwise, merge and return.
426 */
427 if (!insert) {
428 while (type->cnt + nr_new > type->max)
429 if (memblock_double_array(type, obase, size) < 0)
430 return -ENOMEM;
431 insert = true;
432 goto repeat;
433 } else {
434 memblock_merge_regions(type);
435 return 0;
436 }
437 }
如果详细了解具体细节,可以阅读mm/memblock.c的函数。
23 static struct memblock_region memblock_memory_init_regions[INIT_MEMBLOCK_REGIONS] __initdata_membloc k;
24 static struct memblock_region memblock_reserved_init_regions[INIT_MEMBLOCK_REGIONS] __initdata_membl ock;
25
26 struct memblock memblock __initdata_memblock = {
27 .memory.regions = memblock_memory_init_regions,
28 .memory.cnt = 1, /* empty dummy entry */
29 .memory.max = INIT_MEMBLOCK_REGIONS,
30
31 .reserved.regions = memblock_reserved_init_regions,
32 .reserved.cnt = 1, /* empty dummy entry */
33 .reserved.max = INIT_MEMBLOCK_REGIONS,
34
35 .current_limit = MEMBLOCK_ALLOC_ANYWHERE,
36 };
setup_machine_fdt和arm64_memblock_init结束,memblock的数据结构已经初始化。可以利用这个结构来分配内存了。
- Armv8 Linux 内存管理(1)
- ARMv8百日谈(内存管理系统)
- ARMv8 内存管理架构.学习笔记
- 罗玉平: ARMv8 Linux内核内存屏障
- Linux内存管理(1)
- linux与uclinux 内存管理(1)
- linux 内存管理系列1
- 闲聊Linux内存管理(1)
- Linux内存管理(全文)
- Linux内存管理(一)
- linux 内存管理 (转载)
- linux内存管理(上)
- linux内存管理(下)
- Linux内存管理(上)
- Linux内存管理(下)
- linux内存管理(上)
- linux内存管理(下)
- LInux 内存管理(上)
- web项目测试方法总结
- Java集合总览
- hive 分区表 添加新字段 查询为NULL的情况 我也遇到了这个问题
- 陈皓:程序员技术练级攻略
- Linux C++中需要的头文件
- Armv8 Linux 内存管理(1)
- java.lang.AbstractMethodError: org.slf4j.impl.JDK14LoggerAdapter.log(Lorg/slf4j/Marker;Ljava/lang/St
- 每个Java开发者都应该知道的5个JDK工具
- linux 内存释放调试经验积累
- Apache -Common-lang包使用
- 外媒:中国游客“拯救”安倍经济学?
- 数据挖掘中所需的概率论与数理统计知识
- android Intent机制详解
- IBM推出新款企业电子邮件服务:整合社交媒体