内存管理区struct page.docx

上传人:b****1 文档编号:23213164 上传时间:2023-05-15 格式:DOCX 页数:24 大小:20.90KB
下载 相关 举报
内存管理区struct page.docx_第1页
第1页 / 共24页
内存管理区struct page.docx_第2页
第2页 / 共24页
内存管理区struct page.docx_第3页
第3页 / 共24页
内存管理区struct page.docx_第4页
第4页 / 共24页
内存管理区struct page.docx_第5页
第5页 / 共24页
点击查看更多>>
下载资源
资源描述

内存管理区struct page.docx

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

内存管理区struct page.docx

内存管理区structpage

内存管理区(structpage)

前面已经提到,物理内存被划分为三个区来管理,它们是ZONE_DMA、ZONE_NORMAL和ZONE_HIGHMEM。

每个区都用structzone_struct结构来表示,定义于include/linux/mmzone.h:

typedefstructzone_struct{

/*

*Commonly

accessedfields:

*/

spinlock_t

lock;

unsigned

longfree_pages;

unsigned

longpages_min,

pages_low,pages_high;

int

need_balance;

/*

*freeareasofdifferentsizes

*/

free_area_tfree_area[MAX_ORDER];

/*

*Discontig

memorysupportfields.

*/

struct

pglist_data*zone_pgdat;

struct

page*zone_mem_map;

unsigned

long

zone_start_paddr;

unsigned

long

zone_start_mapnr;

/*

*rarelyusedfields:

*/

char

*name;

unsigned

long

size;

}zone_t;

#define

ZONE_DMA

0

#define

ZONE_NORMAL

1

#define

ZONE_HIGHMEM

2

#define

MAX_NR_ZONES

3

对structzone_struct结构中每个域的描述如下:

lock:

用来保证对该结构中其它域的串行访问

free_pages:

在这个区中现有空闲页的个数

pages_min、pages_low及pages_high是对这个区最少、此少及最多页面个数的描述

need_balance:

与kswapd合在一起使用

free_area:

在伙伴分配系统中的位图数组和页面链表

zone_pgdat:

本管理区所在的存储节点

zone_mem_map:

该管理区的内存映射表

zone_start_paddr:

该管理区的起始物理地址

zone_start_mapnr:

在mem_map中的索引(或下标)

name:

该管理区的名字

size:

该管理区物理内存总的大小

其中,free_area_t定义为:

#difine

MAX_ORDER

10

type

structfree_area_struct{

structlist_headfree_list

unsignedint*map

}free_area_t

因此,zone---_struct结构中的free_area[MAX_ORDER]是一组“空闲区间”链表。

为什么要定义一组而不是一个空闲队列呢?

这是因为常常需要成块地在物理空间分配连续的多个页面,所以要按块的大小分别加以管理。

因此,在管理区数据结构中既要有一个队列来保持一些离散(连续长度为1)的物理页面,还要有一个队列来保持一些连续长度为2的页面块以及连续长度为4、8、16、…、直至2MAX_ORDER(即4M字节)的队列。

如前所述,内存中每个物理页面都有一个structpage结构,位于include/linux/mm.h,该结构包含了对物理页面进行管理的所有信息,下面给出具体描述:

typedefstructpage{

struct

list_headlist;

struct

address_space*mapping;

unsigned

longindex;

struct

page*next_hash;

atomic_t

count;

unsigned

longflags;

struct

list_headlru;

wait_queue_head_t

wait;

struct

page**pprev_hash;

struct

buffer_head*buffers;

void

*virtual;

struct

zone_struct*zone;

}

mem_map_t;

对每个域的描述如下:

list:

指向链表中的下一页

mapping:

用来指定我们正在映射的索引节点(inode)

index:

在映射表中的偏移

next_hash:

指向页高速缓存哈希表中下一个共享的页

count:

引用这个页的个数

flags:

页面各种不同的属性

lru:

用在active_list中

wait:

等待这一页的页队列

pprev_hash:

与next_hash相对应

buffers:

把缓冲区映射到一个磁盘块

zone:

页所在的内存管理区

与内存管理区相关的三个主要函数为:

·

free_area_init()函数

·

build_zonelists()函数

·

mem_init()函数

1.free_area_init()函数

这个函数用来初始化内存管理区并创建内存映射表,定义于mm/page_alloc.c中。

函数原型为:

voidfree_area_init(unsignedlong*zones_size);

voidfree_area_init_core(intnid,pg_data_t*pgdat,

struct

page**gmap,

unsignedlong*zones_size,

unsignedlongzone_start_paddr,

unsignedlong*zholes_size,

structpage*lmem_map);

free_area_init()为封装函数,而free_area_init_core()为真正实现的函数,对该函数详细描述如下:

structpage*p;

unsignedlongi,j;

unsignedlongmap_size;

unsignedlongtotalpages,offset,realtotalpages;

constunsignedlongzone_required_alignment=1UL<<

(MAX_ORDER-1);

