本地的笔记有点长,先把bootmem位图分配器的建立 及  使用过程做下梳理。

都是代码,上面做了标注。开始的汇编部分省略了(涉及的内容不多,除了swapper_pg_dir的分配)。

该记录不会再添加说明,看下记录中的注释就明白了bootmem的建立及使用。

该记录中考虑了高端内存……

从start_kernel开始……

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 = ;
| 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空间映射初始化,""页面建立.
|---->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);

//paging_init 非常重要:

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 = ;
| 开启高端内处支持,则需将meminfo分成两个bank,
| (为什么以bank作为变量名,DRAM的物理组成就有bank的概念,
| 此处需要作出区分)
|---->struct membank *bank = &meminfo.bank[];
| memove(bank + , bank, sizef(*bank))
| meminfo.nr_banks++;
| bank[].size -= VMALLOC_MIN - __va(bank->start);
| bank[].start = __pa(VMALLOC_MIN - ) + ;
| bank[].highmem = ;
| 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);
| 分配一个“”页面.
|---->empty_zero_page = virt_to_page(zero_page);
| 管理""页面所对应的struct page虚拟地址.
|---->__flush_dache_page(NULL, empty_zero_page);

//bootmem_init 完成位图分配器的建立,bootmem_init也使用了位图分配器进行内存分配

void bootmem_init(void)
|---->struct meminfo *mi = &meminfo;
| sort(&mi->bank, mi->nr_banks, sizeof(mi->bank[]),
    |          meminfo_cmp, NULL);
| 将meminfo中的bank数组元素按其start地址升序排序
|
|---->int initrd_node = ;
| 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[],
| | 未使用meminfo.bank[]).
| |
| |
| |---->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, , sizeof(zone_size));
| |
| | zone_size[] = 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) - ) + ;
| 获取高端内存的起始虚拟地址
|
|---->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 - );
| | 因为最后要迁移到伙伴系统,因此做了调整
| |
| |---->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, );
| | | 若位图中出现连续的未被占用的页数满足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 = ;
|
|---->pgdat->nr_zones = ;
|
|---->for(j = ; 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] = ;}
| |---->zone->reclaim_stat.recent_rotated[] = ;
| | zone->reclaim_stat.recent_rotated[] = ;
| | zone->reclaim_stat.recent_scanned[] = ;
| | zone->reclaim_stat.recent_scanned[] = ;
| |---->memset(zone->vm_stat, , sizeof(zone->vm_stat);
| |---->zone->flags = ;
| |
| |---->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 = ;
| struct zone *z = NULL;
|
|---->if(highest_memmap_pfn < end_pfn - )
| highest_memap_pfn = end_pfn - ;
| 修正最高的页帧数
|
|---->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, );
| | page的访问计数,当为0时,说明page是空闲的,当大于0的时
| | 候,说明page被一个或多个进程正在使用该页或者有进程在等待该页.
| | .
| |
|-->reset_page_mapcount(page)
| |-->atomic_set(&(page)->_mapcount, -);
| |
|-->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所含页数为( << (MAX_ORDER - )),每个
| 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[...wait_table_hash_nr_entries]);
| | 初始化各个队列头
| |
|---->pgdat->nr_zones = zone_idx(zone) + ;
| 更新pgdat下的zone的数目
|
|---->zone->zone_start_pfn = zone_start_pfn;
|
|---->zone_init_free_lists(zone);
|-->for(order = ; order < MAX_ORDER; order++)
| for(type = ; type < MIGRATE_TYPES; type++)
| {INIT_LIST_HEAD(&zone->free_area[order].free_list[type]);
| zone->free_area[order].nr_free = ;}
| 可以看出,每个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, , )
| 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, , );
| 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虚拟空间的二级页表项的物理末尾地址.

