自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

鴻蒙輕內(nèi)核A核源碼分析系列三物理內(nèi)存之一

開(kāi)發(fā)
從本篇開(kāi)始,我們分析下鴻蒙輕內(nèi)核A核的內(nèi)存管理部分,包括物理內(nèi)存、虛擬內(nèi)存、虛擬映射等部分。物理內(nèi)存(Physical memory)是指通過(guò)物理內(nèi)存條而獲得的內(nèi)存空間,相對(duì)應(yīng)的概念是虛擬內(nèi)存(Virtual memory)。

[[433544]]

想了解更多內(nèi)容,請(qǐng)?jiān)L問(wèn):

51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)

https://harmonyos.51cto.com

從本篇開(kāi)始,我們分析下鴻蒙輕內(nèi)核A核的內(nèi)存管理部分,包括物理內(nèi)存、虛擬內(nèi)存、虛擬映射等部分。物理內(nèi)存(Physical memory)是指通過(guò)物理內(nèi)存條而獲得的內(nèi)存空間,相對(duì)應(yīng)的概念是虛擬內(nèi)存(Virtual memory)。虛擬內(nèi)存使得應(yīng)用進(jìn)程認(rèn)為它擁有一個(gè)連續(xù)完整的內(nèi)存地址空間,而通常是通過(guò)虛擬內(nèi)存和物理內(nèi)存的映射對(duì)應(yīng)著多個(gè)物理內(nèi)存頁(yè)。本文我們先來(lái)熟悉下OpenHarmony鴻蒙輕內(nèi)核提供的物理內(nèi)存(Physical memory)管理模塊。

本文中所涉及的源碼,以O(shè)penHarmony LiteOS-A內(nèi)核為例,均可以在開(kāi)源站點(diǎn)https://gitee.com/openharmony/kernel_liteos_a 獲取。如果涉及開(kāi)發(fā)板,則默認(rèn)以hispark_taurus為例。

我們首先了解了物理內(nèi)存管理的結(jié)構(gòu)體,接著閱讀了物理內(nèi)存如何初始化,然后分析了物理內(nèi)存的申請(qǐng)、釋放和查詢(xún)等操作接口的源代碼。

1、物理內(nèi)存結(jié)構(gòu)體介紹

1.1、物理內(nèi)存頁(yè)LosVmPage

鴻蒙輕內(nèi)核A核的物理內(nèi)存采用了段頁(yè)式管理,每個(gè)物理內(nèi)存段被分割為物理內(nèi)存頁(yè)。在頭文件kernel/base/include/los_vm_page.h中定義了物理內(nèi)存頁(yè)結(jié)構(gòu)體,以及內(nèi)存頁(yè)數(shù)組g_vmPageArray及數(shù)組大小g_vmPageArraySize。物理內(nèi)存頁(yè)結(jié)構(gòu)體LosVmPage可以和物理內(nèi)存頁(yè)一一對(duì)應(yīng),也可以對(duì)應(yīng)多個(gè)連續(xù)的內(nèi)存頁(yè),此時(shí)使用nPages指定內(nèi)存頁(yè)的數(shù)量。

 

  1. typedef struct VmPage { 
  2.     LOS_DL_LIST         node;        /**< 物理內(nèi)存頁(yè)節(jié)點(diǎn),掛在VmFreeList空閑內(nèi)存頁(yè)鏈表上 */ 
  3.     PADDR_T             physAddr;    /**< 物理內(nèi)存頁(yè)內(nèi)存開(kāi)始地址*/ 
  4.     Atomic              refCounts;   /**< 物理內(nèi)存頁(yè)引用計(jì)數(shù) */ 
  5.     UINT32              flags;       /**< 物理內(nèi)存頁(yè)標(biāo)記 */ 
  6.     UINT8               order;       /**< 物理內(nèi)存頁(yè)所在的鏈表數(shù)組的索引,總共有9個(gè)鏈表 */ 
  7.     UINT8               segID;       /**< 物理內(nèi)存頁(yè)所在的物理內(nèi)存段的編號(hào) */ 
  8.     UINT16              nPages;      /**< 連續(xù)物理內(nèi)存頁(yè)的數(shù)量 */ 
  9. } LosVmPage; 
  10.  
  11. extern LosVmPage *g_vmPageArray; 
  12. extern size_t g_vmPageArraySize; 

 

在文件kernel\base\include\los_vm_common.h中定義了內(nèi)存頁(yè)的大小、掩碼和邏輯位移值,可以看出每個(gè)內(nèi)存頁(yè)的大小為4KiB。

 

  1. #ifndef PAGE_SIZE 
  2. #define PAGE_SIZE                        (0x1000U) 
  3. #endif 
  4. #define PAGE_MASK                        (~(PAGE_SIZE - 1)) 
  5. #define PAGE_SHIFT                       (12) 

 

1.2、物理內(nèi)存段LosVmPhysSeg

