内存初始化主要流程代码注释

start_kernel()
    |---->page_address_init()
    |     考虑支持高端内存
    |     业务:初始化page_address_pool链表;
    |          将page_address_maps数组元素按索
    |          引降序插入page_address_pool链表;
    |          初始化page_address_htable数组
    |
    |---->setup_arch(&command_line);
    |
void setup_arch(char **cmdline_p)
    |---->parse_tags(tags);
          |---->parse_tag_mem32(tag)
                |---->arm_add_memory(tag->u.mem.start, 
                |           tag->u.mem.size);
                      |---->为meminfo添加内存信息
                      |     meminfo.bank[meminfo.
                      |         nr_banks].start = start;
                      |     meminfo.bank[meminfo.
                      |         nr_banks].size = size;
                      |     meminfo.bank[meminfo.
                      |         nr_banks].node = 0;
                      |     meminfo.nr_banks++;
                      |
    |----init_mm.start_code = (unsigned long)_text;
    |    init_mm.end_code = (unsigned long)_etext;
    |    init_mm.end_data = (unsigned long)_edata;
    |    init_mm.brk = (unsigned long)_end;
    |
    |---->parse_early_param()
    |     注意,这里也会根据boot传入的command_line中信息来修
    |     正meminfo的内存信息,此处忽略(假定command_line不含内存信息)。
    |---->early_initrd(char *p)
          |     ramdisk
                |---->phys_initrd_start = start;
                |---->phys_initrd_size = size;
    |
    |---->paging_init(mdesc);
    |     bootmem位图分配器初始化,I/O空间、中断向量空间映射,
    |     PKMAP空间映射初始化,"0"页面建立.
    |---->request_standart_resources(&meminfo, mdesc);
    |
    |---->smp_init_cpus()
    |     对于2.6.34的ARM,我能说这个函数有问题么,这时做了ioremap?
    |     获取核的个数,并在cpu_possible_bits上标注核的存在性
    |
    |---->cpu_init()
    |     为每个核的irq、abt、und状态设置栈,每个状态只有12字节
    |     栈空间(static struct stack stacks[NR_CPUS]),因为
    |     基本所有的事情都在svc状态即被处理
    |
    |---->tcm_init()//tightly coupled memory, tks gaohao
    ||
    |---->early_trap_init()
          |---->memcpy(vectors, __vectors_start,
          |           __vectors_end - __vectors_start);
          |     memcpy(vectors + 0x200, __stubs_start,
          |           __stubs_end - __stubs_start);
          |     拷贝中断向量
          |---->memcpy(vectors + 0x1000 - kuser_sz, 
          |           __kuser_helper_start, kuser_sz);
          |     ARM的特殊之处,为用户态进行原子操作提供接口,
          |     即用户态直接进入该部分(3G~4G),中断处将做
          |     特别检查和相应的处理.见__kuser_helper_version
          | 
          |---->memcpy(KERN_SIGRETURN_CODE, sigreturn_codes,
          |            sizeof(sigreturn_codes));
          |---->memcpy(KERN_RESTART_CODE, syscall_restart_code,
          |            sizeof(syscall_restart_code));
          |
          |---->flush_icache_range(vectors, vectors + PAGE_SIZE);
          |---->modify_domain(DOMAIN_USER, DOMAIN_CLIENT);