//request_stanard_resources描述地有些不准确

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 - );
| kernel_data.strt = virt_to_phys(_data);
| kernel_data.end = virt_to_phys(_end - );
|
|---->for(i = ; 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 - ;
| 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位图分配器建立 及 使用的更多相关文章

  1. 启动期间的内存管理之bootmem_init初始化内存管理–Linux内存管理(十二)

    1. 启动过程中的内存初始化 首先我们来看看start_kernel是如何初始化系统的, start_kerne定义在init/main.c?v=4.7, line 479 其代码很复杂, 我们只截取 ...

  2. 内存管理 初始化(四)mem_init bootmem 迁移至伙伴系统

    mm_init中执行mem_init,将原通过bootmem分配器管理的低端内存 及  通过meminfo得知的高端内存释放到伙伴系统中,最后bootmem位图本身占用的低端内存物理页也被释放进伙伴系 ...

  3. Linux内存管理(二)

    Linux内存管理之二:Linux在X86上的虚拟内存管理 本文档来自网络,并稍有改动. 前言 Linux支持很多硬件运行平台,常用的有:Intel X86,Alpha,Sparc等.对于不能够通用的 ...

  4. 内存管理 初始化(八) 至kswapd_init

    至此,内存初始化部分已看完,遗留问题: 1.对于unicore或者mips的页表建立都很清楚,但是对于ARM我不清楚: 初始化部分涉及的页表映射建立,我都以unicore架构为准,ARM的页表映射从原 ...

  5. 垃圾回收GC:.Net自己主动内存管理 上(二)内存算法

    垃圾回收GC:.Net自己主动内存管理 上(二)内存算法 垃圾回收GC:.Net自己主动内存管理 上(一)内存分配 垃圾回收GC:.Net自己主动内存管理 上(二)内存算法 垃圾回收GC:.Net自己 ...

  6. 内存管理初始化源码3:bootmem

    start_kernel ——> setup_arch ——> arch_mem_init ——> bootmem_init ——> init_bootmem_node: 此时 ...

  7. 内存管理 初始化(五)kmem_cache_init 初始化slab分配器(上)

    看了下kmem_cache_init,涉及到不同MIGRATE间的buddy system的迁移,kmem_cache的构建,slab分配器头的构建.buddy system的伙伴拆分. 对于SMP系 ...

  8. linux内存管理初始化

    内存管理子系统是linux内核最核心最重要的一部分,内核的其他部分都需要在内存管理子系统的基础上运行.而对其初始化是了解整个内存管理子系统的基础.对相关数据结构的初始化是从全局启动例程start_ke ...

  9. 内存管理初始化源码5:free_area_init_nodes

    start_kernel ——> setup_arch ——> arch_mem_init ——> |——> bootmem_init  |——> device_tree ...

随机推荐

  1. error "OPatch cannot find a valid oraInst.loc file to locate Central Inventory

    Error tersebut terjadi ketika akan menjalankan command opatch lsinventory untuk mengetahui patch yan ...

  2. 用Total Commander for Android管理应用程序

    用Total Commander for Android管理应用程序 前不久安装了一个Total Commander的Anroid版本,除了用它来管理文件之外,我发现用它管理已安装程序挺不错的. 可以 ...

  3. Java编程的逻辑 (31) - 剖析Arrays

    ​本系列文章经补充和完善,已修订整理成书<Java编程的逻辑>,由机械工业出版社华章分社出版,于2018年1月上市热销,读者好评如潮!各大网店和书店有售,欢迎购买,京东自营链接:http: ...

  4. 【进阶修炼】——改善C#程序质量(3)

    32, 总是优先考虑泛型. 泛型代码有很好的重复利用性,和类型安全性. 33, 应尽量避免在泛型类中声明静态成员. 静态成员达不到共享的目的.List<int>和List<Strin ...

  5. rsa.FromXmlString 系统找不到指定的文件

    rsa做加密解密的时候,发布到IIS7 Windows2008上出现异常,提示rsa.FromXmlString找不到指定的文件. 原因是系统安全性问题,做了变更,修改为如下代码即可: System. ...

  6. iframe中跨域页面访问parent的方法

    背景 如上图所示,系统www.a.com/index.html页面中嵌入一个iframe,iframe中访问不同域的www.b.com/index.html 然后b中有个按钮“保存”,想调用父页面a. ...

  7. Git 环境设置(安装)

    在使用Git之前,必须安装它,并做一些基本配置的变化.下面是步骤在Ubuntu和CentOS Linux安装 Git 客户端. Git客户端安装 如果使用的是GNU/ Linux 发行版Debian基 ...

  8. 30个开源电子商务系统(PHP)

    osCommerce osCommerce是一款著名的PHP开源电子商务解决方案,提出“开箱即用”的强大功能,使网上商店安装非常方便快捷,并可以作为GNU通用公共授权的开源项目免费发布.osComme ...

  9. socket阻塞与非阻塞,同步与异步、I/O模型

    socket阻塞与非阻塞,同步与异步 1. 概念理解 在进行网络编程时,我们常常见到同步(Sync)/异步(Async),阻塞(Block)/非阻塞(Unblock)四种调用方式:同步:      所 ...

  10. Android NDK: Application targets deprecated ABI(s): armeabi Open File

    Error:(81) Android NDK: Application targets deprecated ABI(s): armeabi Error:(82) Android NDK: Suppo ...