在文件kernel/base/include/los_vm_phys.h中定義了物理內(nèi)存段LosVmPhysSeg等幾個(gè)結(jié)構(gòu)體。該文件的部分代碼如下所示。⑴處的宏是物理內(nèi)存伙伴算法中空閑內(nèi)存頁(yè)節(jié)點(diǎn)鏈表數(shù)組的大小,VM_PHYS_SEG_MAX表示系統(tǒng)支持的物理內(nèi)存段的數(shù)量。⑵處的結(jié)構(gòu)體用于伙伴算法中空閑內(nèi)存頁(yè)節(jié)點(diǎn)鏈表數(shù)組的元素類(lèi)型,除了記錄雙向鏈表,還維護(hù)鏈表上節(jié)點(diǎn)數(shù)量。⑶就是我們要介紹的物理內(nèi)存段,包含開(kāi)始地址,大小,內(nèi)存頁(yè)基地址,空閑內(nèi)存頁(yè)節(jié)點(diǎn)鏈表數(shù)組,LRU鏈表數(shù)組等成員。

 

  1. ⑴  #define VM_LIST_ORDER_MAX    9 
  2.     #define VM_PHYS_SEG_MAX    32 
  3.  
  4. ⑵  struct VmFreeList { 
  5.         LOS_DL_LIST node;   // 空閑物理內(nèi)存頁(yè)節(jié)點(diǎn) 
  6.         UINT32 listCnt;     // 空閑物理內(nèi)存頁(yè)節(jié)點(diǎn)數(shù)量 
  7.     }; 
  8.  
  9. ⑶  typedef struct VmPhysSeg { 
  10.         PADDR_T start;            /* 物理內(nèi)存段的開(kāi)始地址 */ 
  11.         size_t size;              /* 物理內(nèi)存段的大小,bytes */ 
  12.         LosVmPage *pageBase;      /* 物理內(nèi)存段第一個(gè)物理內(nèi)存頁(yè)結(jié)構(gòu)體地址 */ 
  13.  
  14.         SPIN_LOCK_S freeListLock; /* 伙伴算法雙向鏈表自旋鎖 */ 
  15.         struct VmFreeList freeList[VM_LIST_ORDER_MAX];  /* 空閑物理內(nèi)存頁(yè)的伙伴雙向鏈表 */ 
  16.  
  17.         SPIN_LOCK_S lruLock;  /* LRU雙向鏈表自旋鎖 */ 
  18.         size_t lruSize[VM_NR_LRU_LISTS];  /* LRU大小 */ 
  19.         LOS_DL_LIST lruList[VM_NR_LRU_LISTS];/* LRU雙向鏈表 */ 
  20.     } LosVmPhysSeg; 
  21.  
  22.     struct VmPhysArea { 
  23.         PADDR_T start;  // 物理內(nèi)存區(qū)開(kāi)始地址 
  24.         size_t size;    // 物理內(nèi)存區(qū)大小 
  25.     }; 

在kernel/base/vm/los_vm_phys.c文件中定義了物理內(nèi)存區(qū)數(shù)組g_physArea[],如下代碼所示,其中SYS_MEM_BASE為DDR_MEM_ADDR的宏名稱(chēng),DDR_MEM_ADDR和SYS_MEM_SIZE_DEFAULT定義在文件./device/hisilicon/hispark_taurus/sdk_liteos/board/target_config.h中,表示開(kāi)發(fā)板相關(guān)的物理內(nèi)存地址和大小。

 

  1. STATIC struct VmPhysArea g_physArea[] = { 
  2.     { 
  3.         .start = SYS_MEM_BASE, 
  4.         .size = SYS_MEM_SIZE_DEFAULT, 
  5.     }, 
  6. }; 

 

看下物理內(nèi)存區(qū)VmPhysArea和物理內(nèi)存段的LosVmPhysSeg區(qū)別,前者信息教少,主要記錄開(kāi)始地址和大小,為一塊物理內(nèi)存的最簡(jiǎn)單描述;后者除了物理內(nèi)存塊開(kāi)始地址和大小,還維護(hù)物理頁(yè)開(kāi)始地址,空閑物理頁(yè)伙伴鏈表,LRU鏈表,相應(yīng)的自旋鎖等信息。

上面提到了伙伴算法,先看下伙伴算法的示意圖,如下。每個(gè)物理內(nèi)存段都分割為一個(gè)一個(gè)的內(nèi)存頁(yè),空閑的內(nèi)存頁(yè)掛載在空閑內(nèi)存頁(yè)節(jié)點(diǎn)鏈表上。共有9個(gè)空閑內(nèi)存頁(yè)節(jié)點(diǎn)鏈表,這些鏈表組成鏈表數(shù)組。第一個(gè)鏈表上的內(nèi)存頁(yè)節(jié)點(diǎn)大小為1個(gè)內(nèi)存頁(yè),第二個(gè)鏈表上的內(nèi)存頁(yè)節(jié)點(diǎn)大小為2個(gè)內(nèi)存頁(yè),第三個(gè)鏈表上的內(nèi)存頁(yè)節(jié)點(diǎn)大小為4個(gè)內(nèi)存頁(yè),依次下去,第9個(gè)鏈表上的內(nèi)存頁(yè)節(jié)點(diǎn)大小為2^8個(gè)內(nèi)存頁(yè)。申請(qǐng)內(nèi)存、釋放內(nèi)存時(shí)會(huì)操作這些空閑內(nèi)存頁(yè)節(jié)點(diǎn)鏈表,后文詳細(xì)分析。

鴻蒙輕內(nèi)核A核源碼分析系列三 物理內(nèi)存(1)-鴻蒙HarmonyOS技術(shù)社區(qū)

1.3、物理內(nèi)存伙伴位圖

上文提到伙伴算法,還需要了解下伙伴位圖。在伙伴算法中,每個(gè)鏈表的索引都對(duì)應(yīng)一個(gè)位圖。 位圖的某位對(duì)應(yīng)于兩個(gè)伙伴塊,為1就表示其中一塊忙,為0表示兩塊都閑或都在使用 。系統(tǒng)每次分配和回收伙伴塊時(shí)都要對(duì)它們的伙伴位 跟1進(jìn)行異或運(yùn)算 。所謂異或是指剛開(kāi)始時(shí),兩個(gè)伙伴塊都空閑,它們的伙伴位為0,如果其中一塊被使用,異或后得1;如果另一塊也被使用,異或后得0;如果前面一塊回收了異或后得1;如果另一塊也回收了異或后得0。位圖用于在釋放內(nèi)存頁(yè)塊時(shí),判斷兩塊內(nèi)存是否屬于地址連續(xù)的伙伴內(nèi)存塊。

