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

注解1.对缺页中断的处理 2.物理地址的管理

搜索 @note_pic 可以查看全部字符图
搜索 @note_why 是注者尚未看明白的地方,如果您看明白了,请告诉注者完善
搜索 @note_thinking 是注者的思考和吐槽的地方
搜索 @note_#if0 是由第三方项目提供不由内核源码中定义的极为重要的结构体,为方便理解而添加的。
搜索 @note_good 是注者给源码点赞的地方
上级 d0b4bae4
......@@ -360,7 +360,11 @@ BOOL OsArchMmuInit(LosArchMmu *archMmu, VADDR_T *virtTtb)
//archMmu->physTtb = (VADDR_T)(UINTPTR)virtTtb + 0x40000000;
return TRUE;
}
//通过虚拟地址查询物理地址
/******************************************************************************
本函数是内核高频函数,通过MMU查询虚拟地址是否映射过,
带走映射的物理地址和权限
******************************************************************************/
STATUS_T LOS_ArchMmuQuery(const LosArchMmu *archMmu, VADDR_T vaddr, PADDR_T *paddr, UINT32 *flags)
{//archMmu->virtTtb:转换表基地址
PTE_T l1Entry = OsGetPte1(archMmu->virtTtb, vaddr);//获取PTE vaddr右移20位 得到L1描述子地址
......
/*
* 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_bitmap.h"
#include "los_printf.h"
#include "los_toolchain.h"
#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif /* __cplusplus */
#endif /* __cplusplus */
#define OS_BITMAP_MASK 0x1FU //
#define OS_BITMAP_WORD_MASK ~0UL
/* find first zero bit starting from LSB */
STATIC INLINE UINT16 Ffz(UINTPTR x)
{//__builtin_ffsl: 返回右起第一个1的位置,函数来自 glibc
return __builtin_ffsl(~x) - 1;//从LSB开始查找第一个零位 LSB(最低有效位) 对应 最高有效位(MSB)
}
//设置对应位置
VOID LOS_BitmapSet(UINT32 *bitmap, UINT16 pos)
{
if (bitmap == NULL) {
return;
}
*bitmap |= 1U << (pos & OS_BITMAP_MASK);//在对应位上置1
}
//对应位上置0
VOID LOS_BitmapClr(UINT32 *bitmap, UINT16 pos)
{
if (bitmap == NULL) {
return;
}
*bitmap &= ~(1U << (pos & OS_BITMAP_MASK));//在对应位上置0
}
//从高到低 返回第一个为1的位数, 例如: 00110110 返回 5
UINT16 LOS_HighBitGet(UINT32 bitmap)
{
if (bitmap == 0) {
return LOS_INVALID_BIT_INDEX;
}
return (OS_BITMAP_MASK - CLZ(bitmap));
}
//从低到高 返回第一个为1的位数, 例如: 00110110 返回 1
UINT16 LOS_LowBitGet(UINT32 bitmap)
{
if (bitmap == 0) {
return LOS_INVALID_BIT_INDEX;
}
return CTZ(bitmap);
}
//从start位置开始设置numsSet个bit位 置1
VOID LOS_BitmapSetNBits(UINTPTR *bitmap, UINT32 start, UINT32 numsSet)
{
UINTPTR *p = bitmap + BITMAP_WORD(start);
const UINT32 size = start + numsSet;
UINT16 bitsToSet = BITMAP_BITS_PER_WORD - (start % BITMAP_BITS_PER_WORD);
UINTPTR maskToSet = BITMAP_FIRST_WORD_MASK(start);
while (numsSet > bitsToSet) {
*p |= bitsToSet;
numsSet -= bitsToSet;
bitsToSet = BITMAP_BITS_PER_WORD;
maskToSet = OS_BITMAP_WORD_MASK;
p++;
}
if (numsSet) {
maskToSet &= BITMAP_LAST_WORD_MASK(size);
*p |= maskToSet;
}
}
//从start位置开始清除numsSet个bit位 置0
VOID LOS_BitmapClrNBits(UINTPTR *bitmap, UINT32 start, UINT32 numsClear)
{
UINTPTR *p = bitmap + BITMAP_WORD(start);
const UINT32 size = start + numsClear;
UINT16 bitsToClear = BITMAP_BITS_PER_WORD - (start % BITMAP_BITS_PER_WORD);
UINTPTR maskToClear = BITMAP_FIRST_WORD_MASK(start);
while (numsClear >= bitsToClear) {
*p &= ~maskToClear;
numsClear -= bitsToClear;
bitsToClear = BITMAP_BITS_PER_WORD;
maskToClear = OS_BITMAP_WORD_MASK;
p++;
}
if (numsClear) {
maskToClear &= BITMAP_LAST_WORD_MASK(size);
*p &= ~maskToClear;
}
}
//从numBits位置开始找到第一个0位
INT32 LOS_BitmapFfz(UINTPTR *bitmap, UINT32 numBits)
{
INT32 bit, i;
for (i = 0; i < BITMAP_NUM_WORDS(numBits); i++) {
if (bitmap[i] == OS_BITMAP_WORD_MASK) {
continue;
}
bit = i * BITMAP_BITS_PER_WORD + Ffz(bitmap[i]);
if (bit < numBits) {
return bit;
}
return -1;
}
return -1;
}
#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_bitmap.h"
#include "los_printf.h"
#include "los_toolchain.h"
#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif /* __cplusplus */
#endif /* __cplusplus */
#define OS_BITMAP_MASK 0x1FU //
#define OS_BITMAP_WORD_MASK ~0UL
/* find first zero bit starting from LSB */
STATIC INLINE UINT16 Ffz(UINTPTR x)
{//__builtin_ffsl: 返回右起第一个1的位置,函数来自 glibc
return __builtin_ffsl(~x) - 1;//从LSB开始查找第一个零位 LSB(最低有效位) 对应 最高有效位(MSB)
}
//设置对应位置
VOID LOS_BitmapSet(UINT32 *bitmap, UINT16 pos)
{
if (bitmap == NULL) {
return;
}
*bitmap |= 1U << (pos & OS_BITMAP_MASK);//在对应位上置1
}
//对应位上置0
VOID LOS_BitmapClr(UINT32 *bitmap, UINT16 pos)
{
if (bitmap == NULL) {
return;
}
*bitmap &= ~(1U << (pos & OS_BITMAP_MASK));//在对应位上置0
}
/********************************************************
杂项算术指令
CLZ 用于计算操作数最高端0的个数,这条指令主要用于一下两个场合
  计算操作数规范化(使其最高位为1)时需要左移的位数
  确定一个优先级掩码中最高优先级
********************************************************/
//从高到低 返回第一个为1的位数, 例如: 00110110 返回 5
UINT16 LOS_HighBitGet(UINT32 bitmap)
{
if (bitmap == 0) {
return LOS_INVALID_BIT_INDEX;
}
return (OS_BITMAP_MASK - CLZ(bitmap));
}
//从低到高 返回第一个为1的位数, 例如: 00110110 返回 2
UINT16 LOS_LowBitGet(UINT32 bitmap)
{
if (bitmap == 0) {
return LOS_INVALID_BIT_INDEX;
}
return CTZ(bitmap);//
}
//从start位置开始设置numsSet个bit位 置1
VOID LOS_BitmapSetNBits(UINTPTR *bitmap, UINT32 start, UINT32 numsSet)
{
UINTPTR *p = bitmap + BITMAP_WORD(start);
const UINT32 size = start + numsSet;
UINT16 bitsToSet = BITMAP_BITS_PER_WORD - (start % BITMAP_BITS_PER_WORD);
UINTPTR maskToSet = BITMAP_FIRST_WORD_MASK(start);
while (numsSet > bitsToSet) {
*p |= bitsToSet;
numsSet -= bitsToSet;
bitsToSet = BITMAP_BITS_PER_WORD;
maskToSet = OS_BITMAP_WORD_MASK;
p++;
}
if (numsSet) {
maskToSet &= BITMAP_LAST_WORD_MASK(size);
*p |= maskToSet;
}
}
//从start位置开始清除numsSet个bit位 置0
VOID LOS_BitmapClrNBits(UINTPTR *bitmap, UINT32 start, UINT32 numsClear)
{
UINTPTR *p = bitmap + BITMAP_WORD(start);
const UINT32 size = start + numsClear;
UINT16 bitsToClear = BITMAP_BITS_PER_WORD - (start % BITMAP_BITS_PER_WORD);
UINTPTR maskToClear = BITMAP_FIRST_WORD_MASK(start);
while (numsClear >= bitsToClear) {
*p &= ~maskToClear;
numsClear -= bitsToClear;
bitsToClear = BITMAP_BITS_PER_WORD;
maskToClear = OS_BITMAP_WORD_MASK;
p++;
}
if (numsClear) {
maskToClear &= BITMAP_LAST_WORD_MASK(size);
*p &= ~maskToClear;
}
}
//从numBits位置开始找到第一个0位
INT32 LOS_BitmapFfz(UINTPTR *bitmap, UINT32 numBits)
{
INT32 bit, i;
for (i = 0; i < BITMAP_NUM_WORDS(numBits); i++) {
if (bitmap[i] == OS_BITMAP_WORD_MASK) {
continue;
}
bit = i * BITMAP_BITS_PER_WORD + Ffz(bitmap[i]);
if (bit < numBits) {
return bit;
}
return -1;
}
return -1;
}
#ifdef __cplusplus
#if __cplusplus
}
#endif /* __cplusplus */
#endif /* __cplusplus */
......@@ -92,9 +92,7 @@ typedef struct VmFault {
UINT32 flags; /* FAULT_FLAG_xxx flags */ //缺页标识
unsigned long pgoff; /* Logical page offset based on region */ //基于线性区的逻辑页偏移量
VADDR_T vaddr; /* Faulting virtual address */ //产生缺页的虚拟地址
VADDR_T *pageKVaddr; /* KVaddr of pagefault's vm page's paddr */ //指的就是物理地址
//pageKVaddr为缺页的vm页面的物理地址,这里要说明下啥意思,缺页的意思是此进程MMU没有虚拟地址与物理地址的映射,
//但并不代表物理页框没有被别的进程虚拟空间所映射.一定要理解这里!
VADDR_T *pageKVaddr; /* KVaddr of pagefault's vm page's paddr */ //page cache中的虚拟地址
} LosVmPgFault;
//虚拟内存文件操作函数指针,上层开发可理解为 class 里的方法,注意是对线性区的操作
struct VmFileOps {// 文件操作
......
......@@ -48,17 +48,17 @@ extern "C" {
******************************************************************************/
//注意: vmPage 中并没有虚拟地址,只有物理地址
typedef struct VmPage { //物理页框描述符
LOS_DL_LIST node; /**< vm object dl list */ //虚拟内存节点,通过它挂到全局g_vmPhysSeg[segID]->freeList[order]物理页框链表上
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 */ //页标签,同时可以有多个标签(共享/引用/活动/被锁==)
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;
} LosVmPage;//注意:关于nPages和order的关系说明,当请求分配为5页时,order是等于3的,因为只有2^3才能满足5页的请求
extern LosVmPage *g_vmPageArray;//物理页框(page frame)池
extern LosVmPage *g_vmPageArray;//物理页框(page frame)池,在g_vmPageArray中:不可能存在两个物理地址一样的物理页框,
extern size_t g_vmPageArraySize;//物理总页框(page frame)数
LosVmPage *LOS_VmPageGet(PADDR_T paddr);
......
......@@ -48,7 +48,7 @@ LRU是Least Recently Used的缩写,即最近最少使用页面置换算法,
是根据页面调入内存后的使用情况进行决策了。由于无法预测各页面将来的使用情况,只能利用
“最近的过去”作为“最近的将来”的近似,因此,LRU算法就是将最近最久未使用的页面予以淘汰。
******************************************************************************/
#define VM_LIST_ORDER_MAX 9 //伙伴算法分组,即物理内存层面一次最大分配 2^8 * 4K = 1M
#define VM_LIST_ORDER_MAX 9 //伙伴算法分组数量,从 2^0,2^1,...,2^8 (256*4K)=1M
#define VM_PHYS_SEG_MAX 32 //最大支持32个段
#ifndef min
......
此差异已折叠。
......@@ -103,6 +103,7 @@ STATIC VOID OsFaultTryFixup(ExcContext *frame, VADDR_T excVaddr, STATUS_T *statu
}
#ifdef LOSCFG_FS_VFS
//读页时发生缺页的处理
STATIC STATUS_T OsDoReadFault(LosVmMapRegion *region, LosVmPgFault *vmPgFault)//读缺页
{
status_t ret;
......@@ -174,7 +175,7 @@ STATIC LosVmPage *OsCowUnmapOrg(LosArchMmu *archMmu, LosVmMapRegion *region, Los
return oldPage;
}
#endif
//写入私有空间时的缺页处理
//在私有线性区写入文件时发生缺页的处理
status_t OsDoCowFault(LosVmMapRegion *region, LosVmPgFault *vmPgFault)
{
STATUS_T ret;
......@@ -258,7 +259,7 @@ ERR_OUT:
return ret;
}
//在共享线性区写文件操作发生缺页的情况处理,因为线性区是共享的
status_t OsDoSharedFault(LosVmMapRegion *region, LosVmPgFault *vmPgFault)
{
STATUS_T ret;
......@@ -274,7 +275,7 @@ status_t OsDoSharedFault(LosVmMapRegion *region, LosVmPgFault *vmPgFault)
return LOS_ERRNO_VM_INVALID_ARGS;
}
ret = LOS_ArchMmuQuery(&space->archMmu, vmPgFault->vaddr, &paddr, NULL);//先能否查出地理地址
ret = LOS_ArchMmuQuery(&space->archMmu, vmPgFault->vaddr, &paddr, NULL);//查询物理地址
if (ret == LOS_OK) {
LOS_ArchMmuUnmap(&space->archMmu, vmPgFault->vaddr, 1);//先取消映射
ret = LOS_ArchMmuMap(&space->archMmu, vaddr, paddr, 1, region->regionFlags);//再重新映射,为啥这么干,是因为regionFlags变了,
......@@ -285,14 +286,14 @@ status_t OsDoSharedFault(LosVmMapRegion *region, LosVmPgFault *vmPgFault)
LOS_SpinLockSave(&region->unTypeData.rf.file->f_mapping->list_lock, &intSave);
fpage = OsFindGetEntry(region->unTypeData.rf.file->f_mapping, vmPgFault->pgoff);
if (fpage) {
if (fpage) {//在页高速缓存(page cache)中找到了
OsMarkPageDirty(fpage, region, 0, 0);//标记为脏页
}
LOS_SpinUnlockRestore(&region->unTypeData.rf.file->f_mapping->list_lock, intSave);
return LOS_OK;
}
//以下是没有映射到物理地址的处理
(VOID)LOS_MuxAcquire(&region->unTypeData.rf.file->f_mapping->mux_lock);
ret = region->unTypeData.rf.vmFOps->fault(region, vmPgFault);//函数指针,执行的是g_commVmOps.OsVmmFileFault
if (ret == LOS_OK) {
......@@ -325,7 +326,7 @@ status_t OsDoSharedFault(LosVmMapRegion *region, LosVmPgFault *vmPgFault)
* For COW fault, pagecache is copied to private anonyous pages and the changes on this page
* won't write through to the underlying file. For SHARED fault, pagecache is mapping with
* region->arch_mmu_flags and the changes on this page will write through to the underlying file
*/
*/ //操作文件时产生缺页中断
STATIC STATUS_T OsDoFileFault(LosVmMapRegion *region, LosVmPgFault *vmPgFault, UINT32 flags)
{
STATUS_T ret;
......@@ -337,7 +338,7 @@ STATIC STATUS_T OsDoFileFault(LosVmMapRegion *region, LosVmPgFault *vmPgFault, U
ret = OsDoCowFault(region, vmPgFault);//(写时拷贝技术)写操作时的私有缺页,pagecache被复制到私有的任意一个页面上,并在此页面上进行更改,不会直接写入磁盘文件
}
} else {//读页的时候产生缺页
ret = OsDoReadFault(region, vmPgFault);//读时缺页,读最简单
ret = OsDoReadFault(region, vmPgFault);//页面读取操作很简单,只需共享页面缓存(节省内存)并进行读权限映射(region->arch_mmu_flags&(~arch_mmu_FLAG_PERM_WRITE))
}
return ret;
}
......@@ -349,7 +350,7 @@ STATIC STATUS_T OsDoFileFault(LosVmMapRegion *region, LosVmPgFault *vmPgFault, U
***************************************************************/
STATUS_T OsVmPageFaultHandler(VADDR_T vaddr, UINT32 flags, ExcContext *frame)
{
LosVmSpace *space = LOS_SpaceGet(vaddr);//获取所属空间
LosVmSpace *space = LOS_SpaceGet(vaddr);//获取虚拟地址所属空间
LosVmMapRegion *region = NULL;
STATUS_T status;
PADDR_T oldPaddr;
......@@ -400,8 +401,8 @@ STATUS_T OsVmPageFaultHandler(VADDR_T vaddr, UINT32 flags, ExcContext *frame)
if (region->unTypeData.rf.file->f_mapping == NULL) {
goto CHECK_FAILED;
}
vmPgFault.vaddr = vaddr;//
vmPgFault.pgoff = ((vaddr - region->range.base) >> PAGE_SHIFT) + region->pgOff;
vmPgFault.vaddr = vaddr;//虚拟地址
vmPgFault.pgoff = ((vaddr - region->range.base) >> PAGE_SHIFT) + region->pgOff;//计算出文件读取位置
vmPgFault.flags = flags;
vmPgFault.pageKVaddr = NULL;//没有物理地址
......@@ -423,17 +424,17 @@ STATUS_T OsVmPageFaultHandler(VADDR_T vaddr, UINT32 flags, ExcContext *frame)
newPaddr = VM_PAGE_TO_PHYS(newPage);//获取物理地址
(VOID)memset_s(OsVmPageToVaddr(newPage), PAGE_SIZE, 0, PAGE_SIZE);//获取虚拟地址 清0
status = LOS_ArchMmuQuery(&space->archMmu, vaddr, &oldPaddr, NULL);//通过虚拟地址查询老物理地址
if (status >= 0) {//L1,,L2 表中没找到
LOS_ArchMmuUnmap(&space->archMmu, vaddr, 1);//取消映射
OsPhysSharePageCopy(oldPaddr, &newPaddr, newPage);
if (status >= 0) {//已经映射过了,@note_thinking 不是缺页吗,怎么会有页的情况?
LOS_ArchMmuUnmap(&space->archMmu, vaddr, 1);//解除映射关系
OsPhysSharePageCopy(oldPaddr, &newPaddr, newPage);//将oldPaddr的数据拷贝到newPage
/* use old page free the new one */
if (newPaddr == oldPaddr) {//新老物理地址一致
LOS_PhysPageFree(newPage);
LOS_PhysPageFree(newPage);//继续使用旧页释放新页
newPage = NULL;
}
/* map all of the pages */
status = LOS_ArchMmuMap(&space->archMmu, vaddr, newPaddr, 1, region->regionFlags);//
status = LOS_ArchMmuMap(&space->archMmu, vaddr, newPaddr, 1, region->regionFlags);//重新映射新物理地址
if (status < 0) {
VM_ERR("failed to map replacement page, status:%d", status);
status = LOS_ERRNO_VM_MAP_FAILED;
......@@ -444,8 +445,8 @@ STATUS_T OsVmPageFaultHandler(VADDR_T vaddr, UINT32 flags, ExcContext *frame)
goto DONE;
} else {//
/* map all of the pages */
LOS_AtomicInc(&newPage->refCounts);//ref ++
status = LOS_ArchMmuMap(&space->archMmu, vaddr, newPaddr, 1, region->regionFlags);
LOS_AtomicInc(&newPage->refCounts);//引用数自增
status = LOS_ArchMmuMap(&space->archMmu, vaddr, newPaddr, 1, region->regionFlags);//映射新物理地址,如此下次就不会缺页了
if (status < 0) {
VM_ERR("failed to map page, status:%d", status);
status = LOS_ERRNO_VM_MAP_FAILED;
......
......@@ -575,7 +575,7 @@ INT32 OsVmmFileFault(LosVmMapRegion *region, LosVmPgFault *vmf)
OsMarkPageDirty(fpage, region, 0, 0);//标记为脏页,要回写磁盘,内核会在适当的时候回写磁盘
}
vmf->pageKVaddr = kvaddr;
vmf->pageKVaddr = kvaddr;//将文件页的虚拟地址带给缺陷页
LOS_SpinUnlockRestore(&mapping->list_lock, intSave);
return LOS_OK;
}
......
......@@ -1155,7 +1155,7 @@ VOID *LOS_KernelMallocAlign(UINT32 size, UINT32 boundary)
return ptr;
}
//内核内存分配
VOID *LOS_KernelRealloc(VOID *ptr, UINT32 size)
{
VOID *tmpPtr = NULL;
......
......@@ -51,11 +51,11 @@ STATIC VOID OsVmPageInit(LosVmPage *page, paddr_t pa, UINT8 segID)
LOS_AtomicSet(&page->refCounts, 0); //引用次数0
page->physAddr = pa; //物理地址
page->segID = segID; //物理地址使用段管理,段ID
page->order = VM_LIST_ORDER_MAX; //所属伙伴算法块组,VM_LIST_ORDER_MAX代表初始化值,不属于任何块组
page->order = VM_LIST_ORDER_MAX; //初始化值,不属于任何块组
}
//伙伴算法初始化
STATIC INLINE VOID OsVmPageOrderListInit(LosVmPage *page, size_t nPages)
{//@note_why 此时所有页面 page->order 都是 VM_LIST_ORDER_MAX,能顺利的加入伙伴算法的链表吗?
{//@note_why 此时所有页面 page->order = VM_LIST_ORDER_MAX,能挂入伙伴算法的链表吗?
OsVmPhysPagesFreeContiguous(page, nPages);//释放连续的物理页框
}
/******************************************************************************
......
......@@ -140,9 +140,9 @@ STATIC INLINE VOID OsVmPhysFreeListInit(struct VmPhysSeg *seg)
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 ..的页框(LosVmPage)
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);
......@@ -335,8 +335,8 @@ VOID OsVmPhysPagesFree(LosVmPage *page, UINT8 order)
if (order < VM_LIST_ORDER_MAX - 1) {//order[0,7]
pa = VM_PAGE_TO_PHYS(page);//获取物理地址
do {
pa ^= VM_ORDER_TO_PHYS(order);//注意这里是高位和低位的 ^= ,也就是说跳到 order块组 物理地址处,此处处理甚妙!
do {//按位异或
pa ^= VM_ORDER_TO_PHYS(order);//@note_good 注意这里是高位和低位的 ^= ,也就是说跳到order块组物理地址处,此处处理甚妙!
buddyPage = OsVmPhysToPage(pa, page->segID);//通过物理地址拿到页框
if ((buddyPage == NULL) || (buddyPage->order != order)) {//页框所在组块必须要对应
break;
......@@ -362,7 +362,7 @@ VOID OsVmPhysPagesFreeContiguous(LosVmPage *page, size_t nPages)
pa = VM_PAGE_TO_PHYS(page);//获取页面物理地址
order = VM_PHYS_TO_ORDER(pa);//通过物理地址找到伙伴算法的级别
n = VM_ORDER_TO_PAGES(order);//通过级别找到物理页块 (1<<order),意思是如果order=3,就可以释放8个页块
if (n > nPages) {//只剩小于2的order时,退出循环
if (n > nPages) {//nPages只剩下小于2^order时,退出循环
break;
}
OsVmPhysPagesFree(page, order);//释放伙伴算法对应块组
......@@ -374,7 +374,7 @@ VOID OsVmPhysPagesFreeContiguous(LosVmPage *page, size_t nPages)
order = LOS_HighBitGet(nPages);//从高到低块组释放
n = VM_ORDER_TO_PAGES(order);//2^order次方
OsVmPhysPagesFree(page, order);//释放块组
page += n;
page += n;//相当于page[n]
}
}
......@@ -396,8 +396,8 @@ STATIC LosVmPage *OsVmPhysPagesGet(size_t nPages)
for (segID = 0; segID < g_vmPhysSegNum; segID++) {
seg = &g_vmPhysSeg[segID];
LOS_SpinLockSave(&seg->freeListLock, &intSave);
page = OsVmPhysPagesAlloc(seg, nPages);//分配指定页数的物理页,nPages需小于伙伴算法一次最大分配页数
if (page != NULL) {
page = OsVmPhysPagesAlloc(seg, nPages);//分配指定页数的物理页,nPages需小于伙伴算法一次能分配的最大页数
if (page != NULL) {//分配成功
/* */
LOS_AtomicSet(&page->refCounts, 0);//设置引用次数为0
page->nPages = nPages;//页数
......@@ -435,12 +435,12 @@ VOID LOS_PhysPagesFreeContiguous(VOID *ptr, size_t nPages)
return;
}
page = OsVmVaddrToPage(ptr);//通过虚拟地址找page
page = OsVmVaddrToPage(ptr);//通过虚拟地址找到页框
if (page == NULL) {
VM_ERR("vm page of ptr(%#x) is null", ptr);
return;
}
page->nPages = 0;
page->nPages = 0;//被分配的页数置为0,表示不被分配
seg = &g_vmPhysSeg[page->segID];
LOS_SpinLockSave(&seg->freeListLock, &intSave);
......@@ -450,8 +450,7 @@ VOID LOS_PhysPagesFreeContiguous(VOID *ptr, size_t nPages)
LOS_SpinUnlockRestore(&seg->freeListLock, intSave);
}
/******************************************************************************
通过物理地址获取内核虚拟地址
可以看出内核虚拟空间
通过物理地址获取内核虚拟地址,内核静态映射,减少虚实地址的转换
******************************************************************************/
VADDR_T *LOS_PaddrToKVaddr(PADDR_T paddr)
{
......@@ -467,7 +466,7 @@ VADDR_T *LOS_PaddrToKVaddr(PADDR_T paddr)
//内核
return (VADDR_T *)(UINTPTR)(paddr - SYS_MEM_BASE + KERNEL_ASPACE_BASE);//
}
//释放物理页框
VOID LOS_PhysPageFree(LosVmPage *page)
{
UINT32 intSave;
......@@ -513,7 +512,7 @@ size_t LOS_PhysPagesAlloc(size_t nPages, LOS_DL_LIST *list)
return count;
}
//共享页面拷贝实现
//拷贝共享页面
VOID OsPhysSharePageCopy(PADDR_T oldPaddr, PADDR_T *newPaddr, LosVmPage *newPage)
{
UINT32 intSave;
......@@ -527,43 +526,43 @@ VOID OsPhysSharePageCopy(PADDR_T oldPaddr, PADDR_T *newPaddr, LosVmPage *newPage
return;
}
oldPage = LOS_VmPageGet(oldPaddr);//由物理地址得到 page信息页
oldPage = LOS_VmPageGet(oldPaddr);//由物理地址得到页框
if (oldPage == NULL) {
VM_ERR("invalid paddr %p", oldPaddr);
return;
}
seg = &g_vmPhysSeg[oldPage->segID];//拿到段
seg = &g_vmPhysSeg[oldPage->segID];//拿到物理
LOS_SpinLockSave(&seg->freeListLock, &intSave);
if (LOS_AtomicRead(&oldPage->refCounts) == 1) {//页面引用次数仅一次,说明还没有被其他进程所使用过
*newPaddr = oldPaddr;
} else {
if (LOS_AtomicRead(&oldPage->refCounts) == 1) {//页面引用次数仅一次,说明只有一个进程在操作
*newPaddr = oldPaddr;//新老指向同一块物理地址
} else {//是个共享内存
newMem = LOS_PaddrToKVaddr(*newPaddr); //新页虚拟地址
oldMem = LOS_PaddrToKVaddr(oldPaddr); //老页虚拟地址
if ((newMem == NULL) || (oldMem == NULL)) {
LOS_SpinUnlockRestore(&seg->freeListLock, intSave);
return;
}
if (memcpy_s(newMem, PAGE_SIZE, oldMem, PAGE_SIZE) != EOK) {//老页内容复制给新页
}//请记住,在保护模式下,物理地址只能用于计算,操作(包括拷贝)需要虚拟地址!
if (memcpy_s(newMem, PAGE_SIZE, oldMem, PAGE_SIZE) != EOK) {//老页内容复制给新页,需操作虚拟地址,拷贝一页数据
VM_ERR("memcpy_s failed");
}
LOS_AtomicInc(&newPage->refCounts);//新页ref++
LOS_AtomicDec(&oldPage->refCounts);//老页ref--
LOS_AtomicInc(&newPage->refCounts);//新页引用次数以原子方式自动减量
LOS_AtomicDec(&oldPage->refCounts);//老页引用次数以原子方式自动减量
}
LOS_SpinUnlockRestore(&seg->freeListLock, intSave);
return;
}
//获取物理页框所在段
struct VmPhysSeg *OsVmPhysSegGet(LosVmPage *page)
{
if ((page == NULL) || (page->segID >= VM_PHYS_SEG_MAX)) {
return NULL;
}
return (OsGVmPhysSegGet() + page->segID);
return (OsGVmPhysSegGet() + page->segID);//等用于OsGVmPhysSegGet()[page->segID]
}
//通过总页数 ,获取块组 ,例如需要分配 8个页,返回就是3 ,如果是1023 ,返回就是10
//获取参数nPages对应的块组,例如 7 -> 2^3 返回 3
UINT32 OsVmPagesToOrder(size_t nPages)
{
UINT32 order;
......
git add -A
git commit -m '注解物理内存是如何管理的?伙伴算法/LRU如何分配/置换物理页框?
git commit -m '注解1.对缺页中断的处理 2.物理地址的管理
搜索 @note_pic 可以查看全部字符图
搜索 @note_why 是注者尚未看明白的地方,如果您看明白了,请告诉注者完善
搜索 @note_thinking 是注者的思考和吐槽的地方
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册