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

文件如何被分页读到页高速缓存,脏页数据又如何回写?

搜索 @note_pic 可以查看全部字符图
搜索 @note_why 是注者尚未看明白的地方,如果您看明白了,请告诉注者完善
搜索 @note_thinking 是注者的思考和吐槽的地方
搜索 @note_#if0 是由第三方项目提供不由内核源码中定义的极为重要的结构体,为方便理解而添加的。
上级 d7859968
......@@ -92,7 +92,7 @@ typedef struct FilePage {
struct VmPhysSeg *physSeg; /* physical memory that file page belongs to */ //物理内存是唯一的
struct VmPage *vmPage; //物理页框
struct page_mapping *mapping; //此结构由文件系统提供,用于描述装入点 见于 ..\third_party\NuttX\include\nuttx\fs\fs.h
VM_OFFSET_T pgoff; //记录文件的位置,文件从哪个位置开始数据映射到文件页中的,用于读写文件数据时确定开始位置
VM_OFFSET_T pgoff; //页标,文件被切成一页一页读到内存
UINT32 flags; //标签
UINT16 dirtyOff; //脏页的页内偏移地址
UINT16 dirtyEnd; //脏页的结束位置
......
......@@ -92,8 +92,8 @@ 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页面物理地址对应的内核虚拟地址,这里要说明下啥意思,缺页的意思是此进程的虚拟空间中没有这个虚拟地址的映射,
VADDR_T *pageKVaddr; /* KVaddr of pagefault's vm page's paddr */ //指的就是物理地址
//pageKVaddr为缺页的vm页面的物理地址,这里要说明下啥意思,缺页的意思是此进程MMU没有虚拟地址与物理地址的映射,
//但并不代表物理页框没有被别的进程虚拟空间所映射.一定要理解这里!
} LosVmPgFault;
//虚拟内存文件操作函数指针,上层开发可理解为 class 里的方法,注意是对线性区的操作
......@@ -109,7 +109,7 @@ struct VmMapRegion {//线性区描述符,内核通过线性区管理虚拟地址
LosVmSpace *space; //所属虚拟空间,虚拟空间由多个线性区组成
LOS_DL_LIST node; /**< region dl list */ //链表节点,通过它将本线性区挂在VmSpace.regions上
LosVmMapRange range; /**< region address range */ //记录线性区的范围
VM_OFFSET_T pgOff; /**< region page offset to file */ //区域页面到文件的偏移量
VM_OFFSET_T pgOff; /**< region page offset to file */ //以文件开始处的偏移量, 必须是分页大小的整数倍, 通常为0, 表示从文件头开始映射。
UINT32 regionFlags; /**< region flags: cow, user_wired *///线性区标签
UINT32 shmid; /**< shmid about shared region */ //shmid为共享线性区id
UINT8 protectFlags; /**< vm region protect flags: PROT_READ, PROT_WRITE, *///线性区中页框的访问许可权
......
......@@ -53,7 +53,7 @@ extern "C" {
extern char __exc_table_start[];
extern char __exc_table_end[];
//线性区正确检查
//线性区正确检查
STATIC STATUS_T OsVmRegionRightCheck(LosVmMapRegion *region, UINT32 flags)
{
if ((flags & VM_MAP_PF_FLAG_WRITE) == VM_MAP_PF_FLAG_WRITE) {//写入许可
......@@ -102,8 +102,8 @@ STATIC VOID OsFaultTryFixup(ExcContext *frame, VADDR_T excVaddr, STATUS_T *statu
#endif
}
#ifdef LOSCFG_FS_VFS
STATIC STATUS_T OsDoReadFault(LosVmMapRegion *region, LosVmPgFault *vmPgFault)
#ifdef LOSCFG_FS_VFS
STATIC STATUS_T OsDoReadFault(LosVmMapRegion *region, LosVmPgFault *vmPgFault)//读缺页
{
status_t ret;
PADDR_T paddr;
......@@ -112,16 +112,16 @@ STATIC STATUS_T OsDoReadFault(LosVmMapRegion *region, LosVmPgFault *vmPgFault)
LosVmSpace *space = region->space;
ret = LOS_ArchMmuQuery(&space->archMmu, vaddr, NULL, NULL);//查询是否缺页
if (ret == LOS_OK) {
return LOS_OK;
if (ret == LOS_OK) {//注意这里时LOS_OK却返回,都OK了说明查到了物理地址,有页了。
return LOS_OK;//查到了就说明不缺页的,缺页就是因为虚拟地址没有映射到物理地址嘛
}
if (region->unTypeData.rf.vmFOps == NULL || region->unTypeData.rf.vmFOps->fault == NULL) {
if (region->unTypeData.rf.vmFOps == NULL || region->unTypeData.rf.vmFOps->fault == NULL) {//线性区必须有实现了缺页接口
VM_ERR("region args invalid, file path: %s", region->unTypeData.rf.file->f_path);
return LOS_ERRNO_VM_INVALID_ARGS;
}
(VOID)LOS_MuxAcquire(&region->unTypeData.rf.file->f_mapping->mux_lock);
ret = region->unTypeData.rf.vmFOps->fault(region, vmPgFault);// 函数指针 g_commVmOps.OsVmmFileFault
ret = region->unTypeData.rf.vmFOps->fault(region, vmPgFault);// 函数指针,执行的是g_commVmOps.OsVmmFileFault
if (ret == LOS_OK) {
paddr = LOS_PaddrQuery(vmPgFault->pageKVaddr);//查询物理地址
page = LOS_VmPageGet(paddr);//获取page
......@@ -294,7 +294,7 @@ status_t OsDoSharedFault(LosVmMapRegion *region, LosVmPgFault *vmPgFault)
}
(VOID)LOS_MuxAcquire(&region->unTypeData.rf.file->f_mapping->mux_lock);
ret = region->unTypeData.rf.vmFOps->fault(region, vmPgFault);
ret = region->unTypeData.rf.vmFOps->fault(region, vmPgFault);//函数指针,执行的是g_commVmOps.OsVmmFileFault
if (ret == LOS_OK) {
paddr = LOS_PaddrQuery(vmPgFault->pageKVaddr);
page = LOS_VmPageGet(paddr);
......@@ -330,14 +330,14 @@ STATIC STATUS_T OsDoFileFault(LosVmMapRegion *region, LosVmPgFault *vmPgFault, U
{
STATUS_T ret;
if (flags & VM_MAP_PF_FLAG_WRITE) {
if (region->regionFlags & VM_MAP_REGION_FLAG_SHARED) {
if (flags & VM_MAP_PF_FLAG_WRITE) {//写页的时候产生缺页
if (region->regionFlags & VM_MAP_REGION_FLAG_SHARED) {//共享线性区
ret = OsDoSharedFault(region, vmPgFault);//写操作时的共享缺页,最复杂,此页上的更改将写入磁盘文件
} else {
} else {//非共享线性区
ret = OsDoCowFault(region, vmPgFault);//(写时拷贝技术)写操作时的私有缺页,pagecache被复制到私有的任意一个页面上,并在此页面上进行更改,不会直接写入磁盘文件
}
} else {
ret = OsDoReadFault(region, vmPgFault);//读时缺页,读最简单 只需共享页面缓存
} else {//读页的时候产生缺页
ret = OsDoReadFault(region, vmPgFault);//读时缺页,读最简单
}
return ret;
}
......@@ -395,15 +395,15 @@ STATUS_T OsVmPageFaultHandler(VADDR_T vaddr, UINT32 flags, ExcContext *frame)
}
vaddr = ROUNDDOWN(vaddr, PAGE_SIZE);//为啥要向下圆整,因为这一页要重新使用,需找到页面基地址
#ifdef LOSCFG_FS_VFS
if (LOS_IsRegionFileValid(region)) {
#ifdef LOSCFG_FS_VFS
if (LOS_IsRegionFileValid(region)) {//是否为文件线性区
if (region->unTypeData.rf.file->f_mapping == NULL) {
goto CHECK_FAILED;
}
vmPgFault.vaddr = vaddr;
vmPgFault.vaddr = vaddr;//
vmPgFault.pgoff = ((vaddr - region->range.base) >> PAGE_SHIFT) + region->pgOff;
vmPgFault.flags = flags;
vmPgFault.pageKVaddr = NULL;
vmPgFault.pageKVaddr = NULL;//没有物理地址
status = OsDoFileFault(region, &vmPgFault, flags);//缺页处理
if (status) {
......
......@@ -423,7 +423,7 @@ STATIC INT32 OsFlushDirtyPage(LosFilePage *fpage)
VM_ERR("WritePage error ret %d", ret);
}
ret = (ret <= 0) ? LOS_NOK : LOS_OK;
OsCleanPageDirty(fpage->vmPage);//还清白之身,哈哈,臣妾又干净了.
OsCleanPageDirty(fpage->vmPage);//撕掉脏页标签,还清白之身,哈哈,臣妾又干净了.
(VOID)file_seek(file, oldPos, SEEK_SET);//写完了脏数据,切回到老位置,一定要回到老位置,因为回写脏数据是内核的行为,而不是用户的行为
return ret;
......@@ -504,7 +504,7 @@ VOID OsDelMapInfo(LosVmMapRegion *region, LosVmPgFault *vmf, BOOL cleanDirty)
}
/**************************************************************************************************
文件缺页时的处理,先读入磁盘数据,再重新读页数据
被 OsDoReadFault(...),OsDoCowFault(...),OsDoSharedFault(...) 等调用
**************************************************************************************************/
INT32 OsVmmFileFault(LosVmMapRegion *region, LosVmPgFault *vmf)
{
......@@ -527,11 +527,11 @@ INT32 OsVmmFileFault(LosVmMapRegion *region, LosVmPgFault *vmf)
/* get or create a new cache node */
LOS_SpinLockSave(&mapping->list_lock, &intSave);
fpage = OsFindGetEntry(mapping, vmf->pgoff);//获取fpage
if (fpage != NULL) {
fpage = OsFindGetEntry(mapping, vmf->pgoff);//获取文件页
if (fpage != NULL) {//找到了,该页已经在页高速缓存中
OsPageRefIncLocked(fpage);
} else {
fpage = OsPageCacheAlloc(mapping, vmf->pgoff);//分配一个vmpage(物理页框) + fpage
} else {//真的缺页了,页高速缓存中没找到
fpage = OsPageCacheAlloc(mapping, vmf->pgoff);//分配一个文件页,将数据初始化好,包括vmpage(物理页框)
if (fpage == NULL) {
VM_ERR("Failed to alloc a page frame");
LOS_SpinUnlockRestore(&mapping->list_lock, intSave);
......@@ -571,7 +571,7 @@ INT32 OsVmmFileFault(LosVmMapRegion *region, LosVmPgFault *vmf)
}
/* share page fault, mark the page dirty */
if ((vmf->flags & VM_MAP_PF_FLAG_WRITE) && (region->regionFlags & VM_MAP_REGION_FLAG_SHARED)) {
if ((vmf->flags & VM_MAP_PF_FLAG_WRITE) && (region->regionFlags & VM_MAP_REGION_FLAG_SHARED)) {//有过写操作或者为共享线性区
OsMarkPageDirty(fpage, region, 0, 0);//标记为脏页,要回写磁盘,内核会在适当的时候回写磁盘
}
......@@ -759,7 +759,7 @@ LosFilePage *OsPageCacheAlloc(struct page_mapping *mapping, VM_OFFSET_T pgoff)
fpage->physSeg = physSeg; //页框所属段.其中包含了 LRU LIST ==
fpage->vmPage = vmPage; //物理页框
fpage->mapping = mapping; //记录所有文件页映射
fpage->pgoff = pgoff; //偏移
fpage->pgoff = pgoff; //将文件切成一页页,页标
(VOID)memset_s(kvaddr, PAGE_SIZE, 0, PAGE_SIZE);//页内数据清0
return fpage;
......
......@@ -452,7 +452,7 @@ LosVmMapRegion *OsCreateRegion(VADDR_T vaddr, size_t len, UINT32 regionFlags, un
//创建线性区的本质就是在画饼,见如下操作:
region->range.base = vaddr; //虚拟地址作为线性区的基地址
region->range.size = len; //线性区大小,这是线性区构思最巧妙的地方,只要不过分,蓝图随便画。
region->pgOff = offset; //页内偏移
region->pgOff = offset; //页
region->regionFlags = regionFlags;//标识,可读/可写/可执行
region->regionType = VM_MAP_REGION_TYPE_NONE;//未映射
region->forkFlags = 0; //
......
......@@ -255,11 +255,17 @@ LosVmPage *OsVmPhysToPage(paddr_t pa, UINT8 segID)
offset = pa - seg->start;//得到偏移地址
return (seg->pageBase + (offset >> PAGE_SHIFT));//得到第n页page
}
//通过page获取内核空间的虚拟地址 参考OsArchMmuInit
/******************************************************************************
通过page获取内核空间的虚拟地址 参考OsArchMmuInit
#define SYS_MEM_BASE DDR_MEM_ADDR /* physical memory base 物理地址的起始地址 * /
本函数非常重要,通过一个物理地址找到内核虚拟地址
内核静态映射:提升虚实转化效率,段映射减少页表项
******************************************************************************/
VOID *OsVmPageToVaddr(LosVmPage *page)//
{
VADDR_T vaddr;
vaddr = KERNEL_ASPACE_BASE + page->physAddr - SYS_MEM_BASE;//page->physAddr - SYS_MEM_BASE 得到物理地址的偏移量
vaddr = KERNEL_ASPACE_BASE + page->physAddr - SYS_MEM_BASE;//page->physAddr - SYS_MEM_BASE 得到物理地址的偏移量
//因在整个虚拟内存中内核空间和用户空间是通过地址隔离的,如此很巧妙的就把该物理页映射到了内核空间
//内核空间的vmPage是不会被置换的,因为是常驻内存,内核空间初始化mmu时就映射好了L1表
return (VOID *)(UINTPTR)vaddr;
......@@ -466,7 +472,7 @@ VOID LOS_PhysPageFree(LosVmPage *page)
LOS_SpinUnlockRestore(&seg->freeListLock, intSave);
}
}
//供外部调用
LosVmPage *LOS_PhysPageAlloc(VOID)
{
return OsVmPhysPagesGet(ONE_PAGE);//分配一页物理页
......
......@@ -98,7 +98,7 @@ flags 控制程序对内存段的改变所造成的影响,有如下属性:
MAP_PRIVATE:内存段私有,对它的修改值仅对本进程有效。
MAP_SHARED:把对该内存段的修改保存到磁盘文件中。
fd 打开的文件描述符。
offset 用以改变经共享内存段访问的文件中数据的起始偏移值
offset 以文件开始处的偏移量, 必须是分页大小的整数倍, 通常为0, 表示从文件头开始映射
成功返回:虚拟内存地址,这地址是页对齐。
失败返回:(void *)-1。
**************************************************/
......
......@@ -25,9 +25,9 @@
****************************************************************/
/* */
/**************************************************************************************************
/******************************************************************************
**************************************************************************************************/
******************************************************************************/
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册