在文件kernel/base/include/los_vm_phys.h中定義了2個(gè)比較重要的和伙伴位圖相關(guān)的宏,如下。⑴處的宏VM_ORDER_TO_PHYS(order)表示對(duì)應(yīng)每個(gè)空閑鏈表都有一個(gè)位來(lái)標(biāo)記伙伴內(nèi)存塊。⑵處宏VM_PHYS_TO_ORDER(phys)把物理內(nèi)存地址轉(zhuǎn)換為空閑鏈表索引。那么問(wèn)題是,物理內(nèi)存地址和索引有對(duì)應(yīng)關(guān)系?物理地址已基于內(nèi)存頁(yè)大小進(jìn)行對(duì)齊。理論上這個(gè)值可大可小,不明白為什么這么設(shè)計(jì)?TODO。

 

  1. ⑴  #define VM_ORDER_TO_PHYS(order)  (1 << (PAGE_SHIFT + (order))) 
  2. ⑵  #define VM_PHYS_TO_ORDER(phys)   (min(LOS_LowBitGet((phys) >> PAGE_SHIFT), VM_LIST_ORDER_MAX - 1)) 

 

2、物理內(nèi)存管理模塊初始化

本節(jié)主要講解物理內(nèi)存管理模塊是如何初始化的,核心函數(shù)是OsVmPageStartup()。在講解之前,會(huì)先看下物理內(nèi)存初始化過(guò)程中的一些內(nèi)部函數(shù)。

2.1 物理內(nèi)存管理初始化內(nèi)部函數(shù)

2.1.1 函數(shù)OsVmPhysSegCreate

