linux内存管理分析2.docx

上传人:b****8 文档编号:9944854 上传时间:2023-02-07 格式:DOCX 页数:26 大小:23.79KB
下载 相关 举报
linux内存管理分析2.docx_第1页
第1页 / 共26页
linux内存管理分析2.docx_第2页
第2页 / 共26页
linux内存管理分析2.docx_第3页
第3页 / 共26页
linux内存管理分析2.docx_第4页
第4页 / 共26页
linux内存管理分析2.docx_第5页
第5页 / 共26页
点击查看更多>>
下载资源
资源描述

linux内存管理分析2.docx

《linux内存管理分析2.docx》由会员分享,可在线阅读,更多相关《linux内存管理分析2.docx(26页珍藏版)》请在冰豆网上搜索。

linux内存管理分析2.docx

linux内存管理分析2

linux内存管理分析【二】

2012-11-1921:

09:

22|分类:

默认分类|标签:

|字号大中小订阅

2内存管理系统建立过程

为建立内存管理系统,在内核初始化过程中调用了下面几个函数:

init/main.c

asmlinkagevoid__initstart_kernel(void)

{

......

初始化持久映射与临时映射的一些信息,后面持久映射和临时映射一节将详细讲解

page_address_init();

setup_arch是特定于体系架构的函数,负责初始化自举分配器和内核页表等。

setup_arch(&command_line);

......

初始化per_cpu机制的一些结构,将.data.percpu段中的数据拷贝到每个CPU的数据段中

setup_per_cpu_areas();

......

建立结点和内存域之间的关系

build_all_zonelists(NULL);

......

停用自举分配器bootmem,迁移到实际的内存管理中,初始化slab分配器,初始化进程虚拟地址空间管理结构

mm_init();

......

为每一个内存区域分配per_cpu_pageset结构并初始化其成员

setup_per_cpu_pageset();

......

}

【start_kernel--->setup_arch】

arch/arm/kernel/setup.c

void__initsetup_arch(char**cmdline_p)

{

structmachine_desc*mdesc;

内核参数可以通过平坦设备树或者tags由bootloader传递给内核。

每一个机器平台都由一个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();

......

}

【start_kernel--->setup_arch--->setup_machine_tags】

arch/arm/kernel/setup.c

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*)&init_tags;内核提供的一个默认参数列表

}

函数mdesc->fixup中一般会获取内存块的信息

if(mdesc->fixup)

mdesc->fixup(tags,&from,&meminfo);

if(tags->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;

}

【start_kernel--->setup_arch--->setup_machine_tags--->parse_tags】

arch/arm/kernel/setup.c

staticvoid__initparse_tags(conststructtag*t)

{

遍历参数列表中每一个参数结构

for(;t->hdr.size;t=tag_next(t))

if(!

parse_tag(t))

......

}

【start_kernel--->setup_arch--->setup_machine_tags--->parse_tags--->parse_tag】

arch/arm/kernel/setup.c

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);

break;

}

returnt<&__tagtable_end;

}

参数ATAG_MEM的解析函数定义如下:

arch/arm/kernel/setup.c

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+sizestart){

size=ULONG_MAX-bank->start;

}

#endif

bank->size=size&PAGE_MASK;

if(bank->size==0)

return-EINVAL;

meminfo.nr_banks++;

return0;

}

【start_kernel--->setup_arch--->sanity_check_meminfo】

arch/arm/mm/mmu.c

void__initsanity_check_meminfo(void)

{

inti,j,highmem=0;

遍历每一个内存块

for(i=0,j=0;i

structmembank*bank=&meminfo.bank[j];

*bank=meminfo.bank[i];

if(bank->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=1;

标志该内存块是否处于高端内存中

bank->highmem=highmem;

如果该内存块部分处于高端内存中,部分处于低端内存中就将其分为两个内存块。

if(!

highmem&&__va(bank->start)

bank->size>vmalloc_min-__va(bank->start)){

if(meminfo.nr_banks>=NR_BANKS){

......

}else{

memmove(bank+1,bank,

(meminfo.nr_banks-i)*sizeof(*bank));

meminfo.nr_banks++;

i++;

bank[1].size-=vmalloc_min-__va(bank->start);

bank[1].start=__pa(vmalloc_min-1)+1;

bank[1].highmem=highmem=1;

j++;

}

bank->size=vmalloc_min-__va(bank->start);

}

#else如果不支持高端内存做如下处理

bank->highmem=highmem;

if(highmem){

......

continue;

}

if(__va(bank->start)>=vmalloc_min||

__va(bank->start)<(void*)PAGE_OFFSET){

......

continue;

}

if(__va(bank->start+bank->size)>vmalloc_min||

__va(bank->start+bank->size)<__va(bank->start)){

unsignedlongnewsize=vmalloc_min-__va(bank->start);

......

bank->size=newsize;

}

#endif

求出低端内存的最大地址值

if(!

bank->highmem&&bank->start+bank->size>arm_lowmem_limit)

arm_lowmem_limit=bank->start+bank->size;

j++;

}

......

meminfo.nr_banks=j;记录内存块数

计算高端内存起始地址,该值不一定等于vmalloc_min,因为可能没有高端内存

high_memory=__va(arm_lowmem_limit-1)+1;

memblock_set_current_limit(arm_lowmem_limit);

}

【start_kernel--->setup_arch--->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;inr_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);

#endif

#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;

}

if(phys_initrd_size&&

memblock_is_region_reserved(phys_initrd_start,phys_initrd_size)){

pr_err("INITRD:

0x%08lx+0x%08lxoverlapsin-usememoryregion-disablinginitrd\n",

phys_initrd_start,phys_initrd_size);

phys_initrd_start=phys_initrd_size=0;

}

为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;

}

#endif

为内核页表分配存储空间

arm_mm_memblock_reserve();

......

}

【start_kernel--->setup_arch--->paging_init】

arch/arm/mm/mmu.c

void__initpaging_init(structmachine_desc*mdesc)

{

void*zero_page;

memblock_set_current_limit(arm_lowmem_limit);

根据不同的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);

}

【start_kernel--->setup_arch--->paging_init--->prepare_page_table】

arch/arm/mm/mmu.c

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

pmd_clear(pmd_off_k(addr));

#ifdefCONFIG_XIP_KERNEL

addr=((unsignedlong)_etext+PMD_SIZE-1)&PMD_MASK;

#endif

for(;addr

pmd_clear(pmd_off_k(addr));

第一个存储区存放的是内核镜像,跳过该区域,即不清除这个区域的页表

end=memblock.memory.regions[0].base+memblock.memory.regions[0].size;

if(end>=arm_lowmem_limit)

end=arm_lowmem_

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高等教育 > 研究生入学考试

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1