内存管理 初始化(四)mem_init bootmem 迁移至伙伴系统
mm_init中执行mem_init,将原通过bootmem分配器管理的低端内存 及 通过meminfo得知的高端内存释放到伙伴系统中,最后bootmem位图本身占用的低端内存物理页也被释放进伙伴系统,当然对于内核、初始页表、pkmap页表、struct page实例、ramdisk、percpu变量、dentry_hashtable、inode_hash_table已经被占用的区域不会被释放(对于内核开始的一段,后面会释放).
start_kernel()
|---->page_address_init()
| 考虑支持高端内存
| 业务:初始化page_address_pool链表;
| 将page_address_maps数组元素按索
| 引降序插入page_address_pool链表;
| 初始化page_address_htable数组
|
|---->setup_arch(&command_line);
|
|---->setup_per_cpu_areas();
| 为per-CPU变量分配空间
|
|---->build_all_zonelist()
| 为系统中的zone建立后备zone的列表.
| 2.6.34中的建立过程与《深入Linux内核架构》中
| p_134~p_135的图不符(即使是UMA也不同),
| 书中讲述是每个zone都有自己的zonelist,
| 2.6.34中对于UMA,所有zone的后备列表都在
| pglist_data->node_zonelists[]中;
|
| 期间也对per-CPU变量boot_pageset做了初始化.
|
|---->page_alloc_init()
|---->hotcpu_notifier(page_alloc_cpu_notifier, );
| 不考虑热插拔CPU
|
|---->pidhash_init()
| 详见下文.
| 根据低端内存页数和散列度,分配hash空间,并赋予pid_hash
|
|---->vfs_caches_init_early()
|---->dcache_init_early()
| dentry_hashtable空间,d_hash_shift, h_hash_mask赋值;
| 同pidhash_init();
| 区别:
| 散列度变化了( - PAGE_SHIFT);
| 传入alloc_large_system_hash的最后参数值为0;
|
|---->inode_init_early()
| inode_hashtable空间,i_hash_shift, i_hash_mask赋值;
| 同pidhash_init();
| 区别:
| 散列度变化了( - PAGE_SHIFT);
| 传入alloc_large_system_hash的最后参数值为0;
|
|---->mm_init()
|
void mm_init(void)
|---->mem_init()
| 业务:bootmem迁移至伙伴系统
|
|---->
void mem_init(void)
|-->max_mapnr = pfn_to_page(max_pfn + PHYS_PFN_OFFSET) - mem_map;
| max_pfn是物理内存的最大页数量,PHYS_PFN_OFFSET是物理内存的起始
| 地址在4G空间中的页帧号;
| pfn_to_page(max_pfn + PHYS_PFN_OFFSET)是物理内存终结地址所在的页
| 锁对应的struct page实例虚拟地址,减去mem_map(struct page起始虚
| 拟地址),故max_mapnr是struct page实例的数量
|
|-->free_unused_memmap_node(, &meminfo)
| 对于连续内存,bank之间没有间隙,因此free_unused_memmap_node不会执行.
|
|-->totalram_pages += free_all_bootmem_node(pgdat);
| |--->return free_all_bootmem_core(pgdat->bdata);
| 、将低端内存中未被使用的页释放到伙伴系统中;
| 、bootmem位图分配器占用的页也释放到了伙伴系统中;
|
|-->for_each_nodebank(i, &meminfo, node = )
|--{
| unsigned long start = bank_pfn_start(&meminfo.bank[i]);
| unsigned long end = bank_pfn_end(&meminfo.bank[i]);
|
| 即:只对于高端内存使用free_area(start, end, NULL)
| if(start >= max_low_pfn + PHYS_PFN_OFFSET)
| totoalhigh_pates += free_area(start, end, NULL);
|--}
|
|--totoalram_pages += totoalhigh_pages;
|
|
|--for_each_nodebank(i, &meminfo, node)
|--{ //统计已被分配的页数(物理页已被使用),并存入reserved_pages;
//统计未被分配的页数(物理页未被使用),并存入free_pages;
| ………………
|--}
|
|--num_physpages = meminfo中的各个membank下的总管理区内存大小.
|
|-->printk: nr_free_pages() << (PAGE_SHIFT) -
| 关于nr_free_pages()中涉及的值,实际上是在free_one_page函数
| 中完成的--->__mod_zone_page_state(zone, NR_FREE_PAGES, << order),
| 其改变了zone_vm_stat[NR_FREE_PAGES]的值.
void free_unused_memmap_node(int node, struct meminfo *mi)
|-->unsigned long bank_start, prev_bank_end = ;
| unsigned int i = ;
|
|-->for_each_nodebank(i, mi, node)
| 遍历属于该node的meminfo下的所有membank;
| 对于UMA,membank分为低端内存和高端内存两个bank
|
| struct membank *bank = &mi->bank[i];
| bank_start = bank_pfn_start(bank);
|
| if(prev_bank_end && prev_bank_end != bank_start)
| free_memmap(node, prev_bank_end, bank_start)
| 对于连续内存,bank之间没有间隙,因此free_memmap不会执行.
|
| prev_bank_end = bank_pfn_end(bank);
|--
|
void free_memmap(int node, unsigned long start_pfn,
unsigned long end_pfn)
|-->struct page *start_pg = NULL, *end_pg = NULL;
| unsigned long pg = , pgend = ;
|
|-->start_pg = pfn_to_page(start_pfn - ) + ;
| 该页帧号所对应的struct page实例的虚拟地址
| end_pg = pfn_to_page(end_pfn);
| 该页帧号所对应的struct page实例的虚拟地址
|
|-->pg = PAGN_ALIGN(__pa(start_pg);
| 获取start_pg所对应的虚拟地址,即start_pfn页帧号所对应的struct page实例
| 的物理地址.
| pgend = __pa(end_pg) & PAGE_MASK;
| 获取end_pg所对应的虚拟地址,即end_pfn页帧号所对应的struct page实例
| 的物理地址.
|
|-->free_bootmem_node(&contig_page_data, pg, pgend - pg);
| 将bootmem分配器中[pg,pgend]所对应的页的bit标志位清0.
|
int free_area(unsigned long pfn, unsigned long end, char *s)
|-->unsigned int pages = , size = (end - pfn) << (PAGESHITF - );
|
|--for(; pfn < end; pfn++)
|--{
| struct page *page = pfn_to_page(pfn);
| ClearPageReserved(page);
| init_page_count(page);
|
| __free_page(page);
| |--->free_pages(page, );
| 详见下文
|
| page++;
|--}
|
unsigned long free_all_bootmem_core(bootmem_data_t *bdata)
|-->unsigned long start = bdata->node_min_pfn;
| 存放低端内存的起始物理页号.
| unsigned long end = bdata->node_low_pfn;
| 存放低端内存的结束物理页号.
|
|-->while(start < end)
|--{
| unsigned long *map = bdata->node_bootmem_map;
| idx = start - bdata->node_min_pfn;
| 获取物理内存页帧相对于起始物理内存页帧号的偏移(从0记).
| vec = ~map[idx/BITS_PER_LONG];
| 取构成一个字的位图的反码.
|
| if(vec == ~0UL && start + BITS_PER_LONG < end)
| 如果一个字内的位图全为0,即一个字内的页都可释放
| {int order = ilog2(BITS_PER_LONG);
| __free_pages_bootmem(pfn_to_page(start), order);
| count += BITS_PER_LONG;}
|
| else //该字内的位图不全为0
| {遍历字内的每一bit位,该bit位在字内偏移量为off.
| 若bit位值为1,则 :
| page = pfn_to_page(start + off);
| __free_pages_bootmem(page, );
| count++;}
|
| start += BITS_PER_LONG;
|--}
|
|-->page = virt_to_page(bdata->node_bootmem_map);
| 获取位图占用的页的相应的struct page 实例的起始虚拟地址.
|
| pages= bdata->node_low_pfn - bdata->node_min_pfn;
| pages = bootmem_bootmap_pages(pages);
| 获取位图所占用的页数
|
| count += pages;
| 更新释放的总页面数
|
|-->while(pages--)
| __free_pages_bootmem(page++; );
| 将bootmem位图分配器所占用的页释放到buddy system
|
|-->return count;
| 返回释放给buddy system总的页面数
void __free_pages_bootmem(struct page *page, unsigned int order)
|-->if(order == )
|--{
| __ClearPageReserved(page);
| 将pgge->flags的PG_reserved清0.
| set_page_count(page, );
| 将page->_count清0.
| set_page_refcounted(page);
| 将page->_count置1.
| __free_page(page);
| |-->__free_pages(page, 0)
|--}
|
|--else
|--{
| int loop = ;
| for(loop = ; loop < BITS_PER_LONG; loop++)
| { struct page *p = &page[loop];
| __ClearPageReReserved(p);
| 将pgge->flags的PG_reserved清0.
| set_page_count(p, );
| 将page->_count清0.}
|
| set_page_refcounted(page);
| //注意此处在循环外只将一个字内的第一个struct page的_count置为1.
| __free_pages(page, order);
|
|--}
void __free_pages(struct page* page, unsigned int order)
|-->if(put_page_testzero(page))
|--{
| //put_page_testzero(page)的意图在于将page->_count值减去1,并
| //检测page->_count的值是否为0,若为0,则执行该块语句.
| if(order == )
| free_hot_cold_page(page, );
| else
| __free_pages_ok(page, order);
|--}
//我们此处只看系统初始化时的情形
/*
* Free a 0-order page
* cold == 1 ? free a cold page : free a hot page
*/
void free_hot_cold_page(struct page *page, int cold)
|-->struct zone *zone = page_zone(page)
| 通过page->flags获取该page所属的zone.
|
|-->int migratetype = get_pageblock_migratetype(page)
| 根据page所属的pageblock获取迁移类型, MIGRATETYPE_MOVABLE
|
|-->set_page_private(page, migratetype);
| 初始化时,page设置为MIGRATETYPE_MOVABLE
|
|-->struct per_cpu_pages *pcp = NULL;
| pcp = &this_cpu_ptr(zone->pageset)->pcp;
|
|-->if(cold)
| list_add_tail(&page->lru, &pcp->lists[migratetype]);
| else
| list_add(&page->lru, &pcp->lists[migratetype]);
|
|-->pcp->count++;
|
| 初始化时pcp->count = -- >; pcp->high = ; pcp->batch = ;
|-->if(pcp->count >= pcp->high)
| { freepcppages_bulk(zone, pcp->batch, pcp);
| pcp->count -= pcp->batch; }
|
|
void free_pcppages_bulk(struct zone *zone, int count, struct per_cpu_pages *pcp)
|-->我们此处回避一些问题,因为本记录以初始化为主,所以,我只下该函数在初始化
| 时的业务.
| list_del(&page->lru); 从MIGRATETYPE_MOVABLE上取下.
| __free_one_page(page, zone, , page_private(page));
void __free_pages_ok(page, order)
|-->free_one_page(page_zone(page), page, order,
| get_pageblock_migratetype(page));
|-->__free_one_page(page, zone, order, migratetype);
void __free_one_page(struct page* page,
struct zone *zone,
unsigned int order,
int migratetype)
|-->unsigned int page_index = page_to_pfn(page)
| & (( << MAX_ORDER) - );
|
|--while(order < MAX_ORDER - )
|--{
| unsigned long combined_idx;
| struct page *buddy;
|
| buddy = __page_find_buddy(page, page_idx, order);
| 找出可与page_idx构成的伙伴.
|
| 测试与page_idx相应的页,是否在伙伴系统中
| if(!page_is_buddy(page, buddy, order))
| break;
|
| 如果在伙伴系统内,则执行伙伴合并,有可能连锁合并,因此用了while循环
| list_del(&buddy->lru);
|
| zone->free_area[order].nr_free--;
| nr_free的意义:处于同一个order下,有nr_free * (**order)个页
|
| rmv_page_order(buddy);
|
| combined_idx = __find_combined_index(page_idx, order);
| 因为可能发生连锁合并,所以计算了combined_idx.
|--}
|
| 对于初始化阶段,均加入free_list[MIGRATETYPE_MOVABLE];
|-->set_page_order(page, order);
| list_add(&page->lru,
| &zone->free_area[order].free_list[migratetypes]);
| zone->free_area[order].nr_free++;
内存管理 初始化(四)mem_init bootmem 迁移至伙伴系统的更多相关文章
- 内存管理 初始化(五)kmem_cache_init 初始化slab分配器(上)
看了下kmem_cache_init,涉及到不同MIGRATE间的buddy system的迁移,kmem_cache的构建,slab分配器头的构建.buddy system的伙伴拆分. 对于SMP系 ...
- 内存管理 初始化(八) 至kswapd_init
至此,内存初始化部分已看完,遗留问题: 1.对于unicore或者mips的页表建立都很清楚,但是对于ARM我不清楚: 初始化部分涉及的页表映射建立,我都以unicore架构为准,ARM的页表映射从原 ...
- 内存管理 初始化(三)before mm_init()
看到了mm_init(),期间将从bootmem迁移到伙伴系统,slab分配器也会建立. 在分析mm_init()之前,把setup_arch(&command_line)之后的函数分析了以下 ...
- 内存管理 初始化(二)bootmem位图分配器建立 及 使用
本地的笔记有点长,先把bootmem位图分配器的建立 及 使用过程做下梳理. 都是代码,上面做了标注.开始的汇编部分省略了(涉及的内容不多,除了swapper_pg_dir的分配). 该记录不会再添 ...
- 内存管理初始化源码3:bootmem
start_kernel ——> setup_arch ——> arch_mem_init ——> bootmem_init ——> init_bootmem_node: 此时 ...
- 启动期间的内存管理之pagging_init初始化分页机制--Linux内存管理(十四)
1 今日内容(分页机制初始化) 在初始化内存的结点和内存区域之前, 内核先通过pagging_init初始化了内核的分页机制. 在分页机制完成后, 才会开始初始化系统的内存数据结构(包括内存节点数据和 ...
- linux内存管理初始化
内存管理子系统是linux内核最核心最重要的一部分,内核的其他部分都需要在内存管理子系统的基础上运行.而对其初始化是了解整个内存管理子系统的基础.对相关数据结构的初始化是从全局启动例程start_ke ...
- 内存管理初始化源码4:add_active_range
我们在阅读源码时,函数功能可以分为两类:1. bootmem.c 2. page_alloc.c. 1. bootmem.c是关于bootmem allocator的,上篇文章已经简述过. 2. pa ...
- 内存管理初始化源码2:setup_arch
PFN相关宏说明: /* kernel/include/linux/pfn.h */ PFN : Page Frame Number(物理页帧) /* * PFN_ALIGN:返回地址x所在那一页帧的 ...
随机推荐
- C++类默认函数
问题,which is true??? 每个类都有一个无参构造函数 每个类都有一个拷贝构造函数 每个类可以有多个构造函数 每个类可以多个析构函数 默认构造函数 析构函数 拷贝构造函数 赋值 ...
- [转]Oracle的update语句优化研究
原文地址:http://blog.csdn.net/u011721927/article/details/39228001 一. update语句的语法与原理 1. 语法 单表 ...
- [转]java利用AES实现URL的参数加密
原文地址:http://h5566h.iteye.com/blog/1465426 很多时候需要在URL传参,希望URL参数能够加密,这里我结合了文章http://www.2cto.com/kf/20 ...
- CentOS6.5 安装Python 的依赖包
1.CentOS6.5 安装Python 的依赖包 yum groupinstall "Development tools"yum install zlib-devel bzip2 ...
- am335x uboot, kernel 编译
一.设置环境变量 // 写在家目录下面的 .bashrc 里面 export KERNEL_PATH=~/aplex/kernel3.2.0 // kernel 路径 export UBOOT_PAT ...
- [转载]番茄时间管理法(Pomodoro Technique):一个番茄是如何让你工作更有效率的
如果你经常读一些关于提高工作效率或时间管理类的博客,一定听说过番茄时间管理法(Pomodoro Technique).这是一种极好的帮助你集中注意力.获得更高工作效率的方法. 基本上,它的实施方法是这 ...
- Spark Streaming自定义Receivers
自定义一个Receiver class SocketTextStreamReceiver(host: String, port: Int( extends NetworkReceiver[String ...
- .NetCore程序发布到IIS上面
一.概述 在传统的.NET Framework中,ASP.NET程序发布到IIS上面,是由IIS的工作进程(w3wp.exe)托管的,在任务管理器中可以找到该进程.在ASP.NET Core程序中不再 ...
- 将url参数转为json对象
/** * 将url参数转为json对象 * * @param str * @returns {{}} */ function parseQueryString(str){ arr = [], len ...
- python 读写二进制文件实例
本程序,首先写入一个矩阵到二进制文件中,然后读取二进制文件恢复到另外一个矩阵中. #coding:utf--8 #https://www.cnblogs.com/cmnz/p/6986979.html ...