linux内存管理分析2文档格式.docx
- 文档编号:5701537
- 上传时间:2023-05-05
- 格式:DOCX
- 页数:26
- 大小:23.79KB
linux内存管理分析2文档格式.docx
《linux内存管理分析2文档格式.docx》由会员分享,可在线阅读,更多相关《linux内存管理分析2文档格式.docx(26页珍藏版)》请在冰点文库上搜索。
每一个机器平台都由一个structmachine_desc结构来描述,内核所支持的所有平台对应的machine_desc结构都包含在段.init.arch.info的__arch_info_begin到__tagtable_end之间。
但每一个平台都有其唯一的机器码machine_arch_type,可通过机器码在段.init.arch.info中找到对应的平台描述结构。
函数setup_machine_tags就是根据机器码找到对应的平台描述结构,并且分析内核参数中内存相关的信息,用以初始化内存块管理结构membank。
mdesc=setup_machine_fdt(__atags_pointer);
if(!
mdesc)
mdesc=setup_machine_tags(machine_arch_type);
machine_desc=mdesc;
machine_name=mdesc->
name;
根据mdesc->
dma_zone_size设置DMA区域的大小arm_dma_zone_size,和DMA区域的结束地址arm_dma_limit
setup_dma_zone(mdesc);
结构structmm_struct管理进程的虚拟地址空间,所有内核线程都使用共同的地址空间,因为他们都是用相同的地址映射,这个地址空间由init_mm来描述。
_text和_etext表示内核镜像代码段的起始和结束位置,_etext和_edata之间是已初始化数据段,_edata到_end是未初始化数据段等,_end之后便是堆区。
init_mm.start_code=(unsignedlong)_text;
init_mm.end_code=(unsignedlong)_etext;
init_mm.end_data=(unsignedlong)_edata;
init_mm.brk=(unsignedlong)_end;
内核命令行参数在函数setup_machine_tags获取并保存在了boot_command_line中
strlcpy(cmd_line,boot_command_line,COMMAND_LINE_SIZE);
*cmdline_p=cmd_line;
分析命令行参数,主要关注一些与内存相关的东西
parse_early_param();
将内存块按从小到大排序
sort(&
meminfo.bank,meminfo.nr_banks,sizeof(meminfo.bank[0]),meminfo_cmp,NULL);
扫描各个内存块,检测低端内存的最大值arm_lowmem_limit,设置高端内存起始值的虚拟地址high_memory
sanity_check_meminfo();
将所有内存块添加到结构memblock的memory区中,将已使用的内存添加到reserved区中去。
arm_memblock_init(&
meminfo,mdesc);
创建内核页表,初始化自举分配器
paging_init(mdesc);
内核中将许多物理资源用structresource结构来管理,下面函数就是将IO内存作为resource注册到内核
request_standard_resources(mdesc);
如果内核命令行中有预留用于内核crash是的转存空间,就将这些存储空间标记为已分配reserve_crashkernel();
setup_arch--->
setup_machine_tags】
staticstructmachine_desc*__initsetup_machine_tags(unsignedintnr)
structtag*tags=(structtag*)&
init_tags;
structmachine_desc*mdesc=NULL,*p;
char*from=default_command_line;
init_tags.mem.start=PHYS_OFFSET;
下面循环根据机器号在段.init.arch.info中寻找对应的machine_desc结构
for_each_machine_desc(p)
if(nr==p->
nr){
printk("
Machine:
%s\n"
p->
name);
mdesc=p;
break;
Bootloader传入的参数地址存放在__atags_pointer中
if(__atags_pointer)
tags=phys_to_virt(__atags_pointer);
elseif(mdesc->
atag_offset)
tags=(void*)(PAGE_OFFSET+mdesc->
atag_offset);
内核参数是由structtag来管理,其中第一个tag类型必然是ATAG_CORE
if(tags->
hdr.tag!
=ATAG_CORE){
tags=(structtag*)&
内核提供的一个默认参数列表
函数mdesc->
fixup中一般会获取内存块的信息
if(mdesc->
fixup)
mdesc->
fixup(tags,&
from,&
meminfo);
hdr.tag==ATAG_CORE){
如果内存块已经初始化,就将参数列表中关于内存的参数标记为ATAG_NONE
if(meminfo.nr_banks!
=0)
squash_mem_tags(tags);
将参数列表拷贝到一个静态数组atags_copy中
save_atags(tags);
分析内核参数,后面细讲
parse_tags(tags);
将解析出来的内核命令行信息拷贝到静态数组boot_command_line中。
在内核启动期间用了很多静态存储空间,它们前面缀有__initdata,像这样的空间在内核启动起来后将被释放
strlcpy(boot_command_line,from,COMMAND_LINE_SIZE);
returnmdesc;
setup_machine_tags--->
parse_tags】
staticvoid__initparse_tags(conststructtag*t)
遍历参数列表中每一个参数结构
for(;
t->
hdr.size;
t=tag_next(t))
parse_tag(t))
parse_tags--->
parse_tag】
staticint__initparse_tag(conststructtag*tag)
externstructtagtable__tagtable_begin,__tagtable_end;
structtagtable*t;
参数类型多种多样解析方式也各不相同,所有针对每一种参数类型都有一个对应的解析函数,这些解析函数和其参数类型由结构structtagtable来管理。
这些结构都存放在段.init.tagtable的__tagtable_begin和__tagtable_end之间。
for(t=&
__tagtable_begin;
t<
&
__tagtable_end;
t++)
if(tag->
hdr.tag==t->
tag){
t->
parse(tag);
returnt<
参数ATAG_MEM的解析函数定义如下:
staticint__initparse_tag_mem32(conststructtag*tag)
returnarm_add_memory(tag->
u.mem.start,tag->
u.mem.size);
__tagtable(ATAG_MEM,parse_tag_mem32);
【parse_tag_mem32--->
arm_add_memory】
从ATAG_MEM参数中获取内存信息,初始化内存块管理结构
int__initarm_add_memory(phys_addr_tstart,unsignedlongsize)
structmembank*bank=&
meminfo.bank[meminfo.nr_banks];
if(meminfo.nr_banks>
=NR_BANKS){
printk(KERN_CRIT"
NR_BANKStoolow,"
"
ignoringmemoryat0x%08llx\n"
(longlong)start);
return-EINVAL;
size-=start&
~PAGE_MASK;
bank->
start=PAGE_ALIGN(start);
#ifndefCONFIG_LPAE
if(bank->
start+size<
bank->
start){
size=ULONG_MAX-bank->
start;
#endif
size=size&
PAGE_MASK;
size==0)
meminfo.nr_banks++;
return0;
sanity_check_meminfo】
arch/arm/mm/mmu.c
void__initsanity_check_meminfo(void)
inti,j,highmem=0;
遍历每一个内存块
for(i=0,j=0;
i<
meminfo.nr_banks;
i++){
meminfo.bank[j];
*bank=meminfo.bank[i];
start>
ULONG_MAX)
highmem=1;
#ifdefCONFIG_HIGHMEM
vmalloc_min在文件arch/arm/mm/mmu.c中定义,它定义了高端内存的起始位置。
PAGE_OFFSET是物理位置的起始处。
如果内存块起始位置大于vmalloc_min,表示存在高端内存。
如果内存扩展超过32位,它就有可能小于PAGE_OFFSET。
if(__va(bank->
start)>
=vmalloc_min||
__va(bank->
start)<
(void*)PAGE_OFFSET)
标志该内存块是否处于高端内存中
highmem=highmem;
如果该内存块部分处于高端内存中,部分处于低端内存中就将其分为两个内存块。
highmem&
&
__va(bank->
vmalloc_min&
size>
vmalloc_min-__va(bank->
start)){
}else{
memmove(bank+1,bank,
(meminfo.nr_banks-i)*sizeof(*bank));
i++;
bank[1].size-=vmalloc_min-__va(bank->
start);
bank[1].start=__pa(vmalloc_min-1)+1;
bank[1].highmem=highmem=1;
j++;
size=vmalloc_min-__va(bank->
#else如果不支持高端内存做如下处理
if(highmem){
continue;
(void*)PAGE_OFFSET){
start+bank->
size)>
vmalloc_min||
size)<
unsignedlongnewsize=vmalloc_min-__va(bank->
size=newsize;
求出低端内存的最大地址值
arm_lowmem_limit)
arm_lowmem_limit=bank->
size;
meminfo.nr_banks=j;
记录内存块数
计算高端内存起始地址,该值不一定等于vmalloc_min,因为可能没有高端内存
high_memory=__va(arm_lowmem_limit-1)+1;
memblock_set_current_limit(arm_lowmem_limit);
arm_memblock_init】
arch/arm/mm/init.c
void__initarm_memblock_init(structmeminfo*mi,structmachine_desc*mdesc)
inti;
将所有内存模块添加到memblock.memory中。
结构体memblock在文件mm/memblock.c中定义,如下:
structmemblockmemblock__initdata_memblock={
.memory.regions=memblock_memory_init_regions,
.reserved.regions=memblock_reserved_init_regions,
};
for(i=0;
mi->
nr_banks;
i++)
memblock_add(mi->
bank[i].start,mi->
bank[i].size);
如果内核在rom中运行就只将它的数据段开始的空间添加到memblock.reserved中,否则将内核代码段开始的空间添加到memblock.reserved中。
#ifdefCONFIG_XIP_KERNEL
memblock_reserve(__pa(_sdata),_end-_sdata);
#else
memblock_reserve(__pa(_stext),_end-_stext);
#ifdefCONFIG_BLK_DEV_INITRD
如果支持initrd启动,此时它还不在内存中
if(phys_initrd_size&
!
memblock_is_region_memory(phys_initrd_start,phys_initrd_size)){
pr_err("
INITRD:
0x%08lx+0x%08lxisnotamemoryregion-disablinginitrd\n"
phys_initrd_start,phys_initrd_size);
phys_initrd_start=phys_initrd_size=0;
memblock_is_region_reserved(phys_initrd_start,phys_initrd_size)){
0x%08lx+0x%08lxoverlapsin-usememoryregion-disablinginitrd\n"
为inird镜像预留一块存储区
if(phys_initrd_size){
memblock_reserve(phys_initrd_start,phys_initrd_size);
initrd_start=__phys_to_virt(phys_initrd_start);
initrd_end=initrd_start+phys_initrd_size;
为内核页表分配存储空间
arm_mm_memblock_reserve();
paging_init】
void__initpaging_init(structmachine_desc*mdesc)
void*zero_page;
根据不同的arm版本初始化不同的mem_types,该结构存放着页表的一些属性相关信息
build_mem_type_table();
将除了内核镜像、主内存所在虚拟地址之外全部内存的页表清除掉
prepare_page_table();
为低端内存的所有区域创建内核页表
map_lowmem();
对DMA区域重新创建页表
dma_contiguous_remap();
为设备IO空间和中断向量表创建页表,并刷新TLB和缓存
devicemaps_init(mdesc);
获取持久映射区页表的位置,存储在pkmap_page_table中
kmap_init();
高64K是用于存放中断向量表的
top_pmd=pmd_off_k(0xffff0000);
分配一个0页,该页用于写时复制机制。
zero_page=early_alloc(PAGE_SIZE);
初始化自举内存分配,后面有专门章节讲解
bootmem_init();
empty_zero_page=virt_to_page(zero_page);
刷新数据缓存
__flush_dcache_page(NULL,empty_zero_page);
paging_init--->
prepare_page_table】
staticinlinevoidprepare_page_table(void)
unsignedlongaddr;
phys_addr_tend;
模块加载的范围应该是在MODULES_VADDR到MODULES_END之间,MODULES_VADDR在文件arch/arm/include/asm/memory.h中定义,如下:
#defineMODULES_VADDR(PAGE_OFFSET-8*1024*1024)
对于arm处理器,该区域在正常内核虚拟地址之下。
清除存储空间在MODULES_VADDR之下的页表项。
for(addr=0;
addr<
MODULES_VADDR;
addr+=PMD_SIZE)
pmd_clear(pmd_off_k(addr));
addr=((unsignedlong)_etext+PMD_SIZE-1)&
PMD_MASK;
for(;
PAGE_OFFSET;
第一个存储区存放的是内核镜像,跳过该区域,即不清除这个区域的页表
end=memblock.memory.regions[0].base+memblock.memory.regions[0].size;
if(end>
=arm_lowmem_limit)
end=arm_lowmem_
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- linux 内存 管理 分析