void pagint_init(struct machine_desc *mdesc)
    |---->build_mem_type_table()
    |     此处没有深入查看ARM的页表项,
    |     ARM的页表项和unicore不同,我的疑问在于:
    |     ARM页表项中没有提供Dirty、Accessed位,那么kswap线程进行页面回收时,
    |     它是怎样判定该操作哪些页?关于页表项就按unicore的理解,比较简单. 
    ||---->sanity_check_meminfo();
    |     以一块2G DRAM为例,前期meminfo.nr_banks = 1;
    |     开启高端内处支持,则需将meminfo分成两个bank,
    |     (为什么以bank作为变量名,DRAM的物理组成就有bank的概念,
    |      此处需要作出区分)
         |---->struct membank *bank = &meminfo.bank[0];
         |     memove(bank + 1, bank, sizef(*bank))
         |     meminfo.nr_banks++;
         |     bank[1].size -= VMALLOC_MIN - __va(bank->start);
         |     bank[1].start = __pa(VMALLOC_MIN - 1) + 1;
         |     bank[1].highmem = 1;
         |     bank->size = VMALLOC_MIN - __va(bank->start);
         |
    |---->prepare_page_table();
    |     将swapper_pg_dir处的页表清除(部分页表项已缓存在TLB中,在
    |     bootmem_init中会间接调用create_mapping(&map),其中会再次建立)
    |
    |---->bootmem_init();
    |     bootmem分配器初始化.
    |
    |---->devicemaps_init(mdesc);
    |     为中断向量和I/O空间的虚拟与物理地址建立映射关系
    |
    |---->kmap_init()
    |     永久映射区域保留,对于ARM,该区域位于3G-4M ~ 3G
    |
    |---->top_pmd = pmd_off_k(0xffff0000);
    |     记录0xffff0000相应的一级页表项地址.
    |---->zero_page = alloc_bootmem_low_pages(PAGE_SIZE);
    |     分配一个“0”页面.
    |---->empty_zero_page = virt_to_page(zero_page);
    |     管理"0"页面所对应的struct page虚拟地址.
    |---->__flush_dache_page(NULL, empty_zero_page);
void bootmem_init(void)
    |---->struct meminfo *mi = &meminfo;
    |     sort(&mi->bank, mi->nr_banks, sizeof(mi->bank[0]),
    |          meminfo_cmp, NULL);
    |     将meminfo中的bank数组元素按其start地址升序排序
    |
    |---->int initrd_node = 0;
    |     initrd_node = check_initrd(mi);
    |     ramdisk在meminfo下的哪个bank
    |     check_initrd(mi)
          |-->struct membank *bank = &mi->bank[i];
          |   if (bank_phys_start(bank) <= phys_initrd_start &&
          |         end <= bank_phys_end(bank))
          |             initrd_node = bank->node;
          |     
          |     return initrd_node
          |
    |---->for_each_node(node)
    |     UMA体系,只有一个node, 仅循环一次
    |     |---->find_node_limits(node, mi, &min, &node_low, &node_high);
    |     |     此处两个bank(高、低)
    |     |     min:物理内存的最小页帧号(pfn)
    |     |     node_low:物理内存中低端内存的最大页帧号
    |     |     node_high:物理内存中高端内存的最大页帧号
    |     |    
    |     |     max_low:物理内存中低端内存的最大页帧号
    |     |     max_high:物理内存中高端内存的最大页帧号
    |     |
    |     |---->bootmem_init_node(node, mi, min, node_low);
    |     |     详见后文标注;
    |     |     业务在于:将低端内存部分与虚拟空间做固定偏移映射,而且采用一级页表完成;
    |     |             采集位图分配器信息,并存放在contig_page_data.bdata
    |     |             内,而且将位图分配器自身所占用的物理内存在位图分配器内标记为
    |     |             占用,此位图分配器暂时只管理低端内存(依据meminfo.bank[0],
    |     |             未使用meminfo.bank[1]).
    |     |
    |     |           
    |     |---->reserve_node_zero(&contig_page_data)
    |     |     |---->reserve_bootmem_node(pgdat, __pa(_stext), 
    |     |     |           _end - _stext, BOOTMEM_DEFAULT);
    |     |     |     把内核中内核所占物理内存在位图分配器中标记为被占用
    |     |     |
    |     |     |---->reserve_bootmem_node(pgdat, __pa(swapper_pg_dir),
    |     |     |      PTRS_PER_PGD * sizeof(pgd_t), BOOTMEM_DEFAULT);
    |     |     |     把0进程的一级页表所占用的物理内存标记为被占用,
    |     |     |     该一级页表是我们迄今为止惟一没有在内核编译时所占用的空间
    |     |     |
    |     |---->bootem_reserve_initrd(node)
    |     |     |---->res = reserve_bootmem_node(pgdat, 
    |     |     |                               phys_initrd_start, 
    |     |     |                phys_initrd_size, BOOTMEM_EXCLUSIVE);
    |     |     |     这里有个疑问:为什么是BOOTMEM_EXCLUSIVE
    |     |     |---->initrd_start = __phys_to_virt(phys_initrd_start);
    |     |     |     initrd_end = initrd_start + phys_initrd_size;
    |     |     |     文件系统的虚拟起始地址和结束地址
    |     |     |
    |---->for_each_node(node)
    |     UMA体系,只有一个node, 仅循环一次
    |     |---->find_node_limits(node, mi, &min, &max_low, &max_high);
    |     |     此处两个bank(高、低)
    |     |     min:物理内存的最小页帧号(pfn)
    |     |     max_low:物理内存中低端内存的最大页帧号
    |     |     max_high:物理内存中高端内存的最大页帧号
    |     |
    |     |---->unsigned long zone_size[MAX_NR_ZONES], 
    |     |         zhole_size[MAX_NR_ZONES];
    |     |     memset(zone_size, 0, sizeof(zone_size));
    |     |
    |     |     zone_size[0] = max_low - min;
    |     |     ZONE_NORMAL区的页帧数
    |     |
    |     |     zone_size[ZONE_HIGHMEM] = max_high - max_low;
    |     |     ZONE_HIGHMEM的页帧
    |     |
    |     |     memcpy(zhole_size, zone_size, sizeof(zhole_size));
    |     |     从zhole_size的各个区中减去各个zone_size,
    |     |     结果是zhole_size数组元素都为0
    |     |
    |     |---->free_area_init_node(node, zone_size, min, zhole_size);
    |     |     完善contig_page_data,并调用重量级函数:
    |     |        free_area_init_core
    |     |
    |---->high_memory = __va((max_low << PAGE_SHIFT) - 1) + 1;
    |     获取高端内存的起始虚拟地址
    |
    |---->max_low_pfn = max_low - PHYS_PFN_OFFSET;
    |     低端内存所对应的页帧数
    |
    |---->max_pfn = max_high - PHYS_PFN_OFFSET;
    |     总共的物理内存页帧数

