女人自慰AV免费观看内涵网,日韩国产剧情在线观看网址,神马电影网特片网,最新一级电影欧美,在线观看亚洲欧美日韩,黄色视频在线播放免费观看,ABO涨奶期羡澄,第一导航fulione,美女主播操b

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

鴻蒙內核如何初始化物理內存?

鴻蒙系統HarmonyOS ? 來源:my.oschina ? 作者:鴻蒙內核源碼分析 ? 2021-04-25 15:05 ? 次閱讀

如何初始化物理內存?

鴻蒙內核物理內存采用了段頁式管理,先看兩個主要結構體.結構體的每個成員變量的含義都已經注解出來,請結合源碼理解.

#define VM_LIST_ORDER_MAX  9 //伙伴算法分組數量,從 2^0,2^1,...,2^8 (256*4K)=1M
#define VM_PHYS_SEG_MAX  32 //最大支持32個段

typedef struct VmPhysSeg {//物理段描述符
    PADDR_T start;            /* The start of physical memory area */ //物理內存段的開始地址
    size_t size;              /* The size of physical memory area */ //物理內存段的大小
    LosVmPage *pageBase;      /* The first page address of this area */ //本段首個物理頁框地址
    SPIN_LOCK_S freeListLock; /* The buddy list spinlock */    //伙伴算法自旋鎖,用于操作freeList上鎖
    struct VmFreeList freeList[VM_LIST_ORDER_MAX];  /* The free pages in the buddy list */ //伙伴算法的分組,默認分成10組 2^0,2^1,...,2^VM_LIST_ORDER_MAX
    SPIN_LOCK_S lruLock;  //用于置換的自旋鎖,用于操作lruList
    size_t lruSize[VM_NR_LRU_LISTS];  //5個雙循環鏈表大小,如此方便得到size
    LOS_DL_LIST lruList[VM_NR_LRU_LISTS]; //頁面置換算法,5個雙循環鏈表頭,它們分別描述五中不同類型的鏈表
} LosVmPhysSeg;


//注意: vmPage 中并沒有虛擬地址,只有物理地址
typedef struct VmPage { //物理頁框描述符
    LOS_DL_LIST         node;        /**< vm object dl list */ //虛擬內存節點,通過它掛/摘到全局g_vmPhysSeg[segID]->freeList[order]物理頁框鏈表上
    UINT32              index;       /**< vm page index to vm object */ //索引位置
    PADDR_T             physAddr;    /**< vm page physical addr */  //物理頁框起始物理地址,只能用于計算,不會用于操作(讀/寫數據==)
    Atomic              refCounts;   /**< vm page ref count */   //被引用次數,共享內存會被多次引用
    UINT32              flags;       /**< vm page flags */    //頁標簽,同時可以有多個標簽(共享/引用/活動/被鎖==)
    UINT8               order;       /**< vm page in which order list */ //被安置在伙伴算法的幾號序列(              2^0,2^1,2^2,...,2^order)
    UINT8               segID;       /**< the segment id of vm page */ //所屬段ID
    UINT16              nPages;      /**< the vm page is used for kernel heap */ //分配頁數,標識從本頁開始連續的幾頁將一塊被分配
} LosVmPage;//注意:關于nPages和order的關系說明,當請求分配為5頁時,order是等于3的,因為只有2^3才能滿足5頁的請求

理解它們是理解物理內存管理的關鍵,尤其是 **LosVmPage ,**鴻蒙內存模塊代碼通篇都能看到它的影子.內核默認最大允許管理32個段.

段頁式管理簡單說就是先將物理內存切成一段段,每段再切成單位為 4K的物理頁框,頁是在內核層的操作單元,物理內存的分配,置換,缺頁,內存共享,文件高速緩存的讀寫,都是以頁為單位的,所以LosVmPage 很重要,很重要!

結構體的每個變量代表了一個個的功能點,結構體中頻繁了出現LOS_DL_LIST的身影,雙向鏈表是鴻蒙內核最重要的結構體,在系列篇開篇就專門講過它的重要性.

再比如LosVmPage.refCounts頁被引用的次數,可理解被進程擁有的次數,當refCounts大于1時,被多個進程所擁有,說明這頁就是共享頁.當等于0時,說明沒有進程在使用了,這時就可以被釋放了.