if(zone_start_paddr&~PAGE_MASK)

BUG();

检查该管理区的起始地址是否是一个页的边界。

totalpages=0;

for(i=0;i<MAX_NR_ZONES;i++){

totalpages+=size;

}

计算本存储节点中页面的个数。

realtotalpages=totalpages;

if(zholes_size)

for(i=0;i<MAX_NR_ZONES;i++)

realtotalpages-=zholes_size[i];

printk("Onnode%dtotalpages:

%lu\n",nid,

realtotalpages);

打印除空洞以外的实际页面数。

INIT_LIST_HEAD(&active_list);

INIT_LIST_HEAD(&inactive_list);

初始化循环链表。

/*

*Somearchitectures(withlotsofmemanddiscontinousmemory

*maps)

havetosearchforagoodmem_maparea:

*For

discontigmem,theconceptualmemmaparraystartsfrom

*

PAGE_OFFSET,weneedtoaligntheactualarrayontoamemmap

*boundary,sothatMAP_NRworks.

*/

map_size=

(totalpages+1)*sizeof(structpage);

if(lmem_map==(structpage*)0){

lmem_map

=(structpage*)(PAGE_OFFSET+

MAP_ALIGN((unsignedlong)lmem_map-PAGE_OFFSET));

}

给局部内存(即本节点中的内存)映射分配空间,并在sizeof(mem_map_t)边界上对齐它。

*gmap=

pgdat->node_mem_map=lmem_map;

pgdat->node_size=totalpages;

pgdat->node_start_paddr=zone_start_paddr;

pgdat->node_start_mapnr=(lmem_map-mem_map);

pgdat->nr_zones=0;

初始化本节点中的域。

/*

*Initially

allpagesarereserved-freeonesarefreed

*upby

free_all_bootmem()oncetheearlybootprocessis

*done.

*/

for(p=lmem_map;p<lmem_map+totalpages;p++){

SetPageReserved(p);

init_waitqueue_head(&p->wait);

memlist_init(&p->list);

}

仔细检查所有的页,并进行如下操作:

·

把页的使用计数(count域)置为0。

·

把页标记为保留。

·

初始化该页的等待队列。

·

初始化链表指针。

offset=lmem_map-mem_map;

变量mem_map是类型为structpages的全局稀疏矩阵。

mem_map下标的起始值取决于第一个节点的第一个管理区。

如果第一个管理区的起始地址为0,则下标就从0开始,并且与物理页面号相对应,也就是说,页面号就是mem_map的下标。

每一个管理区都有自己的映射表,存放在zone_mem_map中,每个管理区又被映射到它所在的节点node_mem_map中,而每个节点又被映射到管理全局内存的mem_map中。

在上面的这行代码中,offset表示该节点放的内存映射表在全局mem_map中的入口点(下标)。

在这里,offset为0,因为在i386上,只有一个节点。

