本文共 11530 字,大约阅读时间需要 38 分钟。
C setup_arch->paging_init->bootmem_init->memblock_allow_resize返回 - mm_init->mem_init返回----此时memblock消亡,buddy初始化完成,开启了基于虚拟内时代的 buddy内存管理器时代
setup_arch(&command_line);->paging_init bootmem_init find_limits(&min_low_pfn, &max_low_pfn, &max_pfn); sparse_init zone_sizes_init(min_low_pfn, max_low_pfn, max_pfn); // 申请 struct page 所在的空间 free_area_init(max_zone_pfn); pg_data_t *pgdat = NODE_DATA(nid); // &contig_page_data free_area_init_node(nid); alloc_node_mem_map(pgdat); // struct page 相关的配置 struct page * map = memblock_alloc_node(size, SMP_CACHE_BYTES, pgdat->node_id); // 为 struct page 申请空间 // struct page 的个数 为 (6000 0000 - 5000 0000)/0x1000 // memblock_reserve: [0x5fdf6000-0x5fff5fff] // 20 0000 个字节 // 一个 struct page 为 0x20 字节 // (6000 0000 - 5000 0000)/0x1000 * 0x20 // 申请的起始地址为 cfdf6000 , 大小为 0x20 0000 pgdat->node_mem_map = map + offset; // map : cfdf6000 // offset : 0 // pgdat->node_mem_map : cfdf6000 mem_map = NODE_DATA(0)->node_mem_map; // mem_map = pgdat->node_mem_map = cfdf6000 free_area_init_core(pgdat); // zone相关的设置 struct zone *zone = pgdat->node_zones + j; zone_init_internals(zone, j, nid, freesize); setup_usemap(pgdat, zone, zone_start_pfn, size); // 申请一块地址,大小 0x20 // 作用是什么 ? 返回的起始地址 是 0x5fffddc0,对应虚拟地址是 0xcfffddc0 // memblock_reserve: [0x5fffddc0-0x5fffdddf] // 返回的虚拟地址 存储到 了 zone->pageblock_flags 中 init_currently_empty_zone zone_init_free_lists(zone); // order 取值范围 为 [0,...,MAX_ORDER(11)] // t 取值范围 为 [MIGRATE_UNMOVABLE(0), ... MIGRATE_TYPES(8)] INIT_LIST_HEAD(&zone->free_area[order].free_list[t]); zone->free_area[order].nr_free = 0; zone->initialized = 1; memmap_init(size, nid, j, zone_start_pfn); // 将 创建的 (6000 0000 - 5000 0000)/0x1000 * 0x20 个 struct page 一次 遍历,并 reserved start_pfn = 50000; end_pfn = 60000; size = end_pfn - start_pfn; memmap_init_zone(size, nid, zone, start_pfn, range_end_pfn, MEMINIT_EARLY, NULL, MIGRATE_MOVABLE); page = pfn_to_page(pfn); if (IS_ALIGNED(pfn, pageblock_nr_pages)) set_pageblock_migratetype(page, migratetype); empty_zero_page = virt_to_page(zero_page); // empty_zero_page 为 cfff5ec0 // 之前 zero_page = early_alloc(PAGE_SIZE); // zero_page 为 cfff6000 // 申请的空间刚好在 全部的 struct page 上面 // 申请了 一物理页(4096B) 空间,返回了 虚拟地址 // 以 虚拟地址 zero_page 得到 对应这块地址的 struct page 即 empty_zero_page // 函数过程 // 1. 根据 虚拟地址 zero_page 获取 物理地址A // 2. 根据 物理地址A 右移 12 , 获取 pfn // 3. 根据 pfn 获取 返回 (mem_map + ((pfn) - ARCH_PFN_OFFSET)) // ARCH_PFN_OFFSET = 50000 // 4. mem_map 是 第一个page 的地址,结构体 类型为 struct page // 5. 返回的是 一个 struct page 的结构体变量地址 // TODO __flush_dcache_page(NULL, empty_zero_page); // 将缓存中保存的数据向内存执行回写 // 从而将缓存内容反映到内存,以此确保缓存和内存间的一致性 // 这里是写了什么 ??? TODO /* setup_processor struct proc_info_list *list = lookup_processor(midr); // __v6_proc_info cpu_cache = *list->cache; // v6_cache_fns __flush_dcache_page if (!PageHighMem(page)) __cpuc_flush_dcache_area(page_address(page), page_size(page)); // 即 v6_flush_kern_dcache_area , 下面有 对 v6_flush_kern_dcache_area 的 重点解析,全局搜索 // page_address(page) : cfff 6000 // page_size(page) : 0x1000 */setup_arch request_standard_resources(mdesc); // 按照树状结构 注册 内存 kernel_code kernel_data// TODObuild_all_zonelists// TODOpage_alloc_init // 订阅 CPUHP_PAGE_ALLOC_DEAD 信息 // 注册 CPUHP_PAGE_ALLOC_DEAD 发布时的处理函数 page_alloc_cpu_dead // linux-2.6.30.4 是 notify chain机制, cpu_chainmm_init page_ext_init_flatmem init_mem_debugging_and_hardening report_meminit // TODO mem_init set_max_mapnr(pfn_to_page(max_pfn) - mem_map); max_mapnr = struct page 的个数(包括空洞的) memblock_free_all free_unused_memmap // 释放 memblock 块 之间的空洞部分 // 例如 memblock_add 了三块 // 0 - 0x1000 0000 // 0x2000 0000 - 0x3000 0000 // 0x5000 0000 - 0x6000 0000 // 那么 0x1000 0000 - 0x2000 0000 就是空洞 // 那么 0x3000 0000 - 0x5000 0000 也是空洞 // 第一块空洞对应 下面的函数调用 // prev_end = 0x1000 0 // start = 0x2000 0 // free_memmap(prev_end, start); // free_memmap -> memblock_free // memblock_free 把一个逻辑块从memblock.reserved 移除 // free_unused_memmap 移除的 是 struct page 所在的地址 // 也就是之前 为 0x1000 0000 - 0x2000 0000 ,即 0x2000 0000 - 0x1000 0000 = 0x1000 0000 大小的空间建立了 // 0x1000 0000/0x1000 = 0x10000 个 struct page // 这 0x10000 * sizeof(struct page) = 0x10000 * 0x20 = 0x200000 // 空间, 是之前 memblock_alloc 申请的 // 但是 这些空间的struct page 没有用(因为对应空洞物理页) // 所以将他们释放了 reset_all_zones_managed_pages // 初始化 contig_page_data.node_zones[].managed_pages 为 0 free_low_memory_core_early for_each_reserved_mem_range(i, &start, &end) reserve_bootmem_region(start, end); for_each_pfn do{ init_reserved_page(pfn); // null INIT_LIST_HEAD(&page->lru); __SetPageReserved(page); } // 针对 memblock.reserved 中的 每一块 reserved 内存 // 将内存对应的 struct page 的 flags 成员 __SetPageReserved // 被 __SetPageReserved 的 物理页 永远不会 被 当做是 可申请的内存 // 被 __SetPageReserved 的 物理页 不在 buddy 的内存管理范围内 for_each_free_mem_range __free_memory_core(start, end); __free_pages_memory memblock_free_pages // 不同于 memblock_free // struct page *page, : 参数1 : page的地址 // unsigned long pfn, : 参数2 : page frame number // unsigned int order : 参数3 : order : 如果为0 表示 2^0 (即1) 个页 __free_pages_core // struct page *page, : 参数1 : page的地址 // unsigned int order : 参数2 : order : 如果为0 表示 2^0 (即1) 个页 __ClearPageReserved __free_pages_ok free_one_page __free_one_page // 针对 memblock.memory 中 memblock.reserved 的补集 中的每一块 内存 // 将内存对应的 struct page 加入到 free_list totalram_pages_add atomic_long_add(count, &_totalram_pages); // 之前 是 0 // 现在 是 ee39 // end start num // 50004 50000 4 // 50100 50008 F8 // 5fdbd 51081 ED3C // 5fdf6 5fdf5 1 // 5fffd 5fffe 1 free_highpages // null mem_init_print_info // 打印如下信息 // ]Memory: 243940K/262144K available (5120K kernel code, 6569K rwdata, 736K rodata, 1024K init, 2134K bss, 18204K reserved, 0K cma-reserved) // 这里本来应有 Virtual kernel memory layout 的打印 // 但是 去掉了,下面为linux中的commit id // 1c31d4e96b8c205fe3aa8e73e930a0ccbf4b9a2b
memblock 时代 物理内存和虚拟内存是怎么管控的 1. 页表(物理地址到虚拟地址的映射) // 对应下面的 early map 类 和 map 类 2. 按区域 注册物理内存到 memblock 内存管理器中的 memblock 变量 // 对应下面的 memblock 类buddy 时代 物理内存和虚拟内存是怎么管控的 1. 页表(物理地址到虚拟地址的映射) 2. 按物理页 注册物理内存到 buddy 内存管理器中的 struct pagememblock 切换 到 buddy , 只需要做 1. 不需要做页表的映射(因为memblock时代,已经做完了,buddy直接用就行了) 2. 将 注册到memblock内存管理器memblock变量中 的 物理页 注册到 buddy内存管理器中的struct page
zone_sizes_init A B C D E F Kbuild_all_zonelists G H I Jmem_init E F Lreset_all_zones_managed_pages L这几个函数主要是 初始化 struct pglist_data contig_page_data; 变量 的成员及 成员的成员struct pglist_data // A struct zone node_zones[MAX_NR_ZONES]; // B atomic_long_t managed_pages; // L unsigned long zone_start_pfn; // C struct free_area free_area[MAX_ORDER]; // D struct list_head free_list[MIGRATE_TYPES]; // E unsigned long nr_free; // F struct zonelist node_zonelists[MAX_ZONELISTS]; // G struct zoneref _zonerefs[MAX_ZONES_PER_ZONELIST + 1];// H struct zone *zone; // I int zone_idx; // J struct page *node_mem_map; // K
/* * v6_flush_kern_dcache_area(void *addr, size_t size) * * Ensure that the data held in the page kaddr is written back * to the page in question. * * - addr - kernel address * - size - region size */ENTRY(v6_flush_kern_dcache_area) // r0 = cfff 6000 // r1 = 0x1000 add r1, r0, r1 // r1 = cfff 7000 bic r0, r0, #D_CACHE_LINE_SIZE - 1 // D_CACHE_LINE_SIZE = 32 // 32 -1 = 1F // r0 = r0 bit_clear 1F = cfff 6000 // HARVARD_CACHE 已经 define1:#ifdef HARVARD_CACHE mcr p15, 0, r0, c7, c14, 1 @ clean & invalidate D line // 写cp15 c7 // 写入的数据 依次是 // cfff 6000 // cfff 6020 // cfff 6040 // ... // cfff 6fe0 // 作用是 Clean and invalidate data cache line // Clean : // 适用于write back 数据缓存 // 意味着如果缓存线包含尚未写入主内存的存储数据,则它现在将写入主内存 // 并且该行被标记为干净。 // invalidate : // 表示缓存线(或缓存中的所有行)被标记为无效。 // 在将该行重新分配到某个地址之前,该行不会发生缓存命中。 // 对于写回数据缓存,这不包括清除缓存线,除非另有说明。 // cache line : // 为了尽量减少控制信息的存储量,空间局部性属性用于将多个位置分组到同一标签下。 // 这个内存位置的逻辑块通常称为缓存线,通常为32字节长。 // Cache写机制分为write through和write back两种 // write back : // Write-back(回写模式)在数据更新时只写入缓存Cache。 // 只在数据被替换出缓存时,被修改的缓存数据才会被写到后端存储。 // 此模式的优点是数据写入速度快,因为不需要写存储; // 缺点是一旦更新后的数据未被写入存储时出现系统掉电的情况,数据将无法找回。 // Write-through : // Write-through(直写模式)在数据更新时,同时写入缓存Cache和后端存储。 // 此模式的优点是操作简单;缺点是因为数据修改需要同时写入存储,数据写入速度较慢 // cache 写机制 之前被设置成了什么 ?? TODO // early_mm_init 中有打印 // Memory policy: Data cache writeback // early_mm_init 中不是设置,而是显示,设置在哪里,TODO // write back 的设置有几个设置点 // 1. cp15 c1 // 控制开 write buffer/cache // 2. pmd // 控制 pmd的bit[3:2] 为 11 // 3. pte // 控制 pte的bit[3:2] 为 11 // 内存 cfff 6000 - cfff 7000 被加载入缓存 // 如果 缓存中的数据有变化,但是 因为 cache 机制 为 write back, 所以此时还没写入内存 // mcr p15, 0, r0, c7, c14, 1 作用是 // 将 缓存中的数据写到 内存 cfff 6000 - cfff 7000 // 但是 内存 cfff 6000 - cfff 7000 中的数据 在 该操作前后 全部为 0(前后一致) // 也就是说 没有修改过 内存 cfff 6000 - cfff 7000, 为什么还要 做cache 与内存的同步#else #endif add r0, r0, #D_CACHE_LINE_SIZE // D_CACHE_LINE_SIZE = 32 = 0x20 cmp r0, r1 blo 1b // r0 < r1 则跳转#ifdef HARVARD_CACHE mov r0, #0 mcr p15, 0, r0, c7, c10, 4 // 写 cp15 c7 // 写入的数据是 0 // Data Synchronization Barrier (formerly Drain Write Buffer) // Data synchronization barrier : // 以前的数据写屏障,数据写载体(DWB),现在叫DSB // 数据同步屏障可以在特权和用户操作模式下执行 // B2.6.2数据同步屏障(DSB) // 特点: // 在DSB完成之前,DSB后面的任何指令都不能执行。 // CP15寄存器7注:该操作历来被称为DrainWriteBuffer或DataWriteBarrier(DWB)。 // 在ARMv6中,这些名称(以及DWB的使用)被弃用,取而代之的是新的DataSynchronizationBarrier名称和DSB。DSB更好地反映了ARMv6中提供的功能; // 它在架构上定义为包括所有缓存、TLB和分支预测维护操作以及显式内存操作。 // 数据同步屏障操作是一种特殊的内存屏障。 // DSB操作在以下情况下完成: // •在本指令之前,所有显式内存访问完成. // •完成本指令之前的所有缓存、分支预测和TLB维护操作。 // DataSynchronizationBarrier的编码在B6-19页的Register 7:cache management functions中描述。#endif ret lr
include/linux/page-flags.h L275275 static __always_inline void __SetPage##uname(struct page *page) \ 276 { __set_bit(PG_##lname, &policy(page, 1)->flags); } static inline __attribute__((__gnu_inline__)) __attribute__((__unused__)) __attribute__((__no_instrument_function__)) __attribute__((__always_inline__)) void __SetPageReserved(struct page *page) { __set_bit(PG_reserved, &({ ((void)(sizeof(( long)(1 && PageCompound(page))))); ({ ((void)(sizeof(( long)(PagePoisoned(page))))); page; }); })->flags); }// 注意 uname 与 lnameinclude/linux/mm_types.h L69struct page { ... union { ... unsigned int page_type; ... }; ...};include/linux/page-flags.h L103enum pageflags { ... PG_reserved, ...};
__free_one_page 996 static inline void __free_one_page(struct page *page, 997 unsigned long pfn, 998 struct zone *zone, unsigned int order, 999 int migratetype, fpi_t fpi_flags) __free_one_page continue_merging: // 像 high level 合并 while (order < max_order) { ... } done_merging: to_tail = xxx; if (to_tail) add_to_free_list_tail(page, zone, order, migratetype); else add_to_free_list(page, zone, order, migratetype); struct free_area *area = &zone->free_area[order]; list_add(&page->lru, &area->free_list[migratetype]); area->nr_free++;
转载地址:http://gcigi.baihongyu.com/