void free_area_init_node(int nid, unsigned long *zones_size,
        unsigned long node_start_pfn, unsigned long *zholes_size)
    |---->pg_data_t *pgdat = &contig_page_data;
    |     pgdat->node_id = nid; (即0)
    |     pgdat->node_start_pfn = node_start_pfn;
    |         物理内存起始地址的页帧号
    |
    |---->calculate_node_totalpages(pgdat, zones_size, zholes_size);
    |     |---->totalpages = 该pgdata下的各个区(zone)所含页的页数
    |     |---->pgdat->node_spanned_pages = totalpages;
    |     |---->realtotalpages = totalpages;
    |     |---->realtotalpages -= 该pgdata下各个区(zone)所含的洞的页数
    |     |     对于连续型,实际上不存在“洞”
    |     |---->pgdat->node_present_pages = realtotalpages;
    |     |
    |---->alloc_node_mem_map(pgdat);
    |     为pglist_data建立mem_map(struct page数组)
    |     |---->start = pgdat->node_start_pfn & 
    |     |               ~(MAX_ORDER_NR_PAGES - 1);
    |     |     因为最后要迁移到伙伴系统,因此做了调整
    |     |
    |     |---->end = pgdat->node_start_pfn + pgdat->node_spanned_pages;
    |     |     end = ALIGN(end, MAX_ORDER_NR_PAGES);
    |     |
    |     |---->size = (end - start) * sizeof(struct page);
    |     |     为了管理pglist所跨越的总的页数目,首先获得需要申请的
    |     |     struct page实例的内存大小.
    |     |
    |     |---->struct page *map = NULL;
    |     |     map = alloc_bootmem_node(pgdat, size);
    |     |     依bootmem位图分配器申请内存
    |     |__alloc_bootmem_node(pgdat, size, SMP_CACHE_BYTES,
    |     |                    __pa(MAX_DAM_ADDRESS))
    |     |  |---->ptr = alloc_bootmem_core(pgdat->bdata, size, 
    |     |  |                   align, goal, 0);
    |     |  |     若位图中出现连续的未被占用的页数满足size的要求,则将在位图中
    |     |  |     找到的相应bit位置1(标记被占用),并将对应物理页清0,返回对应
    |     |  |     物理页的虚拟起始地址.
    |     |  |     return ptr;
    |     |  |
    |     |---->pgdat->node_mem_map = map + (pgdat->node_start_pfn
    |     |                                  - start);
    |     |     终于为pglist_data的node_mem_map域建立好了空间,所有的
    |     |     struct page 实例均存于该空间内.
    |     |---->mem_map = (&contig_page_data)->node_mem_map
    |     |
    |---->free_area_init_core(pgdat, zones_size, zholes_size)
    |     |详见下文
    |     | 初始化pgdat下的各个zone及相关信息