看到這里熟悉JAVA的同學是不是似曾相識,這像是Java的內存回收機制.在內核層面,引用的概念不僅僅適用于內存模塊,也適用于其他模塊,比如文件/設備模塊,同樣都存在共享的場景.這些模塊不在這里展開說,后續有專門的章節細講.

段一開始是怎么劃分的 ? 需要方案提供商手動配置,存在靜態的全局變量中,鴻蒙默認只配置了一段.

struct VmPhysSeg g_vmPhysSeg[VM_PHYS_SEG_MAX];//物理段數組,最大32段
INT32 g_vmPhysSegNum = 0; //總段數
LosVmPage *g_vmPageArray = NULL;//物理頁框數組
size_t g_vmPageArraySize;//總物理頁框數


/* Physical memory area array */
STATIC struct VmPhysArea g_physArea[] = {//這里只有一個區域,即只生成一個段
    {
        .start = SYS_MEM_BASE, //整個物理內存基地址,#define SYS_MEM_BASE            DDR_MEM_ADDR ,  0x80000000
        .size = SYS_MEM_SIZE_DEFAULT,//整個物理內存總大小 0x07f00000
    },
};

有了段和這些全局變量,就可以對內存初始化了. OsVmPageStartup是對物理內存的初始化,它被整個系統內存初始化 OsSysMemInit所調用. 直接上代碼.

/******************************************************************************
 完成對物理內存整體初始化,本函數一定運行在實模式下
 1.申請大塊內存g_vmPageArray存放LosVmPage,按4K一頁劃分物理內存存放在數組中.
******************************************************************************/
VOID OsVmPageStartup(VOID)
{
    struct VmPhysSeg *seg = NULL;
    LosVmPage *page = NULL;
    paddr_t pa;
    UINT32 nPage;
    INT32 segID;

    OsVmPhysAreaSizeAdjust(ROUNDUP((g_vmBootMemBase - KERNEL_ASPACE_BASE), PAGE_SIZE));//校正 g_physArea size

    nPage = OsVmPhysPageNumGet();//得到 g_physArea 總頁數
    g_vmPageArraySize = nPage * sizeof(LosVmPage);//頁表總大小
    g_vmPageArray = (LosVmPage *)OsVmBootMemAlloc(g_vmPageArraySize);//實模式下申請內存,此時還沒有初始化MMU

    OsVmPhysAreaSizeAdjust(ROUNDUP(g_vmPageArraySize, PAGE_SIZE));//

    OsVmPhysSegAdd();// 完成對段的初始化
    OsVmPhysInit();// 加入空閑鏈表和設置置換算法,LRU(最近最久未使用)算法

    for (segID = 0; segID < g_vmPhysSegNum; segID++) {//遍歷物理段,將段切成一頁一頁
        seg = &g_vmPhysSeg[segID];
        nPage = seg->size >> PAGE_SHIFT;//本段總頁數
        for (page = seg->pageBase, pa = seg->start; page <= seg->pageBase + nPage;//遍歷,算出每個頁框的物理地址
             page++, pa += PAGE_SIZE) {
            OsVmPageInit(page, pa, segID);//對物理頁框進行初始化,注意每頁的物理地址都不一樣
        }
        OsVmPageOrderListInit(seg->pageBase, nPage);//伙伴算法初始化,將所有頁加入空閑鏈表供分配
    }
}

結合中文注釋,代碼很好理解,此番操作之后全局變量里的值就都各就各位了,可以開始工作了.

如何分配/回收物理內存?答案是伙伴算法

伙伴算法系列篇中有說過好幾篇,這里再看圖理解下什么伙伴算法,伙伴算法注重物理內存的連續性,注意是連續性!

pIYBAGCFFSWAesbmAADiOxzImKk196.png

?結合圖比如,要分配4(2^2)頁(16k)的內存空間,算法會先從free_area2中查看free鏈表是否為空,如果有空閑塊,則從中分配,如果沒有空閑塊,就從它的上一級free_area3(每塊32K)中分配出16K,并將多余的內存(16K)加入到free_area2中去。如果free_area3也沒有空閑,則從更上一級申請空間,依次遞推,直到free_area max_order,如果頂級都沒有空間,那么就報告分配失敗。

