- 论坛徽章:
- 0
|
本帖最后由 71v5 于 2014-07-26 20:10 编辑
UMA相关的初始化函数(按调用顺序排列)如下所示:
[1->函数uma_startup]-该函数完成了UMA相关的大部分初始化工作,是函数vm_page_startup调用的第一个主要的初始化函数,
函数uma_startup的主要工作简述如下:- SI_SUB_VM = 0x1000000, /* virtual memory system init*/
- 100: SYSINIT(vm_mem, SI_SUB_VM, SI_ORDER_FIRST, vm_mem_init, NULL);
- <1>:初始化变量uma_max_ipers和uma_max_ipers_ref。
- <2>:初始化分配struct uma_keg对象的uma zone。
- <3>:将UMA boot阶段使用到的物理页框组织起来。
- <4>:创建并初始化分配struct uma_zone对象的uma zone。
- <5>:创建分配struct uma_slab对象和分配struct uma_slab_refcnt对象的uma zone。
- <6>:创建分配哈希表的uma zone。
- <7>:创建分配bucket的uma zone。
- <8>:将变量booted设置为UMA_STARTUP
- 我们下面将简单分析一下工作3.
复制代码 [2->函数uma_startup2]-在函数kmeminit中被调用,函数kmeminit主要做一些初始化工作,为内核malloc函数的正常执行做好准备,
这里先略过函数kmeminit:- SI_SUB_KMEM = 0x1800000, /* kernel memory*/
- SYSINIT(kmem, SI_SUB_KMEM, SI_ORDER_FIRST, kmeminit, NULL);
- /**************************************************************************************************
- * 先来简单描述一个数据结构,struct vm_map,该类型的对象描述了内核中某一子系统或者进程
- 可以使用的虚拟线性地址的范围,在分页启动后,开始使用虚拟地址寻址,所以在获得一个
- 物理页框之前,必须先获取一个可以使用的虚拟线性地址区间,然后修改相应的页表项,用
- 获取到的物理页框填充这些页表项,这样就可以访问相应的物理页框了。
- 在virtual memory system初始化阶段,内核会创建几个struct vm_map对象用来给内核其它
- 子系统分配虚拟地址区间:
- kmem_map:在UMA中使用。
- exec_map:在execve系统调用使用。
- pipe_map:在pipe系统调用使用。
- 等等。
- 从SYSINIT宏可以看出,当调用函数kmeminit时,virtual memory system已经初始化
- 完毕,这就意味着可以使用virtual memory system提供的页框分配接口vm_page_alloc*
- 函数来分配页框。
- 在函数kmeminit调用uma_startup2之前,会先调用函数kmem_suballoc初始化kmem_map变量,
- UMA提供的page_alloc函数在满足下面两个条件时才能用来分配页框,因为page_alloc函数要
- 使用变量kmem_map,同时其内部会使用virtual memory system提供的页框分配接口vm_page_alloc*函数
- 来分配页框:
- 条件1:virtual memory system已经初始化完成。
- 条件2:变量kmem_map被正确初始化。
-
- 1773:更新变量booted,将其设置为UMA_STARTUP2。
- 1774:调用函数bucket_enable更新变量bucketdisable。
- 所以在函数uma_startup2执行完后,page_alloc函数才能正常工作。
- ********************************************************/
- 1769 /* see uma.h */
- 1770 void
- 1771 uma_startup2(void)
- 1772 {
- 1773 booted = UMA_STARTUP2;
- 1774 bucket_enable();
- 1775 #ifdef UMA_DEBUG
- 1776 printf("UMA startup2 complete.\n");
- 1777 #endif
- 1778 }
复制代码 [3->函数uma_startup3]:- SI_SUB_VM_CONF = 0x2300000, /* config VM, set limits */
- SYSINIT(uma_startup3, SI_SUB_VM_CONF, SI_ORDER_SECOND, uma_startup3, NULL);
- /*********************************************************************************
- * uma_startup3函数将描述等待事件的struct callout对象添加到相应的callout队列上,
- 每次调用函数uma_timeout时,做下面两工作:
- 1: 调用函数bucket_enable更新变量bucketdisable。
- 2: 如果使用哈希表管理struct slab或者struct uma_slab_refcnt对象,那么就检查
- 是否extend哈希表的大小。
- callout相关机制暂时先忽略。
- **********************************/
- 1785 static void
- 1786 uma_startup3(void)
- 1787 {
- 1788 #ifdef UMA_DEBUG
- 1789 printf("Starting callout.\n");
- 1790 #endif
- 1791 callout_init(&uma_callout, CALLOUT_MPSAFE);
- 1792 callout_reset(&uma_callout, UMA_TIMEOUT * hz, uma_timeout, NULL);
- 1793 #ifdef UMA_DEBUG
- 1794 printf("UMA startup3 complete.\n");
- 1795 #endif
- 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:
下面我们主要对UMA boot阶段如何组织这些页框以及如何分配这些页框做一下简单分析,先来看一个链表:- /******************************************************************
- * Linked list of boot time pages.
- struct {
- struct uma_slab *lh_first;
- }uma_boot_pages = { NULL );
-
- uma_boot_pages链接了分配给UMA boot阶段的页框。
- ******************************/
- 129 static LIST_HEAD(,uma_slab) uma_boot_pages = LIST_HEAD_INITIALIZER(uma_boot_pages);
复制代码 在函数vm_page_startup给UMA boot阶段分配了物理页框后,就立即调用函数uma_startup:- /**********************************************************************************************
- * 函数uma_startup,参数描述:
- bootmem:一个虚拟线性地址,可以访问分配给UMA boot阶段使用的物理页框,上面图1中所示。
- boot_pages:UMA boot阶段使用的物理页框的数目,这里为64个PAGE。
- 其中只保留了组织物理内存相关的代码。
- 从1699-1704之间的for循环可以看出:
- 1:内核巧妙地在每个物理页框头部构造一个struct uma_slab对象,将相应的物理页框链接到
- 链表uma_boot_pages中。
- 2:链表uma_boot_pages中的物理页框是连续的,并且访问相应物理页框的虚拟线性地址也是连续的。
-
- 3:链表uma_boot_pages中的物理页框按照相应的物理页框号以降序排列。
- 并且对于每一个struct uma_slab对象:
- 成员us_data:保存的是访问相应物理页框的虚拟线性地址。
- 成员us_flags:设置标志UMA_SLAB_BOOT,在后续的回收操作中,将不会回收设置了
- UMA_SLAB_BOOT标志的slab。
- 这段for循环执行完后,页框的简单组织如下面的图2所示,为了简单期间,只包含了
- 8个页框。
- #define UMA_SLAB_BOOT 0x01 Slab alloced from boot pages
- *****************************************/
- 1598 void
- 1599 uma_startup(void *bootmem, int boot_pages)
- 1600 {
- .....................................................................................................
- .....................................................................................................
- 1699 for (i = 0; i < boot_pages; i++) {
- 1700 slab = (uma_slab_t)((u_int8_t *)bootmem + (i * UMA_SLAB_SIZE));
- 1701 slab->us_data = (u_int8_t *)slab;
- 1702 slab->us_flags = UMA_SLAB_BOOT;
- 1703 LIST_INSERT_HEAD(&uma_boot_pages, slab, us_link);
- 1704 }
- .....................................................................................................
- .....................................................................................................
- 1767 }
复制代码 图2:
[函数startup_alloc]-UMA boot阶段分配页框:- /*********************************************************************************
- * 参数描述:
- zone:指向描述相关uma zone的struct uma_zone对象。
- bytes:此次需要分配的内存大小。
- pflag:返回给调用者的标志。
- wait:一些标志,标识分配页框过程中是否需要睡眠。
- 结合上面的描述以及图2,startup_alloc函数已经很容易理解了。
- ********************************************************/
- 928 static void *
- 929 startup_alloc(uma_zone_t zone, int bytes, u_int8_t *pflag, int wait)
- 930 {
- 931 uma_keg_t keg;
- 932 uma_slab_t tmps;
- 933 int pages, check_pages;
- 934
- /****************************************************************************
- * kegs:指向相关联的struct uma_keg对象。
- pages:此次请求的页框数目。
- check_pages:用来检查链表uma_boot_pages中是否有足够数目的页框。
- 946-948:如果tmps为NULL,就表示:
- 1:链表uma_boot_pages为空。
- 或者
- 2:链表uma_boot_pages非空,但是剩余的页框数目不满足此次分配需求。
-
- 949-962:链表uma_boot_pages中有足够数目的页框满足此次页框分配需求。
- ****************************************/
- 935 keg = zone_first_keg(zone);
- 936 pages = howmany(bytes, PAGE_SIZE);
- 937 check_pages = pages - 1;
- 938 KASSERT(pages > 0, ("startup_alloc can't reserve 0 pages\n"));
- 939
- 940 /*
- 941 * Check our small startup cache to see if it has pages remaining.
- 942 */
- 943 mtx_lock(&uma_boot_pages_mtx);
- 944
- 945 /* First check if we have enough room. */
- 946 tmps = LIST_FIRST(&uma_boot_pages);
- 947 while (tmps != NULL && check_pages-- > 0)
- 948 tmps = LIST_NEXT(tmps, us_link);
- 949 if (tmps != NULL) {
- 950 /*
- 951 * It's ok to lose tmps references. The last one will
- 952 * have tmps->us_data pointing to the start address of
- 953 * "pages" contiguous pages of memory.
- 954 */
- 955 while (pages-- > 0) {
- 956 tmps = LIST_FIRST(&uma_boot_pages);
- 957 LIST_REMOVE(tmps, us_link);
- 958 }
- 959 mtx_unlock(&uma_boot_pages_mtx);
- 960 *pflag = tmps->us_flags;
- 961 return (tmps->us_data);
- 962 }
- 963 mtx_unlock(&uma_boot_pages_mtx);
- /*********************************************************************************
- * 如果执行到这里,即tmps为NULL,就表示链表uma_boot_pages中的物理页框数目不满足
- 此次分配需求。
- 964-965:
- 检查变量booted的值,如果if的条件为真,就表示此时还处于UMA boot阶段并且
- virtual memory system还没有初始化完成,此时就要增加分配给UMA boot阶段使用的
- 物理页框的数目。
- *********************************/
- 964 if (booted < UMA_STARTUP2)
- 965 panic("UMA: Increase vm.boot_pages");
- /**********************************************************************
- * Now that we've booted reset these users to their real allocator.
- *
- 执行到这里的话,就表示UMA和virtual memory system已经初始化完成,
- 此时用正确的函数地址重新设置成员uk_allocf:
-
- 969-971:和具体底层架构相关,uma_small_alloc函数由相应的实现定义,
- 这里忽略。
- 971-973:很明显,此时函数page_alloc已经可以正常工作。
- 974:使用新的页框分配函数重新分配页框。
- ********************************/
- 968
- 969 #ifdef UMA_MD_SMALL_ALLOC
- 970 keg->uk_allocf = (keg->uk_ppera > 1) ? page_alloc : uma_small_alloc;
- 971 #else
- 972 keg->uk_allocf = page_alloc;
- 973 #endif
- 974 return keg->uk_allocf(zone, bytes, pflag, wait);
- 975 }
复制代码 |
|