免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 10709 | 回复: 3

[FreeBSD] freebsd9.2-UMA-boot阶段的页框分配(函数startup_alloc) [复制链接]

论坛徽章:
0
发表于 2014-07-26 20:08 |显示全部楼层
本帖最后由 71v5 于 2014-07-26 20:10 编辑

UMA相关的初始化函数(按调用顺序排列)如下所示:
[1->函数uma_startup]-该函数完成了UMA相关的大部分初始化工作,是函数vm_page_startup调用的第一个主要的初始化函数,
函数uma_startup的主要工作简述如下:
  1. SI_SUB_VM                = 0x1000000,        /* virtual memory system init*/
  2. 100: SYSINIT(vm_mem, SI_SUB_VM, SI_ORDER_FIRST, vm_mem_init, NULL);

  3. <1>:初始化变量uma_max_ipers和uma_max_ipers_ref。
  4. <2>:初始化分配struct uma_keg对象的uma zone。
  5. <3>:将UMA boot阶段使用到的物理页框组织起来。
  6. <4>:创建并初始化分配struct uma_zone对象的uma zone。
  7. <5>:创建分配struct uma_slab对象和分配struct uma_slab_refcnt对象的uma zone。
  8. <6>:创建分配哈希表的uma zone。
  9. <7>:创建分配bucket的uma zone。
  10. <8>:将变量booted设置为UMA_STARTUP
  11. 我们下面将简单分析一下工作3.
复制代码
[2->函数uma_startup2]-在函数kmeminit中被调用,函数kmeminit主要做一些初始化工作,为内核malloc函数的正常执行做好准备,
这里先略过函数kmeminit:
  1. SI_SUB_KMEM                = 0x1800000,        /* kernel memory*/
  2. SYSINIT(kmem, SI_SUB_KMEM, SI_ORDER_FIRST, kmeminit, NULL);

  3. /**************************************************************************************************
  4. * 先来简单描述一个数据结构,struct vm_map,该类型的对象描述了内核中某一子系统或者进程
  5.    可以使用的虚拟线性地址的范围,在分页启动后,开始使用虚拟地址寻址,所以在获得一个
  6.    物理页框之前,必须先获取一个可以使用的虚拟线性地址区间,然后修改相应的页表项,用
  7.    获取到的物理页框填充这些页表项,这样就可以访问相应的物理页框了。

  8.    在virtual memory system初始化阶段,内核会创建几个struct vm_map对象用来给内核其它
  9.    子系统分配虚拟地址区间:
  10.    kmem_map:在UMA中使用。
  11.    exec_map:在execve系统调用使用。
  12.    pipe_map:在pipe系统调用使用。
  13.    等等。

  14.    从SYSINIT宏可以看出,当调用函数kmeminit时,virtual memory system已经初始化
  15.    完毕,这就意味着可以使用virtual memory system提供的页框分配接口vm_page_alloc*
  16.    函数来分配页框。

  17.    在函数kmeminit调用uma_startup2之前,会先调用函数kmem_suballoc初始化kmem_map变量,
  18.    UMA提供的page_alloc函数在满足下面两个条件时才能用来分配页框,因为page_alloc函数要
  19.    使用变量kmem_map,同时其内部会使用virtual memory system提供的页框分配接口vm_page_alloc*函数
  20.    来分配页框:
  21.    条件1:virtual memory system已经初始化完成。
  22.    条件2:变量kmem_map被正确初始化。

  23.    1773:更新变量booted,将其设置为UMA_STARTUP2。
  24.    1774:调用函数bucket_enable更新变量bucketdisable。

  25.    所以在函数uma_startup2执行完后,page_alloc函数才能正常工作。
  26. ********************************************************/  
  27.   1769        /* see uma.h */
  28.   1770        void
  29.   1771        uma_startup2(void)
  30.   1772        {
  31.   1773                booted = UMA_STARTUP2;
  32.   1774                bucket_enable();
  33.   1775        #ifdef UMA_DEBUG
  34.   1776                printf("UMA startup2 complete.\n");
  35.   1777        #endif
  36.   1778        }
复制代码
[3->函数uma_startup3]:
  1. SI_SUB_VM_CONF                = 0x2300000,        /* config VM, set limits */
  2. SYSINIT(uma_startup3, SI_SUB_VM_CONF, SI_ORDER_SECOND, uma_startup3, NULL);
  3. /*********************************************************************************
  4. * uma_startup3函数将描述等待事件的struct callout对象添加到相应的callout队列上,
  5.    每次调用函数uma_timeout时,做下面两工作:
  6.    1: 调用函数bucket_enable更新变量bucketdisable。
  7.    2: 如果使用哈希表管理struct slab或者struct uma_slab_refcnt对象,那么就检查
  8.       是否extend哈希表的大小。

  9.    callout相关机制暂时先忽略。
  10. **********************************/
  11.   1785        static void
  12.   1786        uma_startup3(void)
  13.   1787        {
  14.   1788        #ifdef UMA_DEBUG
  15.   1789                printf("Starting callout.\n");
  16.   1790        #endif
  17.   1791                callout_init(&uma_callout, CALLOUT_MPSAFE);
  18.   1792                callout_reset(&uma_callout, UMA_TIMEOUT * hz, uma_timeout, NULL);
  19.   1793        #ifdef UMA_DEBUG
  20.   1794                printf("UMA startup3 complete.\n");
  21.   1795        #endif
  22.   1796        }
