linux内存管理.docx
- 文档编号:10250925
- 上传时间:2023-02-09
- 格式:DOCX
- 页数:56
- 大小:61.49KB
linux内存管理.docx
《linux内存管理.docx》由会员分享,可在线阅读,更多相关《linux内存管理.docx(56页珍藏版)》请在冰豆网上搜索。
linux内存管理
linux内存管理
1.内存管理相关结构:
1.1内存结点:
计算机系统按物理内存的管理方式可分为2类:
UMA计算机(一致内存访问):
将所有内存组织在1个连续空间中,smp系统中的每个cpu访问各内存区都是同样地快。
NUMA计算机(非一致内存访问):
每个处理器都有本地内存,可快速访问。
各处理器通过总线连接起来,以支持对其它处理器的访问,访问速度比访问本地内存慢些。
这2种系统模型可混用,比如在UMA系统中,若内存不连续则使用NUMA模型管理会更有帮助。
这2种模型在算法上没有什么差别,在UMA系统上,只使用1个NUMA节点来管理内存,内存管理的其它部分认为它在处理1个伪NUMA系统。
是否可以认为,UMA系统是NUMA系统的1个特例,UMA系统是只有1个节点的NUMA系统?
?
?
?
内存结点管理结构如下:
注意,2.6.34版本的与以下不同。
/*include/linux/mmzone.h*/
typedefstructpglist_data{
structzonenode_zones[MAX_NR_ZONES];/*结点中包含的所有内存域*/
structzonelistnode_zonelists[MAX_ZONELISTS];/*
*备用结点内存域列表,在当前结点没有内存分配时可到备用节点中分配
*/
intnr_zones;/*节点中内存域的数目*/
#ifdefCONFIG_FLAT_NODE_MEM_MAP/*means!
SPARSEMEM*/
structpage*node_mem_map;/*
*指向页结构的数组,该数组包含了节点中所有域的页
*/
#ifdefCONFIG_CGROUP_MEM_RES_CTLR
structpage_cgroup*node_page_cgroup;
#endif
#endif
#ifndefCONFIG_NO_BOOTMEM
structbootmem_data*bdata;/*
*系统启动时会启用一套临时内存管理机制
*structbootmem_data就是该机制的管理结构体
*/
#endif
#ifdefCONFIG_MEMORY_HOTPLUG
/*
*Mustbeheldanytimeyouexpectnode_start_pfn,node_present_pages
*ornode_spanned_pagesstayconstant.Holdingthiswillalso
*guaranteethatanypfn_valid()staysthatway.
*Nestsabovezone->lockandzone->size_seqlock.
*/
spinlock_tnode_size_lock;
#endif
unsignedlongnode_start_pfn;/*
*node_start_pfn表示节点中的第1个页帧编号。
页帧的编号是全局统一的
*在UMA系统中只有1个节点所以node_start_pfn为0
*/
unsignedlongnode_present_pages;/*结点中页帧的总数*/
unsignedlongnode_spanned_pages;/*结点中包括空洞在内的页帧总数*/
intnode_id;/*结点的编号*/
wait_queue_head_tkswapd_wait;/*交换进程的等待队列*/
structtask_struct*kswapd;/*
*指向负责该结点的交换进程的task_struct结构
*/
intkswapd_max_order;/*由页交换子系统使用,定义要释放的区域大小*/
enumzone_typeclasszone_idx;
}pg_data_t;
1.2内存域:
系统所支持的所有内存域类型定义在zone_type结构体变量中。
/*include/linux/mmzone.h*/
enumzone_type{
#ifdefCONFIG_ZONE_DMA
ZONE_DMA,/*支持DMA操作的内存域*/
#endif
#ifdefCONFIG_ZONE_DMA32
ZONE_DMA32,/*
*支持DMA操作的,按32位寻址的内存域
*只有在64位系统中ZONE_DMA才与ZONE_DMA有差别
*/
#endif
ZONE_NORMAL,/*普通内存域*/
#ifdefCONFIG_HIGHMEM
ZONE_HIGHMEM,/*高端内存域*/
#endif
ZONE_MOVABLE,/*伪内存域,防止物理内存碎片机制中要使用该内存域*/
__MAX_NR_ZONES/*钳位,表示所有的内存域总数*/
};
structzone结构体变量用来管理内存域。
注意,zone结构体在2.6.34版本内核中有变。
/*include/linux/mmzone.h*/
structzone{
unsignedlongwatermark[NR_WMARK];/*
*数组watermark管理该内存域的3个水线值:
*WMARK_HIGH:
空闲页多余该值表示内存域的状态是理想的
*WMARK_LOW:
空闲页低于该值,内核开始将页换出到磁盘
*WMARK_MIN:
空闲页低于该值,急需空闲页,内存紧张
*/
unsignedlongpercpu_drift_mark;/*
*当空闲页低于该值,采取一些方法计算空闲页的总数
*避免内存漂移导致水线值失效
*/
unsignedlonglowmem_reserve[MAX_NR_ZONES];/*
*保留一些空闲页,用于一些无论如何都不会失败的关键性分配
*/
unsignedlongdirty_balance_reserve;
#ifdefCONFIG_NUMA
intnode;/*所属的NUMA节点*/
unsignedlongmin_unmapped_pages;/*
*当可回收的页超过此值时,将进行页面回收
*/
unsignedlongmin_slab_pages;/*
*当内存管理区中,用于slab的可回收页大于此值时
*将回收slab中的缓存页
*/
#endif
structper_cpu_pageset__percpu*pageset;/*
*每cpu的页面缓存,当分配单个页面时
*首先从该缓存中分配页面,可提高效率
*/
spinlock_tlock;/*
*该锁用于保护伙伴系统数据结构。
即保护free_area相关数据
*/
intall_unreclaimable;/*当管理区中的所有页面都不可回收时设置此标志*/
......
structfree_areafree_area[MAX_ORDER];/*
*用于实现伙伴系统。
这个数组定义了11个队列
*每个队列中的元素都是大小为2^n的页面块
*/
......
#ifdefCONFIG_COMPACTION
/*下面字段用于内存紧缩*/
unsignedintcompact_considered;
unsignedintcompact_defer_shift;
intcompact_order_failed;
#endif
ZONE_PADDING(_pad1_)/*确保后面的字段是缓存行对齐的*/
/*下面几个字段都跟页面回收相关*/
spinlock_tlru_lock;
structlruveclruvec;
unsignedlongpages_scanned;/*sincelastreclaim*/
unsignedlongflags;/*zoneflags,seebelow*/
/*该内存区域中的各种统计信息*/
atomic_long_tvm_stat[NR_VM_ZONE_STAT_ITEMS];
unsignedintinactive_ratio;
ZONE_PADDING(_pad2_)/*确保后面的字段是按缓存行对齐的*/
/*等待队列,用于等待该区域内存的进程*/
wait_queue_head_t*wait_table;
unsignedlongwait_table_hash_nr_entries;
unsignedlongwait_table_bits;
structpglist_data*zone_pgdat;/*指向该区域所属的结点*/
unsignedlongzone_start_pfn;/*该区域的起始页帧号*/
unsignedlongspanned_pages;/*该区中包含空洞在内,总的页帧数*/
unsignedlongpresent_pages;/*总的页帧数,不包含空洞*/
constchar*name;/*该区域的名字,如Normal、DMA、Highmem等*/
}____cacheline_internodealigned_in_smp;
1.3页面:
page结构用来记录物理页的状态,系统中的每个物理页都对应1个page结构体变量。
/*include/linux/mm_types.h*/
structpage{
unsignedlongflags;/*
*页面标志,也包含了一些其它信息,比如页面所处的管理区编号等
*/
structaddress_space*mapping;/*
*若最低位为0,则指向inode的address_space,否则为NULL
*若页映射为匿名内存,最低位置位,且该指针指向anon_vma对象
*/
......
struct{
union{
atomic_t_mapcount;/*表示在页表中有多少项指向该页*/
struct{/*slab相关*/
unsignedinuse:
16;
unsignedobjects:
15;
unsignedfrozen:
1;
};
};
atomic_t_count;/*页的使用计数*/
};
};
};
......
union{
unsignedlongprivate;/*
*指向私有数据的指针,虚拟内存管理会忽略该数据。
*根据页的用途,可按不同方式使用该指针
*/
......
structkmem_cache*slab;/*用于SLUB分配器,指向slab的指针*/
structpage*first_page;/*用于复合页,指向首页*/
};
void*virtual;/*
*用于高端内存区中的页,即无法直接映射到内核内存中的页
*virtual用于存储该页的虚拟地址
*/
......
}
2.内存管理系统的建立过程:
为建立内存管理系统,在系统初始化过程中调用了下面几个函数:
/*init/main.c*/
asmlinkagevoid__initstart_kernel(void)
{
......
page_address_init();/*初始化持久映射与临时映射的一些信息*/
setup_arch(&command_line);/*
*setup_arch是特定于体系架构的函数
*负责初始化系统启动时的内存分配器boot_mem和内核页表等
*/
......
setup_per_cpu_areas();/*
*初始化per_cpu机制的一些结构
*将进程的.data.percpu段中数据拷贝到每个cpu的数据段中
*/
......
build_all_zonelists(NULL);/*建立结点和内存域之间的关系*/
......
mm_init();/*
*停用系统启动时所使用的内存分配器boot_mem,启动实际的内存管理器
*初始化slab分配器,初始化进程虚拟地址空间管理结构
*/
......
setup_per_cpu_pageset();/*
*为每个内存区分配per_cpu_pageset结构并初始化其成员
*/
......
}
setup_arch()函数:
会在start_kernel()函数中,调用setup_arch()函数。
setup_arch()方法是体系结构相关的,以下为arm架构的setup_arch()函数。
/*arch/arm/kernel/setup.c*/
void__initsetup_arch(char**cmdline_p)
{
structmachine_desc*mdesc;
setup_processor();/*在2.6.34版本内核中有这1行*/
/*
*内核参数可通过平坦设备树或tags由bootloader传给内核
*每种系统平台都由1个machine_desc结构体来描述。
*内核所支持的所有平台的machine_desc结构都包含在.init.arch.info段的
*__arch_info_begin到__tagtable_end之间。
每种平台都有其唯一的机器码
*machine_arch_type,可通过机器码在.init.arch.info段中找到对应平台的
*描述结构
*/
mdesc=setup_machine_fdt(__atags_pointer);/*
*根据机器码找到对应平台的machine_desc结构体变量
*并分析内核参数中的内存相关信息,用以初始化内存块
*管理结构membank
*/
if(!
mdesc)
mdesc=setup_machine_tags(machine_arch_type);/*
*setup_machine_tags()用来处理传入的命令行参数
*注意,setup_machine_tags()函数是1个体系结构相关函数
*且只在arm架构的setup_arch()中会调用该函数
*/
machine_desc=mdesc;
machine_name=mdesc->name;
setup_dma_zone(mdesc);/*
*根据mdesc->dma_zone_size设置dma区域的大小arm_dma_zone_size
*和dma区域的结束地址arm_dma_limit
*对于arm架构来说!
*/
/*在2.6.34版本内核中有如下2行*/
if(mdesc->restart_mode)
reboot_setup(&mdesc->restart_mode);
/*
*与用户进程类似,内核也有1个名为init_mm的mm_strcut结构体变量来描述
*内核内存空间,linux使用mm_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);
sanity_check_meminfo();/*
*扫描各内存块,检测低端内存的最大值arm_lowmem_limit
*设置高端内存起始值的虚拟地址high_memory
*/
arm_memblock_init(&meminfo,mdesc);/*
*将所有内存块添加到结构memblock的memory区中
*将已使用的内存添加到reserved区中去
*/
paging_init(mdesc);/*创建内核页表,初始化自举分配器*/
request_standard_resources(mdesc);/*
*内核中将许多物理资源用structresource结构来管理
*本函数会将IO内存作为resource注册到内核
*/
......
reserve_crashkernel();/*
*如果内核命令行中显式声明了用于内核crash的转存空间
*则将这些存储空间标记为已分配
*/
......
}
setup_machine_tags()函数:
setup_machine_tags()用来处理传入的命令行参数。
setup_machine_tags()函数是体系结构相关的,且只在arm架构的setup_arch()中会调用setup_machine_tags()函数。
setup_machine_tags()函数的调用次序为:
start_kernel()
——>setup_arch()
——>setup_machine_tags()
/*arch/arm/kernel/setup.c*/
staticstructmachine_desc*__initsetup_machine_tags(unsignedintnr)
{
/*内核参数由structtag来管理,其中首个tag类型必然是ATAG_CORE*/
structtag*tags=(structtag*)&init_tags;
structmachine_desc*mdesc=NULL,*p;
char*from=default_command_line;
init_tags.mem.start=PHYS_OFFSET;
/*在1个循环中根据机器码在.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;/*
*内核提供的1个默认参数列表
*在2.6.34版本内核中,该句为tags=(structtag*)&default_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);
save_atags(tags);/*将参数列表复制到静态数组atags_copy中*/
parse_tags(tags);/*分析内核参数*/
}
strlcpy(boot_command_line,from,COMMAND_LINE_SIZE);/*
*将解析出来的内核命令行信息拷贝到静态数组boot_command_line中。
*在内核启动期间用了很多静态存储空间,它们前面缀有__initdata,
*像这样的空间在内核启动起来后将被释放
*/
returnmdesc;
}
parse_tags()函数:
parse_tags()用来分析内核参数。
据我分析,只有在arm架构中才需要parse_tags()函数。
parse_tags()函数的调用次序为:
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))
{
....../*
*在2.6.34版本内核中为:
*printk(KERN_WARNING"Ignoringunrecognisedtag0x%08x\n",
*t->hdr.tag);
*/
}
}
parse_tag()函数:
在parse_tag()函数中,会遍历.init.tagtable段的__tagtable_begin和__tagtable_end之间的所有传入的内核参数,并调用相应的解析函数。
注意,parse_tag()函数是体系结构相关的
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- linux 内存 管理
![提示](https://static.bdocx.com/images/bang_tan.gif)