函數(shù)OsVmPhysSegCreate用于把指定的一個(gè)物理內(nèi)存區(qū)VmPhysArea轉(zhuǎn)換為物理內(nèi)存段LosVmPhysSeg。傳入的2個(gè)參數(shù)分別為物理內(nèi)存區(qū)的開(kāi)始內(nèi)存地址和大小。⑴處表示系統(tǒng)支持的物理內(nèi)存段的數(shù)量為32個(gè),超過(guò)則轉(zhuǎn)換錯(cuò)誤。⑵處從物理內(nèi)存段全局?jǐn)?shù)組g_vmPhysSeg中獲取一個(gè)可用的物理內(nèi)存段。⑶處如果物理內(nèi)存段seg為數(shù)組g_vmPhysSeg中的第一個(gè)元素,則跳過(guò)循環(huán)體直接執(zhí)行⑸設(shè)置物理內(nèi)存段的開(kāi)始地址和大小。如果不為第一個(gè)元素,并且前一個(gè)物理內(nèi)存段的開(kāi)始地址在要轉(zhuǎn)換的物理內(nèi)存段的結(jié)束地址之后,則執(zhí)行⑷處代碼覆蓋前一個(gè)物理內(nèi)存段。在配置物理內(nèi)存區(qū)的時(shí)候,需要注意這里的影響。

 

  1. STATIC INT32 OsVmPhysSegCreate(paddr_t start, size_t size
  2.     struct VmPhysSeg *seg = NULL
  3.  
  4. ⑴  if (g_vmPhysSegNum >= VM_PHYS_SEG_MAX) { 
  5.         return -1; 
  6.     } 
  7.  
  8. ⑵  seg = &g_vmPhysSeg[g_vmPhysSegNum++]; 
  9. ⑶  for (; (seg > g_vmPhysSeg) && ((seg - 1)->start > (start + size)); seg--) { 
  10. ⑷      *seg = *(seg - 1); 
  11.     } 
  12. ⑸  seg->start = start; 
  13.     seg->size = size
  14.  
  15.     return 0; 

函數(shù)OsVmPhysSegAdd調(diào)用上述函數(shù)OsVmPhysSegCreate依次把配置的多個(gè)物理內(nèi)存區(qū)一一進(jìn)行轉(zhuǎn)換,對(duì)于開(kāi)發(fā)板hispark_taurus只配置了一塊物理內(nèi)存區(qū)域。

 

  1. VOID OsVmPhysSegAdd(VOID) 
  2.     INT32 i, ret; 
  3.  
  4.     LOS_ASSERT(g_vmPhysSegNum < VM_PHYS_SEG_MAX); 
  5.  
  6.     for (i = 0; i < (sizeof(g_physArea) / sizeof(g_physArea[0])); i++) { 
  7.         ret = OsVmPhysSegCreate(g_physArea[i].start, g_physArea[i].size); 
  8.         if (ret != 0) { 
  9.             VM_ERR("create phys seg failed"); 
  10.         } 
  11.     } 

 

2.1.2 函數(shù)OsVmPhysInit

函數(shù)OsVmPhysInit繼續(xù)初始化物理內(nèi)存段信息。⑴處循環(huán)物理內(nèi)存段數(shù)組,這里不是循環(huán)32次,而是多少個(gè)物理段就循環(huán)遍歷多少次。遍歷到每一個(gè)物理內(nèi)存段,然后執(zhí)行⑵設(shè)置當(dāng)前物理內(nèi)存段的第一個(gè)物理頁(yè)結(jié)構(gòu)體的地址,每一個(gè)物理內(nèi)存頁(yè)都有自己的結(jié)構(gòu)體LosVmPage,這些結(jié)構(gòu)體維護(hù)在通過(guò)malloc內(nèi)存堆申請(qǐng)的g_vmPageArray數(shù)組里,后文會(huì)詳細(xì)講述。⑶處seg->size >> PAGE_SHIFT計(jì)算當(dāng)前內(nèi)存段對(duì)于的內(nèi)存頁(yè)數(shù)量,然后更新nPages,這是后續(xù)物理內(nèi)存段第一個(gè)內(nèi)存頁(yè)對(duì)應(yīng)的的物理內(nèi)存頁(yè)結(jié)構(gòu)體在數(shù)組g_vmPageArray中索引。⑷處開(kāi)始的函數(shù)OsVmPhysFreeListInit和OsVmPhysLruInit初始化伙伴雙向鏈表和LRU雙向鏈表,后續(xù)分析這2個(gè)函數(shù)。

 

  1. VOID OsVmPhysInit(VOID) 
  2.     struct VmPhysSeg *seg = NULL
  3.     UINT32 nPages = 0; 
  4.     int i; 
  5.  
  6.     for (i = 0; i < g_vmPhysSegNum; i++) { 
  7. ⑴      seg = &g_vmPhysSeg[i]; 
  8. ⑵      seg->pageBase = &g_vmPageArray[nPages]; 
  9. ⑶      nPages += seg->size >> PAGE_SHIFT; 
  10. ⑷      OsVmPhysFreeListInit(seg); 
  11.         OsVmPhysLruInit(seg); 
  12.     } 

 

2.1.3 函數(shù)OsVmPhysFreeListInit

每個(gè)物理內(nèi)存段使用9個(gè)空閑物理內(nèi)存頁(yè)節(jié)點(diǎn)鏈表來(lái)維護(hù)空閑物理內(nèi)存頁(yè)。OsVmPhysFreeListInit函數(shù)用于初始化指定物理內(nèi)存段的空閑物理內(nèi)存頁(yè)節(jié)點(diǎn)鏈表。操作前后需要開(kāi)啟、關(guān)閉空閑鏈表自旋鎖。⑴處遍歷空閑物理內(nèi)存頁(yè)節(jié)點(diǎn)鏈表數(shù)組,然后執(zhí)行⑵初始化每個(gè)雙向鏈表。⑶處把每個(gè)鏈表中的空閑物理內(nèi)存頁(yè)的數(shù)量初始化為0。

 

  1. STATIC INLINE VOID OsVmPhysFreeListInit(struct VmPhysSeg *seg) 
  2.     int i; 
  3.     UINT32 intSave; 
  4.     struct VmFreeList *list = NULL
  5.  
  6.     LOS_SpinInit(&seg->freeListLock); 
  7.  
  8.     LOS_SpinLockSave(&seg->freeListLock, &intSave); 
  9.     for (i = 0; i < VM_LIST_ORDER_MAX; i++) { 
  10. ⑴      list = &seg->freeList[i]; 
  11. ⑵      LOS_ListInit(&list->node); 
  12. ⑶      list->listCnt = 0; 
  13.     } 
  14.     LOS_SpinUnlockRestore(&seg->freeListLock, intSave); 

 

2.1.4 函數(shù)OsVmPhysLruInit

和上個(gè)函數(shù)類(lèi)似,函數(shù)OsVmPhysLruInit初始化指定物理內(nèi)存段的LRU鏈表數(shù)組中的LRU鏈表。LRU鏈表分五類(lèi),由枚舉類(lèi)型enum OsLruList定義。代碼較簡(jiǎn)單,讀者自行閱讀代碼即可。

 

  1. STATIC VOID OsVmPhysLruInit(struct VmPhysSeg *seg) 
  2.     INT32 i; 
  3.     UINT32 intSave; 
  4.     LOS_SpinInit(&seg->lruLock); 
  5.  
  6.     LOS_SpinLockSave(&seg->lruLock, &intSave); 
  7.     for (i = 0; i < VM_NR_LRU_LISTS; i++) { 
  8.         seg->lruSize[i] = 0; 
  9.         LOS_ListInit(&seg->lruList[i]); 
  10.     } 
  11.     LOS_SpinUnlockRestore(&seg->lruLock, intSave); 

 

2.1.5 函數(shù)OsVmPageInit

函數(shù)OsVmPageInit用于初始化物理內(nèi)存頁(yè)的初始值,該函數(shù)需要3個(gè)參數(shù),分別是物理內(nèi)存頁(yè)結(jié)構(gòu)體地址,物理內(nèi)存頁(yè)的開(kāi)始地址,物理內(nèi)存段編號(hào)。⑴處初始化內(nèi)存頁(yè)的鏈表節(jié)點(diǎn),這個(gè)鏈表節(jié)點(diǎn)通常會(huì)掛載在伙伴算法的空閑內(nèi)存頁(yè)節(jié)點(diǎn)鏈表上。⑵處設(shè)置內(nèi)存頁(yè)標(biāo)記為空閑內(nèi)存頁(yè)FILE_PAGE_FREE,該值由枚舉類(lèi)型enum OsPageFlags定義。⑶處設(shè)置內(nèi)存頁(yè)的引用計(jì)數(shù)為0。⑷處設(shè)置內(nèi)存頁(yè)的開(kāi)始地址。⑸處設(shè)置內(nèi)存頁(yè)所在的物理內(nèi)存段的編號(hào)。⑹處設(shè)置內(nèi)存頁(yè)順序order初始值,此時(shí)不屬于任何空閑內(nèi)存頁(yè)節(jié)點(diǎn)鏈表。⑺處設(shè)置內(nèi)存頁(yè)的nPages數(shù)值為0。⑻處的宏VMPAGEINIT調(diào)用函數(shù)OsVmPageInit并自動(dòng)增加內(nèi)存頁(yè)結(jié)構(gòu)體page地址和內(nèi)存頁(yè)pa地址。

 

  1. STATIC VOID OsVmPageInit(LosVmPage *page, paddr_t pa, UINT8 segID) 
  2. ⑴  LOS_ListInit(&page->node); 
  3. ⑵  page->flags = FILE_PAGE_FREE; 
  4. ⑶  LOS_AtomicSet(&page->refCounts, 0); 
  5. ⑷  page->physAddr = pa; 
  6. ⑸  page->segID = segID; 
  7. ⑹  page->order = VM_LIST_ORDER_MAX; 
  8. ⑺  page->nPages = 0; 
  9.  
  10. ... 
  11.      
  12. #define VMPAGEINIT(page, pa, segID) do {    \ 
  13. ⑻   OsVmPageInit(page, pa, segID);         \ 
  14.     (page)++;                               \ 
  15.     (pa) += PAGE_SIZE;                      \ 
  16. } while (0) 

 