釋放是申請的逆過程,當釋放一個內存塊時,先在其對于的free_area鏈表中查找是否有伙伴存在,如果沒有伙伴塊,直接將釋放的塊插入鏈表頭。如果有或板塊的存在,則將其從鏈表摘下,合并成一個大塊,然后繼續查找合并后的塊在更大一級鏈表中是否有伙伴的存在,直至不能合并或者已經合并至最大塊2^max_order為止。

看過系列篇文章的可能都發現了,筆者喜歡用講故事和打比方來說明內核運作機制,為了更好的理解,同樣打個比方,筆者認為伙伴算法很像是賣標準豬肉塊的算法.

物理內存是一整頭豬,已經切成了1斤1斤的了,但是還都連在一起,每一斤上都貼了個標號, 而且老板只按 1斤(2^0), 2斤(2^1), 4斤(2^2),...256斤(2^8)的方式來賣.售貨柜上分成了9組

張三來了要7斤豬肉,怎么辦?**給8斤,注意是給8斤啊 ,因為它要嚴格按它的標準來賣.**張三如果歸還了,查看現有8斤組里有沒有序號能連在一塊的,有的話2個8斤合成16斤,放到16斤組里去.如果沒有這8斤豬肉將掛到上圖中第2組(2^3)再賣.

大家腦海中有畫面了嗎? 那么問題來了,它為什么要這么賣豬肉,好處是什么? 簡單啊:至少兩個好處:

第一:賣肉速度快,效率高,標準化的東西最好賣了.

第二:可防止碎肉太多,后面的人想買大塊的豬肉買不到了.請仔細想想是不是這樣的?如果每次客戶來了要多少就割多少出去,運行一段時候后你還能買到10斤連在一塊的豬肉嗎?很可能給是一包碎肉,里面甚至還有一兩一兩的邊角肉,碎肉的結果必然是管理麻煩,效率低啊.如果按伙伴算法的結果是運行一段時候后,圖中0,1,2各組中都有可賣的豬肉啊,張三哥歸還了那8斤(其實他指向要7斤)豬肉,王五兄弟來了要6斤,直接把張三哥歸還的給王五就行了.效率極高.

那么問題又來了,凡事總有兩面性,它的壞處是什么?也簡單啊 :至少兩個壞處:

第一:浪費了!,白給的三斤對王五沒用啊,浪費的問題有其他辦法解決,但不是在這個層面去解決,而是由slab分配器解決,這里不重點說后續會專門講slab分配器是如何解決這個問題的.

第二:合并要求太嚴格了,一定得是伙伴(連續)才能合并成更大的塊.這樣也會導致時間久了很難有大塊的連續性的豬肉塊.

比方打完了,鴻蒙內核是如何實現賣肉算法的呢?請看代碼

LosVmPage *OsVmPhysPagesAlloc(struct VmPhysSeg *seg, size_t nPages)
{
    struct VmFreeList *list = NULL;
    LosVmPage *page = NULL;
    UINT32 order;
    UINT32 newOrder;

    if ((seg == NULL) || (nPages == 0)) {
        return NULL;
    }
 //因為伙伴算法分配單元是 1,2,4,8 頁,比如nPages = 3時,就需要從 4號空閑鏈表中分,剩余的1頁需要劈開放到1號空閑鏈表中
    order = OsVmPagesToOrder(nPages);//根據頁數計算出用哪個塊組
    if (order < VM_LIST_ORDER_MAX) {//order不能大于9 即:256*4K = 1M 可理解為向內核堆申請內存一次不能超過1M
        for (newOrder = order; newOrder < VM_LIST_ORDER_MAX; newOrder++) {//沒有就找更大塊
            list = &seg->freeList[newOrder];//從最合適的塊處開始找
            if (LOS_ListEmpty(&list->node)) {//理想情況鏈表為空,說明沒找到
                continue;//繼續找更大塊的
            }
            page = LOS_DL_LIST_ENTRY(LOS_DL_LIST_FIRST(&list->node), LosVmPage, node);//找第一個節點就行,因為鏈表上掛的都是同樣大小物理頁框
            goto DONE;
        }
    }
    return NULL;
DONE:
    OsVmPhysFreeListDelUnsafe(page);//將物理頁框從鏈表上摘出來
    OsVmPhysPagesSpiltUnsafe(page, order, newOrder);//將物理頁框劈開,把用不了的頁再掛到對應的空閑鏈表上
    return page;
}