复制代码
结合前面UMA相关数据结构以及上面的描述,每个uma zone由struct uma_zone对象来描述,同时每个uma zone关联了
一个struct uma_keg对象,当从uma zone获取对象时,如果slab为空,就要调用struct uma_keg对象的成员
uk_allocf指向的函数来分配页框。在创建uma zone的过程中,成员uk_allocf被默认设置为函数page_alloc
的地址,随后会根据变量booted的值确定是否要将成员uk_allocf更新为函数startup_alloc的地址。
此时变量booted的值作为一个分界点:
1:当变量booted的值小于UMA_STARTUP2时,表示此时还处于UMA boot阶段并且virtual memory system还没有
初始化完成,就要使用startup_alloc函数来分配页框。

2:当变量booted的值大于UMA_STARTUP2时,就表示UMA boot阶段结束并且virtual memory system已经初始化完毕,
就可以使用函数page_alloc来分配页框。

从以前帖子中对vm_page_startup函数的描述可知,当该函数执行完后,物理内存的简单布局如下下面的图2所示,其中标记为'A'的
物理内存就是分配给UMA boot阶段使用:
图1:
物理内存布局01.jpg

下面我们主要对UMA boot阶段如何组织这些页框以及如何分配这些页框做一下简单分析,先来看一个链表:
  1. /******************************************************************
  2. * Linked list of boot time pages.

  3.    struct {
  4.        struct uma_slab *lh_first;
  5.    }uma_boot_pages = { NULL );
  6.    
  7.    uma_boot_pages链接了分配给UMA boot阶段的页框。
  8. ******************************/
  9.    129        static LIST_HEAD(,uma_slab) uma_boot_pages = LIST_HEAD_INITIALIZER(uma_boot_pages);
复制代码
在函数vm_page_startup给UMA boot阶段分配了物理页框后,就立即调用函数uma_startup:
  1. /**********************************************************************************************
  2. * 函数uma_startup,参数描述:

  3.    bootmem:一个虚拟线性地址,可以访问分配给UMA boot阶段使用的物理页框,上面图1中所示。

  4.    boot_pages:UMA boot阶段使用的物理页框的数目,这里为64个PAGE。

  5.    其中只保留了组织物理内存相关的代码。

  6.    从1699-1704之间的for循环可以看出:
  7.    1:内核巧妙地在每个物理页框头部构造一个struct uma_slab对象,将相应的物理页框链接到
  8.       链表uma_boot_pages中。

  9.    2:链表uma_boot_pages中的物理页框是连续的,并且访问相应物理页框的虚拟线性地址也是连续的。

  10.    3:链表uma_boot_pages中的物理页框按照相应的物理页框号以降序排列。

  11.    并且对于每一个struct uma_slab对象:
  12.    成员us_data:保存的是访问相应物理页框的虚拟线性地址。

  13.    成员us_flags:设置标志UMA_SLAB_BOOT,在后续的回收操作中,将不会回收设置了
  14.                  UMA_SLAB_BOOT标志的slab。

  15.    这段for循环执行完后,页框的简单组织如下面的图2所示,为了简单期间,只包含了
  16.    8个页框。

  17.    #define UMA_SLAB_BOOT        0x01                Slab alloced from boot pages
  18. *****************************************/   
  19.   1598        void
  20.   1599        uma_startup(void *bootmem, int boot_pages)
  21.   1600        {
  22. .....................................................................................................
  23. .....................................................................................................
  24.   1699                for (i = 0; i < boot_pages; i++) {
  25.   1700                        slab = (uma_slab_t)((u_int8_t *)bootmem + (i * UMA_SLAB_SIZE));
  26.   1701                        slab->us_data = (u_int8_t *)slab;
  27.   1702                        slab->us_flags = UMA_SLAB_BOOT;
  28.   1703                        LIST_INSERT_HEAD(&uma_boot_pages, slab, us_link);
  29.   1704                }
  30. .....................................................................................................
  31. .....................................................................................................
  32.   1767        }
复制代码
图2:
uma-boot-page-布局.jpg