2.2 物理內(nèi)存頁(yè)初始化函數(shù)VOID OsVmPageStartup(VOID)

了解上述幾個(gè)內(nèi)部函數(shù)后,我們正式開(kāi)始閱讀物理內(nèi)存頁(yè)初始化函數(shù)VOID OsVmPageStartup(VOID)。系統(tǒng)在啟動(dòng)時(shí),該函數(shù)用于初始化物理內(nèi)存,把物理內(nèi)存段劃分割為為物理內(nèi)存頁(yè)。該函數(shù)被kernel/base/vm/los_vm_boot.c中的UINT32 OsSysMemInit(VOID)調(diào)用,進(jìn)一步被文件platform/los_config.c中的INT32 OsMain(VOID)函數(shù)調(diào)用。下面詳細(xì)分析下函數(shù)的代碼。

⑴處的g_vmBootMemBase初始值為(UINTPTR)&__bss_end,表示系統(tǒng)可用內(nèi)存在bss段之后;ROUNDUP用于內(nèi)存向上對(duì)齊。函數(shù)OsVmPhysAreaSizeAdjust()用于調(diào)整物理區(qū)的開(kāi)始地址和大小。⑵處的 OsVmPhysPageNumGet()計(jì)算物理內(nèi)存段可以劃分多少物理內(nèi)存頁(yè),此行代碼重新計(jì)算物理內(nèi)存頁(yè)數(shù)目,此時(shí)每個(gè)物理頁(yè)對(duì)應(yīng)一個(gè)物理頁(yè)結(jié)構(gòu)體,相應(yīng)結(jié)構(gòu)體也占用內(nèi)存空間。 ⑶處計(jì)算物理頁(yè)結(jié)構(gòu)體數(shù)組的大小,數(shù)組的每個(gè)元素對(duì)應(yīng)每個(gè)物理頁(yè)結(jié)構(gòu)體LosVmPage。接下來(lái)一行調(diào)用函數(shù)OsVmBootMemAlloc為物理頁(yè)結(jié)構(gòu)體數(shù)組g_vmPageArray申請(qǐng)內(nèi)存空間,申請(qǐng)的內(nèi)存空間從地址g_vmBootMemBase截取指定的長(zhǎng)度。⑷處再次調(diào)用函數(shù)OsVmPhysAreaSizeAdjust()用于調(diào)整物理內(nèi)存區(qū)的開(kāi)始地址和大小,確?;趦?nèi)存頁(yè)對(duì)齊。⑸處調(diào)用函數(shù)OsVmPhysSegAdd()轉(zhuǎn)換為物理內(nèi)存段,⑹處調(diào)用OsVmPhysInit函數(shù)初始化物理內(nèi)存段的空閑物理內(nèi)存頁(yè)節(jié)點(diǎn)鏈表和LRU鏈表。上文分析過(guò)這幾個(gè)內(nèi)部函數(shù)。⑺處遍歷每個(gè)物理內(nèi)存段,獲取遍歷到的物理內(nèi)存段的總頁(yè)數(shù)nPage。⑻處為提升初始化物理內(nèi)存頁(yè)的性能,把頁(yè)數(shù)分為8份,count為每份的內(nèi)存頁(yè)的數(shù)目,left為等分為8份后剩余的內(nèi)存頁(yè)數(shù)。⑼處循環(huán)初始化物理內(nèi)存頁(yè),⑽處初始化剩余的物理內(nèi)存頁(yè)。⑾處的函數(shù)OsVmPageOrderListInit把物理內(nèi)存頁(yè)插入到空閑內(nèi)存頁(yè)節(jié)點(diǎn)鏈表,該函數(shù)進(jìn)一步調(diào)用OsVmPhysPagesFreeContiguous函數(shù),后續(xù)再分析該函數(shù)。初始化完成后,物理內(nèi)存段上的內(nèi)存頁(yè)都掛載到空閑內(nèi)存頁(yè)節(jié)點(diǎn)鏈表上了。

 

  1. VOID OsVmPageStartup(VOID) 
  2.     struct VmPhysSeg *seg = NULL
  3.     LosVmPage *page = NULL
  4.     paddr_t pa; 
  5.     UINT32 nPage; 
  6.     INT32 segID; 
  7.  
  8. ⑴  OsVmPhysAreaSizeAdjust(ROUNDUP((g_vmBootMemBase - KERNEL_ASPACE_BASE), PAGE_SIZE)); 
  9.  
  10.     /* 
  11.      * Pages getting from OsVmPhysPageNumGet() interface here contain the memory 
  12.      * struct LosVmPage occupied, which satisfies the equation: 
  13.      * nPage * sizeof(LosVmPage) + nPage * PAGE_SIZE = OsVmPhysPageNumGet() * PAGE_SIZE. 
  14.      */ 
  15. ⑵  nPage = OsVmPhysPageNumGet() * PAGE_SIZE / (sizeof(LosVmPage) + PAGE_SIZE); 
  16. ⑶  g_vmPageArraySize = nPage * sizeof(LosVmPage); 
  17.     g_vmPageArray = (LosVmPage *)OsVmBootMemAlloc(g_vmPageArraySize); 
  18.  
  19. ⑷  OsVmPhysAreaSizeAdjust(ROUNDUP(g_vmPageArraySize, PAGE_SIZE)); 
  20.  
  21. ⑸  OsVmPhysSegAdd(); 
  22. ⑹  OsVmPhysInit(); 
  23.  
  24.     for (segID = 0; segID < g_vmPhysSegNum; segID++) { 
  25. ⑺      seg = &g_vmPhysSeg[segID]; 
  26.         nPage = seg->size >> PAGE_SHIFT; 
  27. ⑻      UINT32 count = nPage >> 3; /* 3: 2 ^ 3, nPage / 8, cycle count */ 
  28.         UINT32 left = nPage & 0x7; /* 0x7: nPage % 8, left page */ 
  29.  
  30. ⑼      for (page = seg->pageBase, pa = seg->start; count > 0; count--) { 
  31.             /* note: process large amount of data, optimize performance */ 
  32.             VMPAGEINIT(page, pa, segID); 
  33.             VMPAGEINIT(page, pa, segID); 
  34.             VMPAGEINIT(page, pa, segID); 
  35.             VMPAGEINIT(page, pa, segID); 
  36.             VMPAGEINIT(page, pa, segID); 
  37.             VMPAGEINIT(page, pa, segID); 
  38.             VMPAGEINIT(page, pa, segID); 
  39.             VMPAGEINIT(page, pa, segID); 
  40.         } 
  41.         for (; left > 0; left--) { 
  42. ⑽          VMPAGEINIT(page, pa, segID); 
  43.         } 
  44. ⑾      OsVmPageOrderListInit(seg->pageBase, nPage); 
  45.     } 

3、物理內(nèi)存管理模塊接口

學(xué)習(xí)過(guò)物理內(nèi)存初始化后,接下來(lái)我們會(huì)分析物理內(nèi)存管理模塊的接口函數(shù),包含申請(qǐng)、釋放、查詢(xún)等功能接口。

3.1 申請(qǐng)物理內(nèi)存頁(yè)接口

3.1.1 申請(qǐng)物理內(nèi)存頁(yè)接口介紹

申請(qǐng)物理內(nèi)存頁(yè)的接口有3個(gè),分別用于滿足不同的申請(qǐng)需求。LOS_PhysPagesAllocContiguous函數(shù)的傳入?yún)?shù)為要申請(qǐng)物理內(nèi)存頁(yè)的數(shù)目,返回值為申請(qǐng)到的物理內(nèi)存頁(yè)對(duì)應(yīng)的內(nèi)核虛擬地址空間中的虛擬內(nèi)存地址。⑴處調(diào)用函數(shù)OsVmPhysPagesGet申請(qǐng)指定數(shù)目的物理內(nèi)存頁(yè),然后⑵處調(diào)用函數(shù)OsVmPageToVaddr轉(zhuǎn)換為內(nèi)核虛擬內(nèi)存地址。函數(shù)LOS_PhysPageAlloc申請(qǐng)一個(gè)物理內(nèi)存頁(yè),返回值為申請(qǐng)到的物理頁(yè)對(duì)應(yīng)的物理頁(yè)結(jié)構(gòu)體地址。代碼比較簡(jiǎn)單,見(jiàn)⑶處,調(diào)用函數(shù)OsVmPageToVaddr傳入ONE_PAGE參數(shù)申請(qǐng)1個(gè)物理內(nèi)存頁(yè)。函數(shù)LOS_PhysPagesAlloc用于申請(qǐng)nPages個(gè)物理內(nèi)存頁(yè),并掛在雙向鏈表list上,返回值為實(shí)際申請(qǐng)到的物理頁(yè)數(shù)目。⑷處循環(huán)調(diào)用函數(shù)OsVmPhysPagesGet()申請(qǐng)一個(gè)物理內(nèi)存頁(yè),如果申請(qǐng)成功不為空,則插入到雙向鏈表,申請(qǐng)成功的物理頁(yè)的數(shù)目加1;如果申請(qǐng)失敗則跳出循環(huán)。⑹返回實(shí)際申請(qǐng)到的物理頁(yè)的數(shù)目。

 

  1. VOID *LOS_PhysPagesAllocContiguous(size_t nPages) 
  2.     LosVmPage *page = NULL
  3.  
  4.     if (nPages == 0) { 
  5.         return NULL
  6.     } 
  7.  
  8. ⑴  page = OsVmPhysPagesGet(nPages); 
  9.     if (page == NULL) { 
  10.         return NULL
  11.     } 
  12.  
  13. ⑵   return OsVmPageToVaddr(page); 
  14. ...... 
  15.      
  16. LosVmPage *LOS_PhysPageAlloc(VOID) 
  17. ⑶   return OsVmPhysPagesGet(ONE_PAGE); 
  18.  
  19. size_t LOS_PhysPagesAlloc(size_t nPages, LOS_DL_LIST *list) 
  20.     LosVmPage *page = NULL
  21.     size_t count = 0; 
  22.  
  23.     if ((list == NULL) || (nPages == 0)) { 
  24.         return 0; 
  25.     } 
  26.  
  27.     while (nPages--) { 
  28. ⑷      page = OsVmPhysPagesGet(ONE_PAGE); 
  29.         if (page == NULL) { 
  30.             break; 
  31.         } 
  32. ⑸      LOS_ListTailInsert(list, &page->node); 
  33.         count++; 
  34.     } 
  35.  
  36. ⑹   return count

3.1.2 申請(qǐng)物理內(nèi)存頁(yè)內(nèi)部接口實(shí)現(xiàn)

3個(gè)內(nèi)存頁(yè)申請(qǐng)函數(shù)都調(diào)用了函數(shù)OsVmPhysPagesGet,下文會(huì)詳細(xì)分析申請(qǐng)物理內(nèi)存頁(yè)內(nèi)部接口實(shí)現(xiàn)。

3.1.2.1 函數(shù)OsVmPhysPagesGet

函數(shù)OsVmPhysPagesGet用于申請(qǐng)指定數(shù)量的物理內(nèi)存頁(yè),返回值為物理內(nèi)存頁(yè)結(jié)構(gòu)體地址。⑴處遍歷物理內(nèi)存段數(shù)組,對(duì)遍歷到的物理內(nèi)存段執(zhí)行⑵處代碼,調(diào)用函數(shù)OsVmPhysPagesAlloc()從指定的內(nèi)存段中申請(qǐng)指定數(shù)目的物理內(nèi)存頁(yè)。如果申請(qǐng)成功,則執(zhí)行⑶把內(nèi)存頁(yè)的引用計(jì)數(shù)初始化為0,根據(jù)注釋?zhuān)绻沁B續(xù)的內(nèi)存頁(yè),則第一個(gè)內(nèi)存頁(yè)持有引用計(jì)數(shù)數(shù)值。接下來(lái)以后更新內(nèi)存頁(yè)的數(shù)量,并返回申請(qǐng)到的內(nèi)存頁(yè)的結(jié)構(gòu)體地址;如果申請(qǐng)失敗則繼續(xù)循環(huán)申請(qǐng)或者返回NULL。

 

  1. STATIC LosVmPage *OsVmPhysPagesGet(size_t nPages) 
  2.     UINT32 intSave; 
  3.     struct VmPhysSeg *seg = NULL
  4.     LosVmPage *page = NULL
  5.     UINT32 segID; 
  6.  
  7.     for (segID = 0; segID < g_vmPhysSegNum; segID++) { 
  8. ⑴      seg = &g_vmPhysSeg[segID]; 
  9.         LOS_SpinLockSave(&seg->freeListLock, &intSave); 
  10. ⑵      page = OsVmPhysPagesAlloc(seg, nPages); 
  11.         if (page != NULL) { 
  12.             /* the first page of continuous physical addresses holds refCounts */ 
  13. ⑶          LOS_AtomicSet(&page->refCounts, 0); 
  14.             page->nPages = nPages; 
  15.             LOS_SpinUnlockRestore(&seg->freeListLock, intSave); 
  16.             return page; 
  17.         } 
  18.         LOS_SpinUnlockRestore(&seg->freeListLock, intSave); 
  19.     } 
  20.     return NULL

3.1.2.2 函數(shù)OsVmPhysPagesAlloc

從上文的介紹,我們知道物理內(nèi)存段包含一個(gè)空閑內(nèi)存頁(yè)節(jié)點(diǎn)鏈表數(shù)組,數(shù)組大小為9。數(shù)組中的每個(gè)鏈表上的內(nèi)存頁(yè)節(jié)點(diǎn)的大小等于2的冪次方個(gè)內(nèi)存頁(yè),例如:第0個(gè)鏈表上掛載的空閑內(nèi)存節(jié)點(diǎn)的大小為2的0次方個(gè)內(nèi)存頁(yè),即1個(gè)內(nèi)存頁(yè);第8個(gè)鏈表上掛載的內(nèi)存頁(yè)節(jié)點(diǎn)的大小為2的8次方個(gè)內(nèi)存頁(yè),即256個(gè)內(nèi)存頁(yè)。相同大小的內(nèi)存塊掛在同一個(gè)鏈表上進(jìn)行管理。

分析函數(shù)OsVmPhysPagesAlloc之前,先看下函數(shù)OsVmPagesToOrder,該函數(shù)根據(jù)指定的物理頁(yè)的數(shù)目計(jì)算屬于空閑內(nèi)存頁(yè)節(jié)點(diǎn)鏈表數(shù)組中的第幾個(gè)雙向鏈表。當(dāng)nPages為最小1時(shí),order取值為0;當(dāng)為2時(shí),order取值1…等于取底為2的對(duì)數(shù)Log2(nPages)。

 

  1. #define VM_ORDER_TO_PAGES(order) (1 << (order)) 
  2. ...... 
  3. UINT32 OsVmPagesToOrder(size_t nPages) 
  4.     UINT32 order
  5.  
  6.     for (order = 0; VM_ORDER_TO_PAGES(order) < nPages; order++); 
  7.  
  8.     return order

 

繼續(xù)分析下函數(shù)OsVmPhysPagesAlloc(),該函數(shù)基于傳入?yún)?shù)從指定的內(nèi)存段申請(qǐng)指定數(shù)目的內(nèi)存頁(yè)。⑴處調(diào)用的函數(shù)上文已經(jīng)講述,根據(jù)內(nèi)存頁(yè)數(shù)目計(jì)算出鏈表數(shù)組索引值。如果索引值小于鏈表最大索引值VM_LIST_ORDER_MAX,則執(zhí)行⑵從小內(nèi)存頁(yè)節(jié)點(diǎn)向大內(nèi)存頁(yè)節(jié)點(diǎn)循環(huán)各個(gè)雙向鏈表。⑶處獲取雙向鏈表,如果空閑鏈表為空則繼續(xù)循環(huán);如果不為空,則執(zhí)行⑷獲取鏈表上的空閑內(nèi)存頁(yè)結(jié)構(gòu)體。