/******************************************************************************
 本函數很像賣豬肉的,拿一大塊肉剁,先把多余的放回到小塊肉堆里去.
 oldOrder:原本要買 2^2肉
 newOrder:卻找到個 2^8肉塊
******************************************************************************/
STATIC VOID OsVmPhysPagesSpiltUnsafe(LosVmPage *page, UINT8 oldOrder, UINT8 newOrder)
{
    UINT32 order;
    LosVmPage *buddyPage = NULL;

    for (order = newOrder; order > oldOrder;) {//把肉剁碎的過程,把多余的肉塊切成2^7,2^6...標準塊,
        order--;//越切越小,逐一掛到對應的空閑鏈表上
        buddyPage = &page[VM_ORDER_TO_PAGES(order)];//@note_good 先把多余的肉割出來,這句代碼很贊!因為LosVmPage本身是在一個大數組上,page[nPages]可直接定位
        LOS_ASSERT(buddyPage->order == VM_LIST_ORDER_MAX);//沒掛到伙伴算法對應組塊空閑鏈表上的物理頁框的order必須是VM_LIST_ORDER_MAX
        OsVmPhysFreeListAddUnsafe(buddyPage, order);//將劈開的節點掛到對應序號的鏈表上,buddyPage->order = order
    }
}

為了方便理解代碼細節,這里說一種情況:比如三哥要買3斤的,發現4斤,8斤的都沒有了,只有16斤的怎么辦?注意不會給16斤,只會給4斤.這時需要把肉劈開,劈成 8,4,4,其中4斤給張三哥,將剩下的8斤,4斤掛到對應鏈表上. OsVmPhysPagesSpiltUnsafe 干的就是劈豬肉的活.

伙伴算法的鏈表是怎么初始化的,再看段代碼

//初始化空閑鏈表,分配物理頁框使用伙伴算法
STATIC INLINE VOID OsVmPhysFreeListInit(struct VmPhysSeg *seg)
{
    int i;
    UINT32 intSave;
    struct VmFreeList *list = NULL;

    LOS_SpinInit(&seg->freeListLock);//初始化用于分配的自旋鎖

    LOS_SpinLockSave(&seg->freeListLock, &intSave);
    for (i = 0; i < VM_LIST_ORDER_MAX; i++) {//遍歷伙伴算法空閑塊組鏈表
        list = &seg->freeList[i]; //一個個來
        LOS_ListInit(&list->node); //LosVmPage.node將掛到list->node上
        list->listCnt = 0;   //鏈表上的數量默認0
    }
    LOS_SpinUnlockRestore(&seg->freeListLock, intSave);
}

鴻蒙是面向未來設計的系統,高瞻遠矚,格局遠大,設計精良, 海量知識點,對內核源碼加上中文注解已有三個多月,越深入精讀內核源碼,越能感受到設計者的精巧用心,創新突破, 向開發者致敬. 可以毫不夸張的說鴻蒙內核源碼可作為大學C語言,數據結構,操作系統,匯編語言 四門課程的教學項目.如此寶庫,不深入研究實在是暴殄天物,于心不忍.

編輯:hfy

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 鴻蒙系統
    +關注

    關注

    183

    文章

    2639

    瀏覽量

    67691
收藏 人收藏

    評論

    相關推薦
    熱點推薦

    【HarmonyOS】鴻蒙內核源碼分析(內存管理篇)

    詳見:../kernel/base/vm有了上篇鴻蒙內核源碼分析(內存概念篇)的基礎,本篇講內存管理部分,本章源碼超級多,很燒腦,但筆者關鍵處都加了注釋。廢話不多說,開始吧。目錄
    發表于 10-14 12:05

    鴻蒙內核源碼分析(內存分配篇):內存的分配方式有哪些

    分析(內存映射篇)中說明,在調度算法切換進程時就需要切換至該進程自己的虛擬空間,即MMU上下文。物理內存初始化物理
    發表于 11-20 10:07

    鴻蒙內核源碼分析(內存管理篇):虛擬內存物理內存是怎么管理的

    有了上篇鴻蒙內核源碼分析(內存概念篇)的基礎,本篇講內存管理部分,本章源碼超級多,很燒腦,但筆者關鍵處都加了注釋。廢話不多說,開始吧。初始化
    發表于 11-20 10:54

    鴻蒙內核源碼分析(內存管理篇):虛擬內存物理內存是怎么管理的

    有了上篇鴻蒙內核源碼分析(內存概念篇)的基礎,本篇講內存管理部分,本章源碼超級多,很燒腦,但筆者關鍵處都加了注釋。廢話不多說,開始吧。初始化
    發表于 11-20 16:48

    鴻蒙內核源碼分析(內存分配篇):內存的分配方式有哪些

    ; 開發指南> 內核開發指南> 內存> 概述 看,有更詳細的描述,這里結合代碼說。Huawei LiteOS的內存管理分為靜態內存管理和動態內存
    發表于 11-20 17:34

    LINUX系統引導和初始化-LINUX內核解讀

    Linux 的系統引導和初始化 ----------Linux2.4.22內核解讀之一 一、 系統引導和初始化概述 相關代碼(引導扇區的程序及其輔助程序,以 x86體系為例): \linux-2.4.22\arch\i386\b
    發表于 11-03 22:31 ?53次下載

    Linux內存初始化

    之前有幾篇博客詳細介紹了Xen的內存初始化,確實感覺這部分內容蠻復雜的。這兩天在看Linux內核啟動中內存初始化,也是看的云里霧里的,想嘗
    發表于 10-12 11:16 ?0次下載

    解析內核初始化時根內存盤的加載過程

    內存盤中作為根盤。 當同時配置了初始化內存盤(Initail RAM Disk)時, 內核初始化時可以在安裝主盤之前, 通過引導程序所加
    發表于 11-08 10:40 ?0次下載

    uboot和內核里phy的初始化_內核里的雙網絡配置及phy的初始化

    uboot 和內核里 phy 的初始化,以及內核里的雙網絡配置及 phy 的初始化。 本文以盈鵬飛嵌入式的CoM-335x(基于AM335x)核心板及網絡芯片LAN8720 為例,說明
    的頭像 發表于 05-17 08:19 ?1.2w次閱讀

    Linux內核初始化過程中的調用順序

    所有的__init函數在區段.initcall.init中還保存了一份函數指針,在初始化內核會通過這些函數指針調用這些__init函數指針,并在整個初始化完成后,釋放整個init區段(包括.init.text,.initcal
    發表于 05-12 08:40 ?1742次閱讀

    UCOS2系統內核講述(五) _初始化TCB詳情

    UCOS2系統內核講述(五)_初始化TCB詳情
    的頭像 發表于 03-25 09:39 ?2447次閱讀
    UCOS2系統<b class='flag-5'>內核</b>講述(五) _<b class='flag-5'>初始化</b>TCB詳情

    UCOS2系統內核講述(二)_ 初始化調用函數

    UCOS2系統內核講述(二)_初始化調用函數
    的頭像 發表于 03-25 09:57 ?1981次閱讀
    UCOS2系統<b class='flag-5'>內核</b>講述(二)_ <b class='flag-5'>初始化</b>調用函數

    鴻蒙內核源碼:內核空間是怎么初始化的?

    data段 該段用于存儲初始化的全局變量,初始化為0的全局變量出于編譯優化的策略還是被保存在BSS段。
    的頭像 發表于 04-26 14:43 ?2086次閱讀
    <b class='flag-5'>鴻蒙</b><b class='flag-5'>內核</b>源碼:<b class='flag-5'>內核</b>空間是怎么<b class='flag-5'>初始化</b>的?

    Armlinux內核移植及系統初始化過程分析

    Armlinux內核移植及系統初始化過程分析說明。
    發表于 04-06 15:53 ?11次下載

    Linux內存方面的初始化和常見的內存分配方式

    在 start_kernel 內核初始化函數中,一共調用 86 個函數去初始化,其中有一個 mm_init 函數,用以初始化內存。 star
    的頭像 發表于 09-28 16:13 ?1019次閱讀
    Linux<b class='flag-5'>內存</b>方面的<b class='flag-5'>初始化</b>和常見的<b class='flag-5'>內存</b>分配方式