提交 f1ae720c 编写于 作者: 鸿蒙内核源码分析's avatar 鸿蒙内核源码分析

注解物理内存是如何管理的?伙伴算法/LRU如何分配/置换物理页框?

搜索 @note_pic 可以查看全部字符图
搜索 @note_why 是注者尚未看明白的地方,如果您看明白了,请告诉注者完善
搜索 @note_thinking 是注者的思考和吐槽的地方
搜索 @note_#if0 是由第三方项目提供不由内核源码中定义的极为重要的结构体,为方便理解而添加的。
上级 0c6c35fa
......@@ -42,16 +42,20 @@
extern "C" {
#endif /* __cplusplus */
#endif /* __cplusplus */
/******************************************************************************
虚拟内存体现的是程序对内存资源的需求,而物理内存是对该请求的供应。
伙伴算法的思想是:把内存中连续的空闲页框空间看成是空闲页框块,并按照它们的大小(连续页框的数目)分组
******************************************************************************/
//注意: vmPage 中并没有虚拟地址,只有物理地址
typedef struct VmPage { //虚拟内存描述符
LOS_DL_LIST node; /**< vm object dl list */ //虚拟内存节点,通过它挂到全局虚拟页
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 */ //物理地址
PADDR_T physAddr; /**< vm page physical addr */ //物理页框起始物理地址,只能用于计算,不会用于操作(读/写数据==)
Atomic refCounts; /**< vm page ref count */ //被引用次数,共享内存会被多次引用
UINT32 flags; /**< vm page flags */ //页标签,(共享/引用/活动/被锁==)
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 */ //vm页面用于内核堆
UINT8 segID; /**< the segment id of vm page */ //所段ID
UINT16 nPages; /**< the vm page is used for kernel heap */ //页总数,页大小4K的倍数
} LosVmPage;
extern LosVmPage *g_vmPageArray;//物理内存所有页框(page frame)记录数组
......
......@@ -43,8 +43,13 @@ extern "C" {
#endif /* __cplusplus */
#endif /* __cplusplus */
#define VM_LIST_ORDER_MAX 9
#define VM_PHYS_SEG_MAX 32
/******************************************************************************
LRU是Least Recently Used的缩写,即最近最少使用页面置换算法,是为虚拟页式存储管理服务的,
是根据页面调入内存后的使用情况进行决策了。由于无法预测各页面将来的使用情况,只能利用
“最近的过去”作为“最近的将来”的近似,因此,LRU算法就是将最近最久未使用的页面予以淘汰。
******************************************************************************/
#define VM_LIST_ORDER_MAX 9 //伙伴算法分组,即物理内存层面一次最大分配 2^8 * 4K = 1M
#define VM_PHYS_SEG_MAX 32 //最大支持32个段
#ifndef min
#define min(x, y) ((x) < (y) ? (x) : (y))
......@@ -69,20 +74,20 @@ enum OsLruList {//Lru全称是Least Recently Used,即最近最久未使用的
VM_NR_LRU_LISTS
};
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 */
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 */
struct VmFreeList freeList[VM_LIST_ORDER_MAX]; /* The free pages in the buddy list */
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;//置换锁
size_t lruSize[VM_NR_LRU_LISTS]; //5个双循环链表大小,如此方便得到size
LOS_DL_LIST lruList[VM_NR_LRU_LISTS]; //5个双循环链表头,它们分别描述五中不同类型的链表
LOS_DL_LIST lruList[VM_NR_LRU_LISTS]; //页面置换算法,5个双循环链表头,它们分别描述五中不同类型的链表
} LosVmPhysSeg;
struct VmPhysArea {
struct VmPhysArea {//物理区描述,仅用于方案商区划分
PADDR_T start;
size_t size;
};
......
/*
* Copyright (c) 2013-2019, Huawei Technologies Co., Ltd. All rights reserved.
* Copyright (c) 2020, Huawei Device Co., Ltd. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "los_vm_page.h"
#include "los_vm_common.h"
#include "los_vm_phys.h"
#include "los_vm_boot.h"
#include "los_vm_filemap.h"
#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif /* __cplusplus */
#endif /* __cplusplus */
LosVmPage *g_vmPageArray = NULL;
size_t g_vmPageArraySize;
//虚拟页初始化
STATIC VOID OsVmPageInit(LosVmPage *page, paddr_t pa, UINT8 segID)
{
LOS_ListInit(&page->node); //页节点初始化
page->flags = FILE_PAGE_FREE; //映射文件初始标识
LOS_AtomicSet(&page->refCounts, 0); //引用次数0
page->physAddr = pa; //物理地址
page->segID = segID; //物理地址使用段管理,段ID
page->order = VM_LIST_ORDER_MAX; //所属伙伴算法块组
}
STATIC INLINE VOID OsVmPageOrderListInit(LosVmPage *page, size_t nPages)
{
OsVmPhysPagesFreeContiguous(page, nPages);//释放页面使可用于伙伴算法分配
}
// page初始化
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);//申请页表存放区域
OsVmPhysAreaSizeAdjust(ROUNDUP(g_vmPageArraySize, PAGE_SIZE));// g_physArea 变小
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);//page初始化
}
OsVmPageOrderListInit(seg->pageBase, nPage);// 页面分配的排序
}
}
//通过物理地址获取页框
LosVmPage *LOS_VmPageGet(PADDR_T paddr)
{
INT32 segID;
LosVmPage *page = NULL;
for (segID = 0; segID < g_vmPhysSegNum; segID++) {//物理内存采用段页管理
page = OsVmPhysToPage(paddr, segID);//通过物理地址和段ID找出物理页框
if (page != NULL) {
break;
}
}
return page;
}
#ifdef __cplusplus
#if __cplusplus
}
#endif /* __cplusplus */
#endif /* __cplusplus */
/*
* Copyright (c) 2013-2019, Huawei Technologies Co., Ltd. All rights reserved.
* Copyright (c) 2020, Huawei Device Co., Ltd. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "los_vm_page.h"
#include "los_vm_common.h"
#include "los_vm_phys.h"
#include "los_vm_boot.h"
#include "los_vm_filemap.h"
#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif /* __cplusplus */
#endif /* __cplusplus */
LosVmPage *g_vmPageArray = NULL;
size_t g_vmPageArraySize;
//虚拟页初始化
STATIC VOID OsVmPageInit(LosVmPage *page, paddr_t pa, UINT8 segID)
{
LOS_ListInit(&page->node); //页节点初始化
page->flags = FILE_PAGE_FREE; //页标签,初始为空闲页
LOS_AtomicSet(&page->refCounts, 0); //引用次数0
page->physAddr = pa; //物理地址
page->segID = segID; //物理地址使用段管理,段ID
page->order = VM_LIST_ORDER_MAX; //所属伙伴算法块组
}
STATIC INLINE VOID OsVmPageOrderListInit(LosVmPage *page, size_t nPages)
{
OsVmPhysPagesFreeContiguous(page, nPages);//释放页面使可用于伙伴算法分配
}
// page初始化
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);//申请页表存放区域
OsVmPhysAreaSizeAdjust(ROUNDUP(g_vmPageArraySize, PAGE_SIZE));// g_physArea 变小
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);//page初始化
}
OsVmPageOrderListInit(seg->pageBase, nPage);// 页面分配的排序
}
}
//通过物理地址获取页框
LosVmPage *LOS_VmPageGet(PADDR_T paddr)
{
INT32 segID;
LosVmPage *page = NULL;
for (segID = 0; segID < g_vmPhysSegNum; segID++) {//物理内存采用段页管理
page = OsVmPhysToPage(paddr, segID);//通过物理地址和段ID找出物理页框
if (page != NULL) {
break;
}
}
return page;
}
#ifdef __cplusplus
#if __cplusplus
}
#endif /* __cplusplus */
#endif /* __cplusplus */
......@@ -41,14 +41,17 @@
extern "C" {
#endif /* __cplusplus */
#endif /* __cplusplus */
/******************************************************************************
鸿蒙物理内存采用段页式管理
******************************************************************************/
#define ONE_PAGE 1
/* Physical memory area array */
STATIC struct VmPhysArea g_physArea[] = {//这里只整了一个区域,即只生成一个段,鸿蒙物理内存采用段页式管理
STATIC struct VmPhysArea g_physArea[] = {//这里只有一个区域,即只生成一个段
{
.start = SYS_MEM_BASE, //整个物理内存基地址
.size = SYS_MEM_SIZE_DEFAULT,//整个物理内存总大小
.start = SYS_MEM_BASE, //整个物理内存基地址,#define SYS_MEM_BASE DDR_MEM_ADDR , 0x80000000
.size = SYS_MEM_SIZE_DEFAULT,//整个物理内存总大小 0x07f00000
},
};
......@@ -59,21 +62,21 @@ LosVmPhysSeg *OsGVmPhysSegGet()
{
return g_vmPhysSeg;
}
//初始化Lru置换 LRU list 在 VmPhysSeg中管理
//初始化Lru置换链表
STATIC VOID OsVmPhysLruInit(struct VmPhysSeg *seg)
{
INT32 i;
UINT32 intSave;
LOS_SpinInit(&seg->lruLock);//自旋锁,自旋锁用户CPU多核同步
LOS_SpinInit(&seg->lruLock);//初始化自旋锁,自旋锁用于CPU多核同步
LOS_SpinLockSave(&seg->lruLock, &intSave);
for (i = 0; i < VM_NR_LRU_LISTS; i++) { //五个双循环链表
seg->lruSize[i] = 0; //记录链表节点数
LOS_ListInit(&seg->lruList[i]);//初始化LRU链表
seg->lruSize[i] = 0; //记录链表节点数
LOS_ListInit(&seg->lruList[i]); //初始化LRU链表
}
LOS_SpinUnlockRestore(&seg->lruLock, intSave);
}
//创建物理段
//创建物理段,由区划分转成段管理
STATIC INT32 OsVmPhysSegCreate(paddr_t start, size_t size)
{
struct VmPhysSeg *seg = NULL;
......@@ -82,7 +85,7 @@ STATIC INT32 OsVmPhysSegCreate(paddr_t start, size_t size)
return -1;
}
seg = &g_vmPhysSeg[g_vmPhysSegNum++];
seg = &g_vmPhysSeg[g_vmPhysSegNum++];//拿到一段数据
for (; (seg > g_vmPhysSeg) && ((seg - 1)->start > (start + size)); seg--) {
*seg = *(seg - 1);
}
......@@ -91,15 +94,15 @@ STATIC INT32 OsVmPhysSegCreate(paddr_t start, size_t size)
return 0;
}
//添加物理段
VOID OsVmPhysSegAdd(VOID)
{
INT32 i, ret;
LOS_ASSERT(g_vmPhysSegNum <= VM_PHYS_SEG_MAX);
for (i = 0; i < (sizeof(g_physArea) / sizeof(g_physArea[0])); i++) {//将g_physArea转化成段
ret = OsVmPhysSegCreate(g_physArea[i].start, g_physArea[i].size);//一个区对应一个段
for (i = 0; i < (sizeof(g_physArea) / sizeof(g_physArea[0])); i++) {//遍历g_physArea数组
ret = OsVmPhysSegCreate(g_physArea[i].start, g_physArea[i].size);//由区划分转成段管理
if (ret != 0) {
VM_ERR("create phys seg failed");
}
......@@ -115,36 +118,36 @@ VOID OsVmPhysAreaSizeAdjust(size_t size)
g_physArea[i].size -= size;
}
}
//获得物理内存的总页数
UINT32 OsVmPhysPageNumGet(VOID)
{
UINT32 nPages = 0;
INT32 i;
for (i = 0; i < (sizeof(g_physArea) / sizeof(g_physArea[0])); i++) {
nPages += g_physArea[i].size >> PAGE_SHIFT;//右移12位,相当于除以4K,出总页数
nPages += g_physArea[i].size >> PAGE_SHIFT;//右移12位,相当于除以4K, 计算出总页数
}
return nPages;
return nPages;//返回所有物理内存总页数
}
//初始化空闲链表,分配页框使用伙伴算法
//初始化空闲链表,分配物理页框使用伙伴算法
STATIC INLINE VOID OsVmPhysFreeListInit(struct VmPhysSeg *seg)
{
int i;
UINT32 intSave;
struct VmFreeList *list = NULL;
LOS_SpinInit(&seg->freeListLock);//自旋锁
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);//初始化9个链表 2^0,2^1,2^2 分配页框 ^代表次方的意思
list->listCnt = 0;
LOS_ListInit(&list->node); //初始化9个链表, 链表上挂分配大小为 2^0,2^1,2^2 ..的页框(LosVmPage)
list->listCnt = 0; //链表上的数量默认0
}
LOS_SpinUnlockRestore(&seg->freeListLock, intSave);
}
//段初始化
//物理段初始化
VOID OsVmPhysInit(VOID)
{
struct VmPhysSeg *seg = NULL;
......@@ -153,13 +156,13 @@ VOID OsVmPhysInit(VOID)
for (i = 0; i < g_vmPhysSegNum; i++) {
seg = &g_vmPhysSeg[i];
seg->pageBase = &g_vmPageArray[nPages];//
nPages += seg->size >> PAGE_SHIFT;//偏移12位,因以页管理,本段总页数
seg->pageBase = &g_vmPageArray[nPages];//记录本段首页物理页框地址
nPages += seg->size >> PAGE_SHIFT;//偏移12位,按4K一页,算出本段总页数
OsVmPhysFreeListInit(seg); //初始化空闲链表,分配页框使用伙伴算法
OsVmPhysLruInit(seg); //初始化LRU置换链表
}
}
//将页框挂入空闲链表,供分配
//将页框挂入空闲链表,分配物理内存从空闲链表里拿
STATIC VOID OsVmPhysFreeListAdd(LosVmPage *page, UINT8 order)
{
struct VmPhysSeg *seg = NULL;
......@@ -169,14 +172,14 @@ STATIC VOID OsVmPhysFreeListAdd(LosVmPage *page, UINT8 order)
LOS_Panic("The page segment id(%d) is invalid\n", page->segID);
}
page->order = order;// page记录 块组
page->order = order;// page记录伙伴算法的组序
seg = &g_vmPhysSeg[page->segID];//先找到所属段
list = &seg->freeList[order];//找到对应List
LOS_ListTailInsert(&list->node, &page->node);//将page节点挂入链表
list->listCnt++;//链表内的节点总数++
list->listCnt++;//链表节点总数++
}
//同上
STATIC VOID OsVmPhysFreeListAddUnsafe(LosVmPage *page, UINT8 order)
{
struct VmPhysSeg *seg = NULL;
......@@ -193,23 +196,23 @@ STATIC VOID OsVmPhysFreeListAddUnsafe(LosVmPage *page, UINT8 order)
LOS_ListTailInsert(&list->node, &page->node);
list->listCnt++;
}
//将物理页框从空闲链表上摘除,见于物理页框被分配的情况
STATIC VOID OsVmPhysFreeListDelUnsafe(LosVmPage *page)
{
struct VmPhysSeg *seg = NULL;
struct VmFreeList *list = NULL;
if ((page->segID >= VM_PHYS_SEG_MAX) || (page->order >= VM_LIST_ORDER_MAX)) {
if ((page->segID >= VM_PHYS_SEG_MAX) || (page->order >= VM_LIST_ORDER_MAX)) {//等于VM_LIST_ORDER_MAX也不行,说明伙伴算法最大支持 2^8的分配
LOS_Panic("The page segment id(%u) or order(%u) is invalid\n", page->segID, page->order);
}
seg = &g_vmPhysSeg[page->segID];
list = &seg->freeList[page->order];
list->listCnt--;
LOS_ListDelete(&page->node);
page->order = VM_LIST_ORDER_MAX;
seg = &g_vmPhysSeg[page->segID]; //找到物理页框对应的段
list = &seg->freeList[page->order]; //根据伙伴算法组序号找到空闲链表
list->listCnt--; //链表节点总数减一
LOS_ListDelete(&page->node); //将自己从链表上摘除
page->order = VM_LIST_ORDER_MAX; //告诉系统物理页框已不在空闲链表上, 已妙用于OsVmPhysPagesSpiltUnsafe的断言
}
//同上
STATIC VOID OsVmPhysFreeListDel(LosVmPage *page)
{
struct VmPhysSeg *seg = NULL;
......@@ -226,19 +229,24 @@ STATIC VOID OsVmPhysFreeListDel(LosVmPage *page)
page->order = VM_LIST_ORDER_MAX;
}
/******************************************************************************
本函数很像卖猪肉的,拿一大块肉剁,先把多余的放回到小块肉堆里去.
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;) {
order--;
buddyPage = &page[VM_ORDER_TO_PAGES(order)];
LOS_ASSERT(buddyPage->order == VM_LIST_ORDER_MAX);
OsVmPhysFreeListAddUnsafe(buddyPage, order);
for (order = newOrder; order > oldOrder;) {//把肉剁碎的过程,把多余的肉块切成2^7,2^6...标准块,
order--;//逐一放回仓库,一直切到最后的2^2块
buddyPage = &page[VM_ORDER_TO_PAGES(order)];//@note_why 先把多余的肉割出来,但 没有理解是怎么做到的?
LOS_ASSERT(buddyPage->order == VM_LIST_ORDER_MAX);//@note_why 同样没理解为什么能下这个断言
OsVmPhysFreeListAddUnsafe(buddyPage, order);//将劈开的节点挂到对应序号的链表上
}
}
//通过物理地址获取所属页面
//通过物理地址获取所属物理页框
LosVmPage *OsVmPhysToPage(paddr_t pa, UINT8 segID)
{
struct VmPhysSeg *seg = NULL;
......@@ -252,7 +260,7 @@ LosVmPage *OsVmPhysToPage(paddr_t pa, UINT8 segID)
return NULL;
}
offset = pa - seg->start;//得到偏移地址
offset = pa - seg->start;//得到物理地址的偏移量
return (seg->pageBase + (offset >> PAGE_SHIFT));//得到第n页page
}
......@@ -286,7 +294,7 @@ LosVmPage *OsVmVaddrToPage(VOID *ptr)
return NULL;
}
//从段中分配指定页框
//从参数段中分配参数页
LosVmPage *OsVmPhysPagesAlloc(struct VmPhysSeg *seg, size_t nPages)
{
struct VmFreeList *list = NULL;
......@@ -297,22 +305,22 @@ LosVmPage *OsVmPhysPagesAlloc(struct VmPhysSeg *seg, size_t nPages)
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)) {//没找到
if (LOS_ListEmpty(&list->node)) {//理想情况链表为空,说明没找到
continue;//继续找更大块的
}
page = LOS_DL_LIST_ENTRY(LOS_DL_LIST_FIRST(&list->node), LosVmPage, node);//找
page = LOS_DL_LIST_ENTRY(LOS_DL_LIST_FIRST(&list->node), LosVmPage, node);//找第一个节点就行,因为链表上挂的都是同样大小物理页框
goto DONE;
}
}
return NULL;
DONE:
OsVmPhysFreeListDelUnsafe(page);
OsVmPhysPagesSpiltUnsafe(page, order, newOrder);
OsVmPhysFreeListDelUnsafe(page);//将物理页框从链表上摘出来
OsVmPhysPagesSpiltUnsafe(page, order, newOrder);//将物理页框劈开,把用不了的页再挂到对应的空闲链表上
return page;
}
//释放物理页,所谓释放物理页就是把页挂到空闲链表中
......
git add -A
git commit -m '文件如何被分页读到页高速缓存,脏页数据又如何回写?
git commit -m '注解物理内存是如何管理的?伙伴算法/LRU如何分配/置换物理页框?
搜索 @note_pic 可以查看全部字符图
搜索 @note_why 是注者尚未看明白的地方,如果您看明白了,请告诉注者完善
搜索 @note_thinking 是注者的思考和吐槽的地方
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册