void free_area_init_core(struct pglist_data *pgdat,
        unsigned long *zones_size, 
        unsigned long *zholes_size)
    |---->init_waitqueue_head(&pgdat->kswapd_wait);
    |     pgdat->kswapd_max_order = 0;
    |
    |---->pgdat->nr_zones = 0;
    |
    |---->for(j = 0; j < MAX_NR_ZONES; j++)
    |     依次建立pglist_data下的每个zone.
    |
    |     struct zone *zone = pgdat->node_zones + j;
    |     unsigned long size, realsize, memmap_pages;
    |     enum lru_list l;  
    |     |
    |     |---->size = zone_spanned_pages_in_node(nid, j, zones_size);
    |     |     获取该区所跨越的页的总数
    |     |     
    |     |     realsize = size - zone_absent_pages_in_node(nid, j,
    |     |                zholes_size);
    |     |     获取该区实际可用的物理页的总数(除去“洞”)
    |     |
    |     |---->memmap_pages = PAGE_ALIGN(size * sizeof(struct page))
    |     |                    >> PAGE_SHIFT;
    |     |     获取因管理该区所使用的struct page实例的内存大小
    |     |
    |     |---->realisze -= memmap_pages;
    |     |     获取该区实际可用的物理页的总数(除去管理结构所占用页数)
    |     |
    |     |---->if(!is_highmem_idx(j)) nr_kernel_pages += realsize;
    |     |     将非高端内存区中,还未被所占用的页数计入nr_kernel_pages
    |     |
    |     |---->nr_all_pages += realsize;
    |     |     将所有还未被占用的页数计入nr_all_pages
    |     |
    |     |
    |     |开始为pglist_data下的各个区建立信息
    |     |---->zone->spanned_pages = size;
    |     |     将该区跨越的页数存入pglist_data下相应的
    |     |     zone->spanned_pages.
    |     |---->zone->present_pages = realsize;
    |     |     将该区可以使用的实际页数存入pglist_data下相应的
    |     |     zone->present_pages.
    |     |---->zone->name = zones_names[j];
    |     |     为pglist_data下相应的zone添加名称
    |     |---->spin_lock_init(&zone->lock);
    |     |     spin_lock_init(&zone->lru_lock);
    |     |---->zone->zone_pgdat = pgdat;
    |     |     记录zone所在的pglist_data
    |     |---->zone->pre_priority = DEF_PRIORITY
    |     |---->zone_pcp_init(zone);
    |     |     WHAT:????????????????????
    |     |---->for_each_lru(l)
    |     |     {INIT_LIST_HEAD(&zone->lru[l].list);
    |     |      zone->reclaim_stat.nr_saved_scan[l] = 0;}
    |     |---->zone->reclaim_stat.recent_rotated[0] = 0;
    |     |     zone->reclaim_stat.recent_rotated[1] = 0;
    |     |     zone->reclaim_stat.recent_scanned[0] = 0;
    |     |     zone->reclaim_stat.recent_scanned[1] = 0;
    |     |---->memset(zone->vm_stat, 0, sizeof(zone->vm_stat);     
    |     |---->zone->flags = 0;
    |     |
    |     |---->setup_usemap(pgdat, zone, size);
    |     |     将管理该zone中的pageblock的比特位图的起始地址
    |     |     存入zone->pageblock_flags.
    |     |
    |     |---->init_currently_empty_zone(zone, zone_start_pfn,
    |     |         size, MEMMAP_EARLY);
    |     |     详见下文
    |     |     分配zone的hash资源(用于进程请求页时阻塞);
    |     |     初始化zone的free_area,以及free_area元素下
    |     |     的各类free_list.
    |     |
    |     |---->memmap_init(size, nid, j, zone_start_pfn)
    |     |     即:memmap_init_zone(size, nid, j, 
    |     |                   zone_start_pfn, MEMMAP_EARLY);
    |     |     详细见下文
    |     |     该函数的业务: 
    |     |        修正最高的页帧数highest_memap_pfn;
    |     |        获取zone所管理的页对应的struct page实例,
    |     |        在struct page中的flags中标注各种标志;
    |     |        将页所隶属的pageblock的位图标记为MIGRATE_MOVABLE;
    |     |
    |     |---->zone_start_pfn += size;
void memmap_init_zone(unsigned long size, int nid, 
    unsigned long zone,                                             
    unsigned long start_pfn, enum memmap_context context)   
    |---->struct page *page = NULL;
    |     unsigned long end_pfn = start_pfn + size;
    |     unsigned long pfn = 0;
    |     struct zone *z = NULL;
    |
    |---->if(highest_memmap_pfn < end_pfn - 1)
    |        highest_memap_pfn = end_pfn - 1;
    |      修正最高的页帧数
    |
    |---->z = &NODE_DATA(nid)->node_zones[zone];
    |     获取需要操作的zone
    |
    |-->for(pfn = start_pfn; pfn < end_pfn; pfn++)
        |-->page = pfn_to_page(pfn);
        |   获取页帧号所对应的struct page实例地址
        |
        |-->set_page_links(page, zone, nid, pfn);
        |   |-->set_page_zone(page, zone);
        |   |   在struct page->flags中记录该页是属于哪个zone
        |   |-->set_page_node(page, node);
        |   |   set_page_section(page, pfn_to_section_nr(pfn);
        |   |   对于单个node,实际上无需在page->flags中
        |   |   存储node,section信息.
        |   |
        |-->init_page_count(page)
        |   |-->atomic_set(&page->_count, 1);
        |   |   page的访问计数,当为0时,说明page是空闲的,当大于0的时
        |   |   候,说明page被一个或多个进程正在使用该页或者有进程在等待该页.
        |   |   .
        |   |
        |-->reset_page_mapcount(page)
        |   |-->atomic_set(&(page)->_mapcount, -1);
        |   |
        |-->SetPageReserved(page);
        |   | 关于SetPageReserved请参阅:page-flags.h
        |   | 定义了许多宏以及page->flags各位的意义.
        |   |
        |-->INIT_LIST_HEAD(&page->lru)
        |   |
        |--->set_pageblock_migratetype(page, MIGRATE_MOVABLE);
        |    实际上此处是先测试,若满足条件再执行,一般直接执行也没问题。
        |    我们已经知道,内存中的一些页隶属于同一个pageblock,
        |    而且内存所对应的zone中,已存储了管理pageblock的位图
        |    pageblock_flags的起始地址。此函数的任务在于将每个page
        |    所属于的pageblock标记为MIGRATE_MOVABLE(即:属于该
        |    pageblock 中的页均MIGRATE_MOVABLE)
void setup_usemap(struct pglist_data *pgdat,
                  struct zone *zone, unsigne long zonesize)
    |---->unsigned long usemapsize = usemap_size(zonesize);
    |     每个zone中的页按pageblock被分成几个block,一个
    |     pageblock所含页数为(1 << (MAX_ORDER - 1)),每个
    |     pageblock需要几个bit位来存储信息(这几个bit位的
    |     作用,暂时不知道),usemap_size的作用就在于计算
    |     该zone中的pageblock数所对应的bit位数,并转化成字节数.
    |
    |---->zone->pageblock_flags = alloc_bootmem_node(pgdat, usemapsize);
    |     将管理该zone中的pageblock的比特位图的起始地址
    |     存入zone->pageblock_flags.
当对一个page做I/O操作的时候,I/O操作需要被锁住,防止不正确的数据被访问。进程在访问page前,调用wait_on_page()函数,使进程加入一个等待队列。访问完后,UnlockPage()函数解锁其他进程对page的访问。其他正在等待队列中的进程被唤 醒。每个page都可以有一个等待队列,但是太多的分离的等待队列使得花费太多的内存访问周期。替代的解决方法,就是将所有的队列放在struct zone数据结构中。    
                                                                                               
如果struct zone中只有一个队列,则当一个page unlock的时候,访问这个zone里内存page的所有休眠的进程将都被唤醒,这样就会出现拥堵(thundering herd)的问题。建立一个哈希表管理多个等待队列,能解解决这个问题,zone->wait_table就是这个哈希表。哈希表的方法可能还是会造成一些进程不必要的唤醒。
int init_currently_empty_zone(struct zone *zone,
        unsigned long zone_start_pfn,
        unsigned long size,
        enum memmap_context_context)
    |---->zone_wait_table_init(zone, size);
    |     初始化zone下的hash表(用于进程等待页资源时使用,
    |     我们可以将等待对列存放在各个struct page内,但是
    |     这样会使struct page结构体空间太大,造成浪费,
    |     因此放在了zone中,并用hash表实现).
    |     |---->zone->wait_table_hash_nr_entries = 
    |     |       wait_table_hash_nr_entries(size);
    |     |     获取所需的hash表的数组元素个数
    |     |
    |     |---->zone->wait_table_bits = 
    |     |       wait_table_bits(zone->wait_table_hash_nr_entries);
    |     |     获取值wait_table_hash_nr_entries中首个bit位值为1的序号
    |     |     (从最低位0开始记起,例如1,则获取值为0)
    |     |
    |     |---->alloc_size = zone->wait_table_hash_nr_entries *
    |     |          sizeof(wait_queue_head_t);
    |     |     获取所需的hash表的数组所需空间大小
    |     |
    |     |---->zone->wait_table = (wait_queue_head_t *)
    |     |       alloc_bootmem_node(pgdat, alloc_size);
    |     |     分配hash表数组空间
    |     |
    |     |---->init_waitqueue_head(
    |     |     zone->wait_table[0...wait_table_hash_nr_entries]);
    |     |     初始化各个队列头
    |     |
    |---->pgdat->nr_zones = zone_idx(zone) + 1;
    |     更新pgdat下的zone的数目   
    |
    |---->zone->zone_start_pfn = zone_start_pfn;
    |
    |---->zone_init_free_lists(zone);
          |-->for(order = 0; order < MAX_ORDER; order++)
          |     for(type = 0; type < MIGRATE_TYPES; type++)
          |     {INIT_LIST_HEAD(&zone->free_area[order].free_list[type]);
          |      zone->free_area[order].nr_free = 0;}
          |   可以看出,每个zone除了被分为pageblock外,
          |   还被分为数个free_area, 每个free_area又被
          |   分为不同类型的free_list,各个free_area下
          |   的各自的free_list所含的页数是下同的.

static void bootmem_init_node(int node, struct meminfo *mi,
        unsigned int start_pfn, unsigned long end_pfn)
    |---->unsigned long boot_pfn;
    |     unsigned int boot_pages;
    |     pg_data_t *pgdat;
    |     int i;
    |
    |---->for_each_nodebank(i, mi, node)
    |     i依次取得meminfo中的bank索引
    |     struct membank *bank = &mi->bank[i];
    |     if(!bank->highmem) map_memory_bank(bank);
    |     对于低端内存所在的bank,需执行map_memory_bank(bank);
    |    
    |     map_memory_bank(bank)
          |---->struct map_desc map;
          |     map.pfn = bank_pfn_start(bank);
          |     map.virtual = __phys_to_virt(bank_phys_start(bank));
          |     map.length = banks_phys_size(bank);
          |     map.type = MT_MEMORY;
          |     create_mapping(&map);
          |     |---->此处以超页映射(低端内存,一级页表即可完成映射,
          |     |     ,减少TLB刷新)
          |
    |---->boot_pages = bootmem_bootmap_pages(end_pfn - start_pfn)
    |     对于低端内存,先用位图进行管理,获取bit位所需的页数
    |
    |---->boot_pfn = find_bootmap_pfn(node, mi, boot_pages);
    |     获取内核结束地址的页号,作为寻找位图页的起始页
    |
    |---->pg_data_t *pgdat = NODE_DATA(node);
    |---->init_bootmem_node(pgdat, boot_pfn, start_pfn, end_pfn);
          |---->init_bootmem_core(pgdat->bdata, boot_pfn, 
          |                       start_pfn, end_pfn);
          |     见后文对此函数的标注
          |
    |---->for_each_nodebank(i, mi, node)
    |     i依次取得meminfo中的bank索引
    |     struct membank *bank = &mi->bank[i];
    |     if(!bank->highmem)
    |        free_bootmem_node(pgdat, bank_phys_start(bank),
    |                          bank_phys_size(bank));                                
    |     对于低端内存所在的bank,需执行free_bootmem_node
    |
    |     free_bootmem_node---->
    |     mark_bootmem_node(pgdat->bdata, start, end, 0, 0)
    |           start为低端内存起始物理页帧号,
    |           end为低端内存终止页帧号
          |---->__free(bdata, sidx, eidx);
          |     sidx:低端内存起始页号(需减去bdata->node_min_pfn);
          |     eidx:低端内存终止页号(需减去bdata->node_min_pfn);
          |     业务:将bdata中的页图标注为未被占用
          |
    |---->reserve_bootmem_node(pgdat, boot_pfn << PAGE_SHIFT,
    |          boot_pages << PAGE_SHIFT, BOOTMEM_DEFAULT);  
    |     业务:即将位图分配器自身所占用的内存标记为被占用
          |---->mark_bootmem_node(pgdata->bdata, start, end, 1, 0);
          |     start:低端内存中,位图所占用的物理内存起始页帧号
          |     end:低端内存中,位图所占用的物理内存终止页帧号
                |---->sidx = start - bdata->node_min_pfn;
                |     eidx = end - bdata->node_min_pfn;
                |---->__reserve(bdata, sidx, eidx, flags)
                |     sidx:低端内存起始页号(需减去bdata->node_min_pfn);
                |     eidx:低端内存终止页号(需减去bdata->node_min_pfn);
                |     业务:将bdata中的页图标注为被占用
                |

unsigned long init_bootmem_core(bootmem_data_t *bdata,
        unsigned long mapstart, unsigned long start, unsigned long end)
    |---->bdata->node_bootmem_map = phys_to_virt(PFN_PHY(mapstart));
    |     bdata->node_bootmem_map存放位图页的虚拟地址
    |---->bdata->node_min_pfn = start;
    |     存放低端内存的起始物理页号
    |---->bdata->node_low_pfn = end;
    |     存放低端内存的结束物理页号
    |
    |---->link_bootmem(bdata);
    |     将bdata按照node_min_pfn值的升序顺序插入到bdata_list链表中
    |---->unsigned long mapsize = bootmap_bytes(end - sart);
    |     获取位图所需的字节数  
    |     memset(bdata->node_bootmem_map, 0XFF, mapsize);
    |     将位图全部标记为已被占用(后期会再做修改, 注意文件系统位置)

static void devicemaps_init(struct machine_desc *mdesc)
    |---->void *vectors = NULL;
    |     vectors = alloc_bootmem_low_pages(PAGE_SIZE);
    |     为中断向量申请内存空间,
    |     实际上仍是通过alloc_bootmem_core函数完成内存分配.
    |
    |---->for(addr = VMALLOC_END; addr; addr += PGDIR_SIZE)
    |          pmd_clear(pmd_off_k(addr))
    |     将VMALLOC_END ~ 4G的页表映射全部清除
    |---->map.pfn = __phys_to_pfn(virt_to_phys(vectors));
    |     map.virtual = CONFIG_VECTORS_BASE;
    |     map.length = PAGE_SIZE;
    |     map.type = MT_HIGH_VECTORS;
    |     create_mapping(&map);
    |     将物理地址和虚拟地址建立映射关系,此处即为:
    |     为中断向量虚拟地址寻找一个物理页面,并且建立映射关系.
    |    
    |---->mdesc->map_io()
    |     为I/O空间建立映射,注意页表中cache的属性,
    |     这部分完全和SOC设计相关,将需要建立的映射关系
    |     存放于一个struct map_desc实例数组中,调用
    |     create_mapping完成I/O空间映射.
    |---->local_flush_tlb_all();
    |     flush_cache_all();
    |     同步硬件缓存与物理内存.
void kmap_init(void)
    |---->pmd_t *pmd = pmd_off_k(PKMAP_BASE);
    |     获取PKMAP_BASE虚拟地址所对应的一级页表项的的地址
    |     关于PKMAP_BASE,网上有很多都说是接近4G,但是我只在
    |     X86上看到是这样,而ARM或者unicore都是:
    |     PAGE_OFFSET - PMD_SIZE 暂时不知作出改动的原因.
    |---->pte_t *pte = alloc_bootmem_low_pages(
    |          PTRS_PER_PTR * sizeof(pte_t);
    |     PKMAP空间需做二级页表映射,此处获得二级页表的起始
    |     地址.
    |---->__pmd_populate(pmd, __pa(pte) | 
    |                    _PAGE_KERNEL_TABLE);
    |     在相应的一级页表项中计入二级页表项的物理地址,并设置好
    |     一级页表项的属性.
    |---->pkmap_page_table = pte + PTRS_PER_PTE
    |     记录PKMAP虚拟空间的二级页表项的物理末尾地址.

void request_standard_resources(struct meminfo *mi,
        struct machine_desc *mdesc)
    |---->kernel_code.start = virt_to_phys(_text);
    |     kernel_code.end = virt_to_phys(_etext - 1);
    |     kernel_data.strt = virt_to_phys(_data);
    |     kernel_data.end = virt_to_phys(_end - 1);
    |
    |---->for(i = 0; i < mi->nr_banks; i++)
    |     此处是将meminfo的资源放入iomem_resource树中,
    |     同时将内核镜像资源也放入iomem_resource树中.
    |     注意内核镜像资源如何放入.
    |
    |     struct *res = NULL;
    |     res = alloc_bootmem_low(sizeof(*res));
    |     res->name = "System RAM"
    |     res->start = mi->bank[i].start;
    |     res->end = mi->bank[i].start + mi->bank[i].size - 1;
    |     res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
    |
    |     request_resource(&iomem_resource, res)
    |     将内存资源放入iomem_resource树中.
    |     
    |     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);
    |     将内核镜像资源放入iomem_resource树中.

内存管理 初始化(二)bootmem位图分配器建立 及 使用 - 阿加 - 博客园

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值