for(j=0;j<MAX_NR_ZONES;j++){

这个循环对zone的域进行初始化。

zone_t*zone=

pgdat->node_zones+j;

unsigned

longmask;

unsigned

longsize,realsize;

realsize

=size=zones_size[j];

管理区的实际数据是存放在节点中的,因此,让指针指向正确的管理区,并获得该管理区的大小。

if

(zholes_size)

realsize-=zholes_size[j];

printk("zone(%lu):

%lupages.\n",j,size);

计算各个区的实际大小,并进行打印。

例如,在具有256MB的内存上,上面的输出为:

zone(0):

4096pages.

zone

(1):

61440pages.

zone

(2):

0pages.

这里,管理区2为0,因为只有256MB的RAM.

zone->size

=size;

zone->name

=zone_names[j];

zone->lock

=SPIN_LOCK_UNLOCKED;

zone->zone_pgdat

=pgdat;

zone->free_pages

=0;

zone->need_balance

=0;

初始化管理区中的各个域。

if

(!

size)

continue;

如果一个管理区的大小为0

pgdat->nr_zones

=j+1;

mask

=(realsize/zone_balance_ratio[j]);

if

(mask<zone_balance_min[j])

mask=zone_balance_min[j];

else

if(mask>zone_balance_max[j])

mask=zone_balance_max[j];

计算合适的平衡比率。

zone->pages_min

=mask;

zone->pages_low

=mask*2;

zone->pages_high

=mask*3;

zone->zone_mem_map

=mem_map+offset;

zone->zone_start_mapnr

=offset;

zone->zone_start_paddr

=zone_start_paddr;

设置该管理区中页面数量的几个界限,并把在全局变量mem_map中的入口点作为zone_mem_map的初值。

用全局变量mem_map的下标初始化变量zone_start_mapnr。

if

((zone_start_paddr>>PAGE_SHIFT)&

(zone_required_alignment-1))

printk("BUG:

wrongzonealignment,itwill

crash\n");

for

(i=0;i<size;i++){

structpage*page=mem_map+offset+i;

page->zone=zone;

if(j!

=ZONE_HIGHMEM)

page->virtual=__va(zone_start_paddr);

zone_start_paddr+=PAGE_SIZE;

}

对该管理区中的每一页进行处理。

首先,把structpage结构中的zone域初始化为指向该管理区(zone),如果这个管理区不是ZONE_HIGHMEM,则设置这一页的虚地址(即物理地址+PAGE_OFFSET)。

也就是说,建立起每一页物理地址到虚地址的映射。

offset

+=size;

把offset增加size,使它指向mem_map中下一个管理区的起始位置。

for

(i=0;;i++){

unsignedlongbitmap_size;

memlist_init(&zone->free_area[i].free_list);

if(i==MAX_ORDER-1){

zone->free_area[i].map=NULL;

break;

}

初始化free_area[]链表,把free_area[]中最后一个序号的位图置为NULL。

/*

*Page

buddysystemuses"index>>(i+1)",

*where"index"isatmost"size-1".

*

*Theextra

"+3"istorounddowntobyte

*size(8

bitsperbyteassumption).Thus

*weget"(size-1)>>(i+4)"asthelastbyte

*wecanaccess.

*

*The

"+1"isbecausewewanttoroundthe

*byteallocationupratherthandown.So

*weshouldhavehada"+7"beforeweshifted

*downbythree.Also,wehavetoaddoneas

*weactually_use_thelastbit(it's[0,n]

*inclusive,not[0,n[).

*

*Sowe

actuallyhad+7+1beforeweshift

*downby3.But(n+8)>>3==(n>>3)+1

*(modulooverflows,whichwedonothave).

*

*Finally,

weLONG_ALIGNbecauseallbitmap

*operationsareonlongs.

*/

bitmap_size=(size-1)

>>(i+4);

bitmap_size

=LONG_ALIGN(bitmap_size+1);

zone->free_area[i].map=(unsignedlong*)

alloc_bootmem_node(pgdat,bitmap_size);

}

计算位图的大小,然后调用alloc_bootmem_node给位图分配空间。

}

build_zonelists(pgdat);

在节点中为不同的管理区创建链表。

2.build_zonelists()函数

函数原型:

staticinlinevoidbuild_zonelists(pg_data_t

*pgdat)

代码如下:

inti,j,

k;

for(i=

0;i<=GFP_ZONEMASK;i++){

zonelist_t*zonelist;

zone_t*zone;

zonelist

=pgdat->node_zonelists+i;

memset(zonelist,

0,sizeof(*zonelist));

获得节点中指向管理区链表的域,并把它初始化为空。

j=0;

k=ZONE_NORMAL;

if

(i&__GFP_HIGHMEM)

k=

ZONE_HIGHMEM;

if

(i&__GFP_DMA)

k=

ZONE_DMA;

把当前管理区掩码与三个可用管理区掩码相“与”,获得一个管理区标识,把它用在下面的switch语句中。

switch

(k){

default:

BUG();

/*

*fallthrough:

*/

caseZONE_HIGHMEM:

zone=pgdat->node_zones+ZONE_HIGHMEM;

if(zone->size){

#ifndefCONFIG_HIGHMEM

BUG();

#endif

zonelist->zones[j++]=zone;

}

caseZONE_NORMAL:

zone=pgdat->node_zones+ZONE_NORMAL;

if(zone->size)

zonelist->zones[j++]=zone;

caseZONE_DMA:

zone=pgdat->node_zones+ZONE_DMA;

if(zone->size)

zonelist->zones[j++]=zone;

}

给定的管理区掩码指定了优先顺序,我们可以用它找到在switch语句中的入口点。

如果掩码为__GFP_DMA,管理区链表zonelist将仅仅包含DMA管理区,如果为__GFP_HIGHMEM,则管理区链表中就会依次有ZONE_HIGHMEM、ZONE_NORMAL和ZONE_DMA。

zonelist->zones[j++]

=NULL;

}

用Null结束链表。

3.mem_init()函数

这个函数由start_kernel()调用,以对管理区的分配算法进行进一步的初始化,定义于arch/i386/mm/init.c中,具体解释如下:

intcodesize,reservedpages,datasize,initsize;

inttmp;

intbad_ppro;

if(!

mem_map)

BUG();

#ifdef

CONFIG_HIGHMEM

highmem_start_page

=mem_map+highstart_pfn;

max_mapnr=

num_physpages=highend_pfn;

如果HIGHMEM被激活,就要获得HIGHMEM的起始地址和总的页面数。

#else

max_mapnr=

num_physpages=max_low_pfn;

#endif

否则,页面数就是常规内存的页面数。

high_memory

=(void*)__va(max_low_pfn*PAGE_SIZE);

获得低区内存中最后一个页面的虚地址。

/*clear

thezero-page*/

memset(empty_zero_page,0,PAGE_SIZE);

/*this

willputalllowmemoryontothefreelists*/

totalram_pages

+=free_all_bootmem();

reservedpages=0;

free_a

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

当前位置:首页 > 初中教育 > 英语

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

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