[函数startup_alloc]-UMA boot阶段分配页框:
  1. /*********************************************************************************
  2. * 参数描述:
  3.    zone:指向描述相关uma zone的struct uma_zone对象。
  4.    bytes:此次需要分配的内存大小。
  5.    pflag:返回给调用者的标志。
  6.    wait:一些标志,标识分配页框过程中是否需要睡眠。

  7.    结合上面的描述以及图2,startup_alloc函数已经很容易理解了。
  8. ********************************************************/
  9.    928        static void *
  10.    929        startup_alloc(uma_zone_t zone, int bytes, u_int8_t *pflag, int wait)
  11.    930        {
  12.    931                uma_keg_t keg;
  13.    932                uma_slab_t tmps;
  14.    933                int pages, check_pages;
  15.    934       
  16. /****************************************************************************
  17. * kegs:指向相关联的struct uma_keg对象。

  18.    pages:此次请求的页框数目。

  19.    check_pages:用来检查链表uma_boot_pages中是否有足够数目的页框。

  20.    946-948:如果tmps为NULL,就表示:
  21.             1:链表uma_boot_pages为空。
  22.             或者
  23.             2:链表uma_boot_pages非空,但是剩余的页框数目不满足此次分配需求。
  24.    
  25.    949-962:链表uma_boot_pages中有足够数目的页框满足此次页框分配需求。
  26. ****************************************/  
  27.    935                keg = zone_first_keg(zone);
  28.    936                pages = howmany(bytes, PAGE_SIZE);
  29.    937                check_pages = pages - 1;
  30.    938                KASSERT(pages > 0, ("startup_alloc can't reserve 0 pages\n"));
  31.    939       
  32.    940                /*
  33.    941                 * Check our small startup cache to see if it has pages remaining.
  34.    942                 */
  35.    943                mtx_lock(&uma_boot_pages_mtx);
  36.    944       
  37.    945                /* First check if we have enough room. */
  38.    946                tmps = LIST_FIRST(&uma_boot_pages);
  39.    947                while (tmps != NULL && check_pages-- > 0)
  40.    948                        tmps = LIST_NEXT(tmps, us_link);
  41.    949                if (tmps != NULL) {
  42.    950                        /*
  43.    951                         * It's ok to lose tmps references.  The last one will
  44.    952                         * have tmps->us_data pointing to the start address of
  45.    953                         * "pages" contiguous pages of memory.
  46.    954                         */
  47.    955                        while (pages-- > 0) {
  48.    956                                tmps = LIST_FIRST(&uma_boot_pages);
  49.    957                                LIST_REMOVE(tmps, us_link);
  50.    958                        }
  51.    959                        mtx_unlock(&uma_boot_pages_mtx);
  52.    960                        *pflag = tmps->us_flags;
  53.    961                        return (tmps->us_data);
  54.    962                }
  55.    963                mtx_unlock(&uma_boot_pages_mtx);
  56. /*********************************************************************************
  57. * 如果执行到这里,即tmps为NULL,就表示链表uma_boot_pages中的物理页框数目不满足
  58.    此次分配需求。

  59.    964-965:
  60.    检查变量booted的值,如果if的条件为真,就表示此时还处于UMA boot阶段并且
  61.    virtual memory system还没有初始化完成,此时就要增加分配给UMA boot阶段使用的
  62.    物理页框的数目。
  63. *********************************/
  64.    964                if (booted < UMA_STARTUP2)
  65.    965                        panic("UMA: Increase vm.boot_pages");
  66. /**********************************************************************
  67. * Now that we've booted reset these users to their real allocator.
  68. *
  69.    执行到这里的话,就表示UMA和virtual memory system已经初始化完成,
  70.    此时用正确的函数地址重新设置成员uk_allocf:
  71.    
  72.    969-971:和具体底层架构相关,uma_small_alloc函数由相应的实现定义,
  73.             这里忽略。

  74.    971-973:很明显,此时函数page_alloc已经可以正常工作。

  75.    974:使用新的页框分配函数重新分配页框。
  76. ********************************/
  77.    968                 
  78.    969        #ifdef UMA_MD_SMALL_ALLOC
  79.    970                keg->uk_allocf = (keg->uk_ppera > 1) ? page_alloc : uma_small_alloc;
  80.    971        #else
  81.    972                keg->uk_allocf = page_alloc;
  82.    973        #endif
  83.    974                return keg->uk_allocf(zone, bytes, pflag, wait);
  84.    975        }
复制代码

论坛徽章:
2
亥猪
日期:2014-03-19 16:36:35午马
日期:2014-11-23 23:48:46
发表于 2014-07-27 11:57 |显示全部楼层
一定坚持下去!

论坛徽章:
0
发表于 2014-07-27 21:15 |显示全部楼层
回复 2# gvim

在自己能力范围内,尽最大努力
   

论坛徽章:
0
发表于 2014-08-10 18:50 |显示全部楼层
本人小白一枚,慢慢学习中,多谢楼主分享~
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP