浅析armlinux-paging_init()->free_area_init_core()函数

    //----------------------------------------

    //1.mm/Numa.c->free_area_init_node()

    void __init free_area_init_node(int nid, pg_data_t *pgdat, struct page *pmap,

     unsigned long *zones_size, unsigned long zone_start_paddr,

     unsigned long *zholes_size)

    {

     free_area_init_core(0, &contig_page_data, &mem_map, zones_size,

     zone_start_paddr, zholes_size, pmap);

    }

    //----------------------------------------

    //2.mm/Page_alloc.c->free_area_init_core()

    void __init free_area_init_core(int nid, pg_data_t *pgdat, struct page **gmap,

     unsigned long *zones_size, unsigned long zone_start_paddr,

     unsigned long *zholes_size, struct page *lmem_map)

    {

     unsigned long i, j;

     unsigned long map_size;

     unsigned long totalpages, offset, realtotalpages;

     const unsigned long zone_required_alignment = 1UL (MAX_ORDER-1);

     if (zone_start_paddr & ~PAGE_MASK)//zone_start_paddr一定是要页对齐的

     BUG();

     totalpages = 0;

    //#define MAX_NR_ZONES 3

    //累加所有zone大小到totalpages

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

     unsigned long size = zones_size;

     totalpages += size;

     }

     realtotalpages = totalpages;

     if (zholes_size)

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

     realtotalpages -= zholes_size;//挖掉每一个zone内部的holes空间

     printk("On node %d totalpages: %lu\n", nid, realtotalpages);

    //每一个4k物理页都对应一个mem_map_t来管理

     map_size = (totalpages + 1)*sizeof(struct page);

    //多申请1个sizeof(struct page),因为可能会调整掉第一个sizeof(struct page)大小空间的前几个字节

     if (lmem_map == (struct page *)0) {

    lmem_map = (struct page *) alloc_bootmem_node(pgdat, map_size);

    //将申请到的map_size大小的空间起始地址lmem_map进行sizeof(mem_map_t)字节对齐调整,其对齐基址为PAGE_OFFSET

    lmem_map = (struct page *)(PAGE_OFFSET + MAP_ALIGN((unsigned long)lmem_map - PAGE_OFFSET));

     }

     *gmap = pgdat->node_mem_map = lmem_map;//存储到mem_map中,之后mem_map=lmem_map

     pgdat->node_size = totalpages;

     pgdat->node_start_paddr = zone_start_paddr;

     pgdat->node_start_mapnr = (lmem_map - mem_map);//lmem_map和mem_map相对距离,距离多少个mem_map_t

     pgdat->nr_zones = 0;

     offset = lmem_map - mem_map;

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

     zone_t *zone = pgdat->node_zones + j;

     unsigned long mask;

     unsigned long size, realsize;

     zone_table[nid * MAX_NR_ZONES + j] = zone;

     realsize = size = zones_size[j];

     if (zholes_size)

     realsize -= zholes_size[j];

    //打印输出内容如下:

    //On node 0 totalpages: 8192

    //zone(0): 8192 pages.

    //zone(1): 0 pages.

    //zone(2): 0 pages.

     printk("zone(%lu): %lu pages.\n", j, size);

     zone->size = size;

     zone->name = zone_names[j];

    //zone_names[0]="DMA"

    //zone_names[1]="Normal"

    //zone_names[2]="HighMem"

    //zone只是内核为了管理方便做的一种逻辑上的划分,并不存在这种物理硬件单元[gliethttp]

     zone->lock = SPIN_LOCK_UNLOCKED;

     zone->zone_pgdat = pgdat;

     zone->free_pages = 0;

     zone->need_balance = 0;

     if (!size)

     continue;

    //用于进程睡眠在当前页上的waitqueue个数

     zone->wait_table_size = wait_table_size(size);

     zone->wait_table_shift =

     BITS_PER_LONG - wait_table_bits(zone->wait_table_size);

    //申请zone->wait_table_size*sizeof(wait_queue_head_t)个字节连续数据

     zone->wait_table = (wait_queue_head_t *)

     alloc_bootmem_node(pgdat, zone->wait_table_size

     * sizeof(wait_queue_head_t));

    //初始化wait_table中wait_queue_head表项

     for(i = 0; i zone->wait_table_size; ++i)

     init_waitqueue_head(zone->wait_table + i);

     pgdat->nr_zones = j+1;//记录当前zone的下一个zone号

    //static int zone_balance_ratio[MAX_NR_ZONES] __initdata = { 128, 128, 128, };

    //static int zone_balance_min[MAX_NR_ZONES] __initdata = { 20 , 20, 20, };

    //static int zone_balance_max[MAX_NR_ZONES] __initdata = { 255, 255, 255, };

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

    //系统中空闲物理页数绝对不要少于pages_min

    //该zone区内存最底限,到达该值时,分配器将同步调用kswapd,

    //让其将内核中的某些页交换到外存,从而保证系统中有足够的空闲页块.

     zone->pages_min = mask;

    //当系统中空闲物理页数少于pages_low时,开始强化交换

     zone->pages_low = mask*2;

    //当空闲物理页数少于pages_high时,启动后台交换;而当空闲物理页数大于pages_high时,内核交换守护进程什么也不做

     zone->pages_high = mask*3;//该zone区内存临界值

    //管理每个4k页的数据结构mem_map_t的起始地址zone->zone_mem_map

     zone->zone_mem_map = mem_map + offset;

     zone->zone_start_mapnr = offset;//lmem_map和mem_map相对距离,距离多少个mem_map_t

     zone->zone_start_paddr = zone_start_paddr;//该zone的起始地址

    //该zone_start_paddr应该是2^9,即:512页对齐,即:512*4k=2M字节对齐

     if ((zone_start_paddr >> PAGE_SHIFT) & (zone_required_alignment-1))

     printk("BUG: wrong zone alignment, it will crash\n");

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

     struct page *page = mem_map + offset + i;

    //对每一个4k页对应的mem_map_t管理结构进行如下相同的初始化

     set_page_zone(page, nid * MAX_NR_ZONES + j);//设置flag的zone标志区

     set_page_count(page, 0);//设置使用本页的用户数目为0

     SetPageReserved(page);//设置本页flag为PG_reserved,由系统保留使用

     INIT_LIST_HEAD(&page->list);//初始化list的头和屁股使其都指向自己

     if (j != ZONE_HIGHMEM)

     set_page_address(page, __va(zone_start_paddr));//对于我的armlinux版本,此为空

     zone_start_paddr += PAGE_SIZE;//因为上一句没有,所以此举也无用

     }

     offset += size;

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

    //linux的物理页分配采用链表和位图结合的方法.linux内核定义了一个称为free_area的数组,

    //该数组的每一项描述某一种页块的信息

    //zone->free_area[0]--2^0=1页空闲链表

    //zone->free_area[1]--2^1=2页空闲链表

    //zone->free_area[2]--2^2=4页空闲链表

    //...

    //zone->free_area[9]--2^9=512页空闲链表

    //数组free_area的每项包含三个元素:next、prev和map.指针next、prev用于将物理页块结构mem_map_t连结成一个双向链表;

    //而map则是记录这种页块组分配情况的位图,例如,位图的第N位为1,表明第N个页块是空闲的.从图中也可以看到,

    //用来记录页块组分配情况的位图大小各不相同,显然页块越小,位图越大.free_area数组的大小为10或12(可调),

    //因此它管理的最小内存块的大小是1页(4K),最大内存块的大小是512页(2M)或2048页(8M).

    //页分配代码使用向量表free_area来分配和回收物理页

     unsigned long bitmap_size;

     INIT_LIST_HEAD(&zone->free_area.free_list);//初始化free_area的链表

     if (i == MAX_ORDER-1) {

     zone->free_area.map = NULL;

    //我的armlinux,MAX_ORDER=10,所以一共2^9=512个页,所以kmalloc最多申请512个连续页=512*4k=2M

     break;

     }

    //对于Buddy(伙伴)算法,采用模型:index >> (i+1);[i为order值,index一般是"size-1"]

    //所以Buddy管理的内存总大小为size-1,记得在念高中的时候有这样一道数学题

    //s=1/2+1/4+1/8+...1/2^n的值是多少,怎么计算呢?先将等式两端分别乘上2,即:

    //2s=1+1/2+1/4+...1/2^(n+1)=逼近1+s,所以s=逼近1,但永远都达不到1.

    //所以从这个角度我们可以看到Buddy的算法中,不论MAX_ORDER有多大10,10000...,甚至无穷,也不会把size-1切割到0

    //size-1数据穷尽,当然这还要考虑小于1字节时已经不存在意义的问题,

    //但是MAX_ORDER已经有很强的伸缩性了[gliethttp 2007-08-05]

    //free_area[0].map位图大小=((size-1)/ 2)/8=(size-1) >> (0+1+3)

    //free_area[1].map位图大小=((size-1)/ 4)/8=(size-1) >> (1+1+3)

    //...

    //free_area[9].map位图大小=((size-1)/1024)/8=(size-1) >> (9+1+3)

     bitmap_size = (size-1) >> (i+4);

    //因为所有bitmap的操作都是long类型4字节操作,所以需要将bitmap_size调整成>=先前bitmap_size的4倍数

    //可以假想bitmap_size&0x03=1,出现零头,怎么办,显然我们不能不管,所以

    //#define LONG_ALIGN(x) (((x)+(sizeof(long))-1)&~((sizeof(long))-1))

    //(1+3)&~3这样对齐到了上边界,

    //但是为什么要传递bitmap_size+1进行调整呢,这是为了,当bitmap_size&0x03=0时,它还要进行

    //(0+1+3)&~3,这样就到了下一个对齐单元,但明明已经对齐了,为什么还要加,我想这可能和统一的风格和舒适度有关系吧!

    //所以调整后的bitmap_size肯定大于先前为调整的bitmap_size,1~4字节之间

     bitmap_size = LONG_ALIGN(bitmap_size+1);

     zone->free_area.map = //返回32字节对齐后的bitmap_size大小虚拟地址空间首地址

    //请参考《浅析armlinux-setup_arch()->alloc_bootmem_low_pages()函数5-1》[http://gliethttp.cublog.cn]

     (unsigned long *) alloc_bootmem_node(pgdat, bitmap_size);

     }

     }

     build_zonelists(pgdat);//将zone登记到zonelist中

    }

    //----------------------------------------

    //3.

    #define PAGES_PER_WAITQUEUE 256

    static inline unsigned long wait_table_size(unsigned long pages)

    {unsigned long size = 1;

     pages /= PAGES_PER_WAITQUEUE;//8192/256=32

     while (size pages)

     size = 1;//计算上边界

     size = min(size, 4096UL);//权衡一个用来进程睡眠wait queue的合理大小

     return size;

    }

    //----------------------------------------

    //4.

    static inline unsigned long wait_table_bits(unsigned long size)

    {

    //fft[find first zero in word]从低位开始找到第一个0的位置

    //所以这里是查找size中第一个出现1的位置

     return ffz(~size);

    }

    //----------------------------------------

    //5.

    static inline void set_page_zone(struct page *page, unsigned long zone_num)

    {

     page->flags &= ~(~0UL ZONE_SHIFT);//清0高8位

     page->flags |= zone_num ZONE_SHIFT;//将zone_num送到高8位

    }

    //----------------------------------------

    //6.include/linux/Mm.h->SetPageReserved()

    #define SetPageReserved(page) set_bit(PG_reserved, &(page)->flags)

    //arch/arm/lib/setbit.S->set_bit()

    ENTRY(set_bit)

     and r2, r0, #7//r0在字节内的移位量0~7

     mov r3, #1

     mov r3, r3, lsl r2//将计算偏移后结果存入r3

     save_and_disable_irqs ip, r2//禁用中断,同时ip=cpsr

     ldrb r2, [r1, r0, lsr #3]//读取第r0位对应字节处的字节数据,即:r1+r0/8

     orr r2, r2, r3//相应位置1

     strb r2, [r1, r0, lsr #3]//回写

     restore_irqs ip

     RETINSTR(mov,pc,lr)

    //----------------------------------------

    //7.mm/Page_alloc.c->build_zonelists()

    static inline void build_zonelists(pg_data_t *pgdat)

    {int i, j, k;

    //#define GFP_ZONEMASK 0x0f

     for (i = 0; i = GFP_ZONEMASK; i++) {

     zonelist_t *zonelist;

     zone_t *zone;

     zonelist = pgdat->node_zonelists + i;

    //对memset的理解可参见《浅析armlinux-__arch_clear_user内存拷贝函数之3》[http://gliethttp.cublog.cn]

     memset(zonelist, 0, sizeof(*zonelist));

     j = 0;

    //#define ZONE_DMA 0

    //#define ZONE_NORMAL 1

    //#define ZONE_HIGHMEM 2

    //所以GFP_ZONEMASK的所有i值,都被0x3掩掉

     k = ZONE_NORMAL;

     if (i & __GFP_HIGHMEM)

     k = ZONE_HIGHMEM;

     if (i & __GFP_DMA)

     k = ZONE_DMA;

     switch (k) {//我的armlinux版本只提供3种zone

     default:

     BUG();

     case ZONE_HIGHMEM:

     zone = pgdat->node_zones + ZONE_HIGHMEM;

     if (zone->size) {

    #ifndef CONFIG_HIGHMEM

     BUG();

    #endif

     zonelist->zones[j++] = zone;//将ZONE_HIGHMEM对应的node填入表中

     }

     case ZONE_NORMAL:

     zone = pgdat->node_zones + ZONE_NORMAL;

     if (zone->size)

     zonelist->zones[j++] = zone;//将ZONE_NORMAL对应的node填入表中

     case ZONE_DMA:

     zone = pgdat->node_zones + ZONE_DMA;

     if (zone->size)

     zonelist->zones[j++] = zone;//将ZONE_DMA对应的node填入表中

     }

     zonelist->zones[j++] = NULL;//给node链表追加结尾

     }

    }

转载出处: http://blog.chinaunix.net/u1/38994/showart_354178.html
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值