如果根據(jù)內(nèi)存頁(yè)數(shù)計(jì)算出的數(shù)組索引值大于等于鏈表最大索引值VM_LIST_ORDER_MAX,說(shuō)明空閑鏈表上并沒(méi)有這么大塊的內(nèi)存頁(yè)節(jié)點(diǎn),需要從物理內(nèi)存段上申請(qǐng),需要執(zhí)行⑸調(diào)用函數(shù)OsVmPhysLargeAlloc()申請(qǐng)大的內(nèi)存頁(yè)。如果申請(qǐng)不到內(nèi)存頁(yè)則申請(qǐng)失敗,返回NULL;如果申請(qǐng)到合適的內(nèi)存頁(yè),則繼續(xù)執(zhí)行后續(xù)DONE標(biāo)簽代碼。這些代碼從空閑鏈表中刪除,拆分,多余的空閑內(nèi)存頁(yè)插入空閑鏈表等,后文繼續(xù)分析調(diào)用的這些函數(shù)。先看下這些參數(shù)的實(shí)際傳入?yún)?shù),order為要申請(qǐng)的內(nèi)存頁(yè)對(duì)應(yīng)的鏈表數(shù)組索引,newOrder為實(shí)際申請(qǐng)的內(nèi)存頁(yè)對(duì)應(yīng)的鏈表數(shù)組索引。⑹處的for循環(huán)條件中,&page[nPages]為需要申請(qǐng)的內(nèi)存頁(yè)結(jié)構(gòu)體的結(jié)束地址,&tmp[1 << newOrder]表示伙伴算法中空閑內(nèi)存頁(yè)節(jié)點(diǎn)鏈表上的內(nèi)存塊的結(jié)束地址。這里為啥使用for循環(huán)呢,上面申請(qǐng)內(nèi)存時(shí),應(yīng)該申請(qǐng)了多個(gè)內(nèi)存節(jié)點(diǎn)拼接起來(lái)了??聪垄颂幍暮瘮?shù)的傳入?yún)?shù),&page[nPages]為需要申請(qǐng)的內(nèi)存頁(yè)結(jié)構(gòu)體的結(jié)束地址,往后的部分被拆分放入空閑鏈表。(1 << min(order, newOrder))表示實(shí)際申請(qǐng)的內(nèi)存頁(yè)的數(shù)目。

 

  1. STATIC LosVmPage *OsVmPhysPagesAlloc(struct VmPhysSeg *seg, size_t nPages) 
  2.     struct VmFreeList *list = NULL
  3.     LosVmPage *page = NULL
  4.     LosVmPage *tmp = NULL
  5.     UINT32 order
  6.     UINT32 newOrder; 
  7.  
  8. ⑴  order = OsVmPagesToOrder(nPages); 
  9.     if (order < VM_LIST_ORDER_MAX) { 
  10. ⑵      for (newOrder = order; newOrder < VM_LIST_ORDER_MAX; newOrder++) { 
  11. ⑶          list = &seg->freeList[newOrder]; 
  12.             if (LOS_ListEmpty(&list->node)) { 
  13.                 continue
  14.             } 
  15. ⑷          page = LOS_DL_LIST_ENTRY(LOS_DL_LIST_FIRST(&list->node), LosVmPage, node); 
  16.             goto DONE; 
  17.         } 
  18.     } else { 
  19.         newOrder = VM_LIST_ORDER_MAX - 1; 
  20. ⑸      page = OsVmPhysLargeAlloc(seg, nPages); 
  21.         if (page != NULL) { 
  22.             goto DONE; 
  23.         } 
  24.     } 
  25.     return NULL
  26. DONE: 
  27.  
  28.     for (tmp = page; tmp < &page[nPages]; tmp = &tmp[1 << newOrder]) { 
  29. ⑹       OsVmPhysFreeListDelUnsafe(tmp); 
  30.     } 
  31.     OsVmPhysPagesSpiltUnsafe(page, order, newOrder); 
  32. ⑺  OsVmRecycleExtraPages(&page[nPages], nPages, ROUNDUP(nPages, (1 << min(order, newOrder)))); 
  33.  
  34.     return page; 

想了解更多內(nèi)容,請(qǐng)?jiān)L問(wèn):

51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)

https://harmonyos.51cto.com

 

責(zé)任編輯:jianghua 來(lái)源: 鴻蒙社區(qū)
相關(guān)推薦

2021-11-08 15:06:15

鴻蒙HarmonyOS應(yīng)用

2021-12-03 16:20:26

鴻蒙HarmonyOS應(yīng)用

2021-05-17 09:28:59

鴻蒙HarmonyOS應(yīng)用

2022-03-03 18:28:28

Harmony進(jìn)程任務(wù)管理模塊

2022-04-13 11:02:12

鴻蒙事件模塊事件Event

2022-03-11 20:23:14

鴻蒙源碼分析進(jìn)程管理

2022-03-31 16:26:49

鴻蒙源碼分析進(jìn)程管理

2021-05-21 09:25:11

鴻蒙HarmonyOS應(yīng)用

2022-01-10 15:31:44

鴻蒙HarmonyOS應(yīng)用

2022-01-12 10:50:23

鴻蒙HarmonyOS應(yīng)用

2021-06-04 09:57:49

鴻蒙HarmonyOS應(yīng)用

2021-06-04 14:15:10

鴻蒙HarmonyOS應(yīng)用

2021-05-08 15:14:50

鴻蒙HarmonyOS應(yīng)用

2021-05-25 09:28:34

鴻蒙HarmonyOS應(yīng)用

2021-10-20 16:08:57

鴻蒙HarmonyOS應(yīng)用

2022-04-13 11:12:43

鴻蒙輕內(nèi)核信號(hào)量模塊操作系統(tǒng)

2021-05-10 15:05:56

鴻蒙HarmonyOS應(yīng)用

2021-12-01 15:59:22

鴻蒙HarmonyOS應(yīng)用

2021-05-31 20:30:55

鴻蒙HarmonyOS應(yīng)用

2021-06-17 09:36:07

鴻蒙HarmonyOS應(yīng)用
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)