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

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

搜索 @note_pic 可以查看全部字符图
搜索 @note_why 是注者尚未看明白的地方,如果您看明白了,请告诉注者完善
搜索 @note_thinking 是注者的思考和吐槽的地方
搜索 @note_#if0 是由第三方项目提供不由内核源码中定义的极为重要的结构体,为方便理解而添加的。
......@@ -45,8 +45,25 @@
extern "C" {
#endif /* __cplusplus */
#endif /* __cplusplus */
/**************************************************************************************************
https://blog.csdn.net/kuangyufei/article/details/109032636
https://blog.csdn.net/qq_38410730/article/details/81036768
虚拟内存空间中的地址叫做“虚拟地址”;而实际物理内存空间中的地址叫做“实际物理地址”或“物理地址”。处理器
运算器和应用程序设计人员看到的只是虚拟内存空间和虚拟地址,而处理器片外的地址总线看到的只是物理地址空间和物理地址。
MMU是 MemoryManagementUnit 的缩写即,内存管理单元.
MMU 的作用:
1. 将虚拟地址翻译成为物理地址,然后访问实际的物理地址
2. 访问权限控制
当处理器试图访问一个虚存页面时,首先到页表中去查询该页是否已映射到物理页框中,并记录在页表中。如果在,
则MMU会把页码转换成页框码,并加上虚拟地址提供的页内偏移量形成物理地址后去访问物理内存;如果不在,
则意味着该虚存页面还没有被载入内存,这时MMU就会通知操作系统:发生了一个页面访问错误(页面错误),
接下来系统会启动所谓的“请页”机制,即调用相应的系统操作函数,判断该虚拟地址是否为有效地址。
typedef struct ArchMmu {
如果是有效的地址,就从虚拟内存中将该地址指向的页面读入到内存中的一个空闲页框中,并在页表中添加上
相对应的表项,最后处理器将从发生页面错误的地方重新开始运行;如果是无效的地址,则表明进程在试图访问
一个不存在的虚拟地址,此时操作系统将终止此次访问。
**************************************************************************************************/
typedef struct ArchMmu {//内存管理单元
LosMux mtx; /**< arch mmu page table entry modification mutex lock *///对页表操作的互斥量
VADDR_T *virtTtb; /**< translation table base virtual addr */ //注意:这里是个指针,内核操作都用这个地址
PADDR_T physTtb; /**< translation table base phys addr */ //注意:这里是个值,这个值是记录给MMU使用的,MMU只认它,内核是无法使用的
......
......@@ -65,7 +65,7 @@ extern "C" {
//page_mapping描述的是一个文件在内存中被映射了多少页,<文件,文件页的关系>
/* file mapped in VMM pages */
struct page_mapping {
LOS_DL_LIST page_list; /* all pages */ //文件映射的所有页链表,这些页的内容都来源同一个文件
LOS_DL_LIST page_list; /* all pages */ //链表上挂的是属于该文件的所有FilePage,这些页的内容都来源同一个文件
SPIN_LOCK_S list_lock; /* lock protecting it */
LosMux mux_lock; /* mutex lock */ //
unsigned long nrpages; /* number of total pages */
......@@ -85,24 +85,24 @@ struct file_map {
//文件页结构体
typedef struct FilePage {
LOS_DL_LIST node; //节点
LOS_DL_LIST node; //节点,节点挂到page_mapping.page_list上,链表以 pgoff 从小到大方式排序.
LOS_DL_LIST lru; //lru节点, 结合 LosVmPhysSeg: LOS_DL_LIST lruList[VM_NR_LRU_LISTS] 理解
LOS_DL_LIST i_mmap; /* list of mappings */
UINT32 n_maps; /* num of mapping */
struct VmPhysSeg *physSeg; /* physical memory that file page belongs to */
LOS_DL_LIST i_mmap; /* list of mappings */ //链表记录文件页被哪些进程映射 MapInfo.node挂上来
UINT32 n_maps; /* num of mapping */ //记录被进程映射的次数
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; //页偏移地址,主要用于seek时使用
VM_OFFSET_T pgoff; //记录文件的位置,文件从哪个位置开始数据映射到文件页中的,用于读写文件数据时确定开始位置
UINT32 flags; //标签
UINT16 dirtyOff; //脏页的页内偏移地址
UINT16 dirtyEnd; //脏页的结束位置
} LosFilePage;
//虚拟地址和文件页的映射信息
typedef struct MapInfo {
LOS_DL_LIST node; //节点
VADDR_T vaddr; //虚拟地址
LosFilePage *page; //文件页
LosArchMmu *archMmu;//mmu
typedef struct MapInfo {//在一个进程使用文件页之前,需要提前做好文件页在此内存空间的映射关系,如此通过虚拟内存就可以对文件页读写操作.
LOS_DL_LIST node; //节点,挂到page->i_mmap链表上.链表上记录要操作文件页的进程对这个page的映射信息
VADDR_T vaddr; //虚拟地址.每个进程访问同一个文件页的虚拟地址都是不一样的
LosFilePage *page; //文件页中只记录物理地址,是不会变的.但它是需要被多个进程访问,和映射的.
LosArchMmu *archMmu;//mmu完成vaddr和page->vmPage->physAddr物理地址的映射
} LosMapInfo;
//Flags由 bitmap 管理
enum OsPageFlags {
......
......@@ -334,7 +334,7 @@ STATIC STATUS_T OsDoFileFault(LosVmMapRegion *region, LosVmPgFault *vmPgFault, U
if (region->regionFlags & VM_MAP_REGION_FLAG_SHARED) {
ret = OsDoSharedFault(region, vmPgFault);//写操作时的共享缺页,最复杂,此页上的更改将写入磁盘文件
} else {
ret = OsDoCowFault(region, vmPgFault);//写操作时的私有缺页,pagecache被复制到私有的任意一个页面上,并在此页面上进行更改,不会直接写入磁盘文件
ret = OsDoCowFault(region, vmPgFault);//(写时拷贝技术)写操作时的私有缺页,pagecache被复制到私有的任意一个页面上,并在此页面上进行更改,不会直接写入磁盘文件
}
} else {
ret = OsDoReadFault(region, vmPgFault);//读时缺页,读最简单 只需共享页面缓存
......@@ -347,7 +347,6 @@ STATIC STATUS_T OsDoFileFault(LosVmMapRegion *region, LosVmPgFault *vmPgFault, U
第一种:由编程错误引起的异常
第二种:属于进程的地址空间范围但还尚未分配物理页框引起的异常
***************************************************************/
STATUS_T OsVmPageFaultHandler(VADDR_T vaddr, UINT32 flags, ExcContext *frame)
{
LosVmSpace *space = LOS_SpaceGet(vaddr);//获取所属空间
......@@ -406,7 +405,7 @@ STATUS_T OsVmPageFaultHandler(VADDR_T vaddr, UINT32 flags, ExcContext *frame)
vmPgFault.flags = flags;
vmPgFault.pageKVaddr = NULL;
status = OsDoFileFault(region, &vmPgFault, flags);
status = OsDoFileFault(region, &vmPgFault, flags);//缺页处理
if (status) {
VM_ERR("vm fault error, status=%d", status);
goto CHECK_FAILED;
......
此差异已折叠。
......@@ -224,7 +224,7 @@ LosVmMapRegion *OsShareRegionClone(LosVmMapRegion *oldRegion)
*newRegion = *oldRegion;
return newRegion;
}
//克隆私有线性区,输入区,输出新区
//克隆私有线性区,输入区,输出新区
LosVmMapRegion *OsPrivateRegionClone(LosVmMapRegion *oldRegion)
{
/* need to create vm object */
......@@ -302,13 +302,13 @@ STATUS_T LOS_VmSpaceClone(LosVmSpace *oldVmSpace, LosVmSpace *newVmSpace)
}
LOS_ArchMmuMap(&newVmSpace->archMmu, vaddr, paddr, 1, flags & ~VM_MAP_REGION_FLAG_PERM_WRITE);//映射新空间
#ifdef LOSCFG_FS_VFS
if (LOS_IsRegionFileValid(oldRegion)) {
#ifdef LOSCFG_FS_VFS //文件系统开关
if (LOS_IsRegionFileValid(oldRegion)) {//是都是一个文件映射线性区
LosFilePage *fpage = NULL;
LOS_SpinLockSave(&oldRegion->unTypeData.rf.file->f_mapping->list_lock, &intSave);
fpage = OsFindGetEntry(oldRegion->unTypeData.rf.file->f_mapping, newRegion->pgOff + i);
if ((fpage != NULL) && (fpage->vmPage == page)) { /* cow page no need map */
OsAddMapInfo(fpage, &newVmSpace->archMmu, vaddr);
OsAddMapInfo(fpage, &newVmSpace->archMmu, vaddr);//添加文件页映射,记录页面被进程映射过
}
LOS_SpinUnlockRestore(&oldRegion->unTypeData.rf.file->f_mapping->list_lock, intSave);
}
......
......@@ -255,7 +255,7 @@ LosVmPage *OsVmPhysToPage(paddr_t pa, UINT8 segID)
offset = pa - seg->start;//得到偏移地址
return (seg->pageBase + (offset >> PAGE_SHIFT));//得到第n页page
}
//通过page获取空间的虚拟地址 参考OsArchMmuInit
//通过page获取内核空间的虚拟地址 参考OsArchMmuInit
VOID *OsVmPageToVaddr(LosVmPage *page)//
{
VADDR_T vaddr;
......
......@@ -36,7 +36,11 @@
#include "los_vm_filemap.h"
/* unmap a lru page by map record info caller need lru lock */
VOID OsUnmapPageLocked(LosFilePage *page, LosMapInfo *info)//通过映射记录信息调用者需要lru锁来取消一个映射lru页面
/**************************************************************************************************
解除文件页和进程(mmu)的映射关系
参数info记录了进程的MMU
**************************************************************************************************/
VOID OsUnmapPageLocked(LosFilePage *page, LosMapInfo *info)
{
if (page == NULL || info == NULL) {
VM_ERR("UnmapPage error input null!");
......@@ -46,16 +50,16 @@ VOID OsUnmapPageLocked(LosFilePage *page, LosMapInfo *info)//通过映射记录
LOS_ListDelete(&info->node);
LOS_AtomicDec(&page->vmPage->refCounts);
LOS_ArchMmuUnmap(info->archMmu, info->vaddr, 1);
LOS_MemFree(m_aucSysMem0, info);
LOS_MemFree(m_aucSysMem0, info);//释放虚拟
}
//取消所有文件页的映射
//解除文件页在所有进程的映射
VOID OsUnmapAllLocked(LosFilePage *page)
{
LosMapInfo *info = NULL;
LosMapInfo *next = NULL;
LOS_DL_LIST *immap = &page->i_mmap;
LOS_DL_LIST_FOR_EACH_ENTRY_SAFE(info, next, immap, LosMapInfo, node) {
LOS_DL_LIST_FOR_EACH_ENTRY_SAFE(info, next, immap, LosMapInfo, node) {//遍历 immap->info 链表
OsUnmapPageLocked(page, info);
}
}
......
......@@ -77,7 +77,7 @@ STATUS_T OsCheckMMapParams(VADDR_T vaddr, unsigned prot, unsigned long flags, si
return LOS_OK;
}
//匿名映射指的是swap分区
//线性区映射类型:匿名映射
STATUS_T OsAnonMMap(LosVmMapRegion *region)
{
LOS_SetRegionTypeAnon(region);
......
git add -A
git commit -m '文件是如何映射到内存的?理解页高速缓存(page cache)是关键
git commit -m '文件如何被分页读到页高速缓存,脏页数据又如何回写?
搜索 @note_pic 可以查看全部字符图
搜索 @note_why 是注者尚未看明白的地方,如果您看明白了,请告诉注者完善
搜索 @note_thinking 是注者的思考和吐槽的地方
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册