- 論壇徽章:
- 0
|
本帖最后由 71v5 于 2014-07-26 20:10 編輯
UMA相關(guān)的初始化函數(shù)(按調(diào)用順序排列)如下所示:
[1->函數(shù)uma_startup]-該函數(shù)完成了UMA相關(guān)的大部分初始化工作,是函數(shù)vm_page_startup調(diào)用的第一個(gè)主要的初始化函數(shù),
函數(shù)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對(duì)象的uma zone。
- <3>:將UMA boot階段使用到的物理頁框組織起來。
- <4>:創(chuàng)建并初始化分配struct uma_zone對(duì)象的uma zone。
- <5>:創(chuàng)建分配struct uma_slab對(duì)象和分配struct uma_slab_refcnt對(duì)象的uma zone。
- <6>:創(chuàng)建分配哈希表的uma zone。
- <7>:創(chuàng)建分配bucket的uma zone。
- <8>:將變量booted設(shè)置為UMA_STARTUP
- 我們下面將簡單分析一下工作3.
復(fù)制代碼 [2->函數(shù)uma_startup2]-在函數(shù)kmeminit中被調(diào)用,函數(shù)kmeminit主要做一些初始化工作,為內(nèi)核malloc函數(shù)的正常執(zhí)行做好準(zhǔn)備,
這里先略過函數(shù)kmeminit:- SI_SUB_KMEM = 0x1800000, /* kernel memory*/
- SYSINIT(kmem, SI_SUB_KMEM, SI_ORDER_FIRST, kmeminit, NULL);
- /**************************************************************************************************
- * 先來簡單描述一個(gè)數(shù)據(jù)結(jié)構(gòu),struct vm_map,該類型的對(duì)象描述了內(nèi)核中某一子系統(tǒng)或者進(jìn)程
- 可以使用的虛擬線性地址的范圍,在分頁啟動(dòng)后,開始使用虛擬地址尋址,所以在獲得一個(gè)
- 物理頁框之前,必須先獲取一個(gè)可以使用的虛擬線性地址區(qū)間,然后修改相應(yīng)的頁表項(xiàng),用
- 獲取到的物理頁框填充這些頁表項(xiàng),這樣就可以訪問相應(yīng)的物理頁框了。
- 在virtual memory system初始化階段,內(nèi)核會(huì)創(chuàng)建幾個(gè)struct vm_map對(duì)象用來給內(nèi)核其它
- 子系統(tǒng)分配虛擬地址區(qū)間:
- kmem_map:在UMA中使用。
- exec_map:在execve系統(tǒng)調(diào)用使用。
- pipe_map:在pipe系統(tǒng)調(diào)用使用。
- 等等。
- 從SYSINIT宏可以看出,當(dāng)調(diào)用函數(shù)kmeminit時(shí),virtual memory system已經(jīng)初始化
- 完畢,這就意味著可以使用virtual memory system提供的頁框分配接口vm_page_alloc*
- 函數(shù)來分配頁框。
- 在函數(shù)kmeminit調(diào)用uma_startup2之前,會(huì)先調(diào)用函數(shù)kmem_suballoc初始化kmem_map變量,
- UMA提供的page_alloc函數(shù)在滿足下面兩個(gè)條件時(shí)才能用來分配頁框,因?yàn)閜age_alloc函數(shù)要
- 使用變量kmem_map,同時(shí)其內(nèi)部會(huì)使用virtual memory system提供的頁框分配接口vm_page_alloc*函數(shù)
- 來分配頁框:
- 條件1:virtual memory system已經(jīng)初始化完成。
- 條件2:變量kmem_map被正確初始化。
-
- 1773:更新變量booted,將其設(shè)置為UMA_STARTUP2。
- 1774:調(diào)用函數(shù)bucket_enable更新變量bucketdisable。
- 所以在函數(shù)uma_startup2執(zhí)行完后,page_alloc函數(shù)才能正常工作。
- ********************************************************/
- 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 }
復(fù)制代碼 [3->函數(shù)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函數(shù)將描述等待事件的struct callout對(duì)象添加到相應(yīng)的callout隊(duì)列上,
- 每次調(diào)用函數(shù)uma_timeout時(shí),做下面兩工作:
- 1: 調(diào)用函數(shù)bucket_enable更新變量bucketdisable。
- 2: 如果使用哈希表管理struct slab或者struct uma_slab_refcnt對(duì)象,那么就檢查
- 是否extend哈希表的大小。
- callout相關(guān)機(jī)制暫時(shí)先忽略。
- **********************************/
- 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 }
復(fù)制代碼 結(jié)合前面UMA相關(guān)數(shù)據(jù)結(jié)構(gòu)以及上面的描述,每個(gè)uma zone由struct uma_zone對(duì)象來描述,同時(shí)每個(gè)uma zone關(guān)聯(lián)了
一個(gè)struct uma_keg對(duì)象,當(dāng)從uma zone獲取對(duì)象時(shí),如果slab為空,就要調(diào)用struct uma_keg對(duì)象的成員
uk_allocf指向的函數(shù)來分配頁框。在創(chuàng)建uma zone的過程中,成員uk_allocf被默認(rèn)設(shè)置為函數(shù)page_alloc
的地址,隨后會(huì)根據(jù)變量booted的值確定是否要將成員uk_allocf更新為函數(shù)startup_alloc的地址。
此時(shí)變量booted的值作為一個(gè)分界點(diǎn):
1:當(dāng)變量booted的值小于UMA_STARTUP2時(shí),表示此時(shí)還處于UMA boot階段并且virtual memory system還沒有
初始化完成,就要使用startup_alloc函數(shù)來分配頁框。
2:當(dāng)變量booted的值大于UMA_STARTUP2時(shí),就表示UMA boot階段結(jié)束并且virtual memory system已經(jīng)初始化完畢,
就可以使用函數(shù)page_alloc來分配頁框。
從以前帖子中對(duì)vm_page_startup函數(shù)的描述可知,當(dāng)該函數(shù)執(zhí)行完后,物理內(nèi)存的簡單布局如下下面的圖2所示,其中標(biāo)記為'A'的
物理內(nèi)存就是分配給UMA boot階段使用:
圖1:
物理內(nèi)存布局01.jpg (213.66 KB, 下載次數(shù): 137)
下載附件
2014-07-26 20:06 上傳
下面我們主要對(duì)UMA boot階段如何組織這些頁框以及如何分配這些頁框做一下簡單分析,先來看一個(gè)鏈表:- /******************************************************************
- * 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);
復(fù)制代碼 在函數(shù)vm_page_startup給UMA boot階段分配了物理頁框后,就立即調(diào)用函數(shù)uma_startup:- /**********************************************************************************************
- * 函數(shù)uma_startup,參數(shù)描述:
- bootmem:一個(gè)虛擬線性地址,可以訪問分配給UMA boot階段使用的物理頁框,上面圖1中所示。
- boot_pages:UMA boot階段使用的物理頁框的數(shù)目,這里為64個(gè)PAGE。
- 其中只保留了組織物理內(nèi)存相關(guān)的代碼。
- 從1699-1704之間的for循環(huán)可以看出:
- 1:內(nèi)核巧妙地在每個(gè)物理頁框頭部構(gòu)造一個(gè)struct uma_slab對(duì)象,將相應(yīng)的物理頁框鏈接到
- 鏈表uma_boot_pages中。
- 2:鏈表uma_boot_pages中的物理頁框是連續(xù)的,并且訪問相應(yīng)物理頁框的虛擬線性地址也是連續(xù)的。
-
- 3:鏈表uma_boot_pages中的物理頁框按照相應(yīng)的物理頁框號(hào)以降序排列。
- 并且對(duì)于每一個(gè)struct uma_slab對(duì)象:
- 成員us_data:保存的是訪問相應(yīng)物理頁框的虛擬線性地址。
- 成員us_flags:設(shè)置標(biāo)志UMA_SLAB_BOOT,在后續(xù)的回收操作中,將不會(huì)回收設(shè)置了
- UMA_SLAB_BOOT標(biāo)志的slab。
- 這段for循環(huán)執(zhí)行完后,頁框的簡單組織如下面的圖2所示,為了簡單期間,只包含了
- 8個(gè)頁框。
- #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 }
復(fù)制代碼 圖2:
uma-boot-page-布局.jpg (138.93 KB, 下載次數(shù): 118)
下載附件
2014-07-26 20:06 上傳
[函數(shù)startup_alloc]-UMA boot階段分配頁框:- /*********************************************************************************
- * 參數(shù)描述:
- zone:指向描述相關(guān)uma zone的struct uma_zone對(duì)象。
- bytes:此次需要分配的內(nèi)存大小。
- pflag:返回給調(diào)用者的標(biāo)志。
- wait:一些標(biāo)志,標(biāo)識(shí)分配頁框過程中是否需要睡眠。
- 結(jié)合上面的描述以及圖2,startup_alloc函數(shù)已經(jīng)很容易理解了。
- ********************************************************/
- 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:指向相關(guān)聯(lián)的struct uma_keg對(duì)象。
- pages:此次請(qǐng)求的頁框數(shù)目。
- check_pages:用來檢查鏈表uma_boot_pages中是否有足夠數(shù)目的頁框。
- 946-948:如果tmps為NULL,就表示:
- 1:鏈表uma_boot_pages為空。
- 或者
- 2:鏈表uma_boot_pages非空,但是剩余的頁框數(shù)目不滿足此次分配需求。
-
- 949-962:鏈表uma_boot_pages中有足夠數(shù)目的頁框滿足此次頁框分配需求。
- ****************************************/
- 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);
- /*********************************************************************************
- * 如果執(zhí)行到這里,即tmps為NULL,就表示鏈表uma_boot_pages中的物理頁框數(shù)目不滿足
- 此次分配需求。
- 964-965:
- 檢查變量booted的值,如果if的條件為真,就表示此時(shí)還處于UMA boot階段并且
- virtual memory system還沒有初始化完成,此時(shí)就要增加分配給UMA boot階段使用的
- 物理頁框的數(shù)目。
- *********************************/
- 964 if (booted < UMA_STARTUP2)
- 965 panic("UMA: Increase vm.boot_pages");
- /**********************************************************************
- * Now that we've booted reset these users to their real allocator.
- *
- 執(zhí)行到這里的話,就表示UMA和virtual memory system已經(jīng)初始化完成,
- 此時(shí)用正確的函數(shù)地址重新設(shè)置成員uk_allocf:
-
- 969-971:和具體底層架構(gòu)相關(guān),uma_small_alloc函數(shù)由相應(yīng)的實(shí)現(xiàn)定義,
- 這里忽略。
- 971-973:很明顯,此時(shí)函數(shù)page_alloc已經(jīng)可以正常工作。
- 974:使用新的頁框分配函數(shù)重新分配頁框。
- ********************************/
- 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 }
復(fù)制代碼 |
|