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

注解写时复制(Copy On Write)技术在鸿蒙内核的实现

搜索 @note_pic 方便理解画的字符图
搜索 @note_why 尚未看明白的地方,如果您看明白了,请告知完善
搜索 @note_thinking 一点思考和吐槽的地方
搜索 @note_#if0 由第三方项目提供不由内核源码中定义的极为重要的结构体,为方便理解而添加的
搜索 @note_good 给源码点赞
上级 48a7a977
...@@ -41,44 +41,47 @@ ...@@ -41,44 +41,47 @@
#if 0 //@note_#if0 #if 0 //@note_#if0
定义见于 ..\third_party\NuttX\include\nuttx\fs\fs.h 定义见于 ..\third_party\NuttX\include\nuttx\fs\fs.h
typedef volatile INT32 Atomic; typedef volatile INT32 Atomic;
struct page_mapping { //page_mapping描述的是一个文件在内存中被映射了多少页,<文件,文件页的关系>
LOS_DL_LIST page_list; /* all pages */ /* file mapped in VMM pages */
SPIN_LOCK_S list_lock; /* lock protecting it */ struct page_mapping {//记录文件页和文件关系的结构体,叫文件页映射
LosMux mux_lock; /* mutex lock * / LOS_DL_LIST page_list; /* all pages */ //链表上挂的是属于该文件的所有FilePage,这些页的内容都来源同一个文件
unsigned long nrpages; /* number of total pages */ SPIN_LOCK_S list_lock; /* lock protecting it */ //操作page_list的自旋锁
unsigned long flags; LosMux mux_lock; /* mutex lock */ // //操作page_mapping的互斥量
Atomic ref; /* reference counting */ unsigned long nrpages; /* number of total pages */ //page_list的节点数量
struct file *host; /* owner of this mapping */ unsigned long flags; //@note_why 全量代码中也没查到源码中对其操作
Atomic ref; /* reference counting */ //引用次数(自增/自减),对应add_mapping/dec_mapping
struct file *host; /* owner of this mapping *///属于哪个文件的映射
};
/* map: full_path(owner) <-> mapping */ //叫文件映射
struct file_map { //为在内核层面文件在内存的身份证,每个需映射到内存的文件必须创建一个file_map,都挂到全局g_file_mapping链表上
LOS_DL_LIST head; //链表节点,用于挂到g_file_mapping上
LosMux lock; /* lock to protect this mapping */
struct page_mapping mapping; //每个文件都有唯一的page_mapping标识其在内存的身份
char *owner; /* owner: full path of file *///文件全路径来标识唯一性
}; };
/* map: full_path(owner) <-> mapping * / struct file //文件系统最重要的两个结构体之一,另一个是inode
struct file_map {
LOS_DL_LIST head; //挂入文件链表的节点
LosMux lock; /* lock to protect this mapping * /
struct page_mapping mapping; //映射内容区
char *owner; /* owner: full path of file * /
};
struct file
{ {
unsigned int f_magicnum; /* file magic number */ unsigned int f_magicnum; /* file magic number */ //文件魔法数字
int f_oflags; /* Open mode flags */ int f_oflags; /* Open mode flags */ //打开模式标签
FAR struct inode *f_inode; /* Driver interface */ FAR struct inode *f_inode; /* Driver interface */ //设备驱动程序
loff_t f_pos; /* File position */ loff_t f_pos; /* File position */ //文件的位置
unsigned long f_refcount; /* reference count */ unsigned long f_refcount; /* reference count */ //被引用的数量,一个文件可被多个进程打开
char *f_path; /* File fullpath */ char *f_path; /* File fullpath */ //全路径
void *f_priv; /* Per file driver private data */ void *f_priv; /* Per file driver private data */ //文件私有数据
const char *f_relpath; /* realpath */ const char *f_relpath; /* realpath */ //真实路径
struct page_mapping *f_mapping; /* mapping file to memory */ struct page_mapping *f_mapping; /* mapping file to memory */ //与内存的映射 page-cache
void *f_dir; /* DIR struct for iterate the directory if open a directory */ void *f_dir; /* DIR struct for iterate the directory if open a directory */ //所在目录
}; };
#endif #endif
static struct file_map g_file_mapping = {0}; static struct file_map g_file_mapping = {0};//用于挂载所有文件的file_map
/************************************************************************************************** /**************************************************************************************************
初始化文件映射模块, 初始化文件映射模块,
file_map: 每个需映射到内存的文件必须创建一个 file_map,都挂到全g_file_mapping链表上 file_map: 每个需映射到内存的文件必须创建一个 file_map,都挂到全g_file_mapping链表上
page_mapping: 记录的是<文件,文件页>的关系,一个文件在操作过程中被映射成了多少个页, page_mapping: 记录的是<文件,文件页>的关系,一个文件在操作过程中被映射成了多少个页,
file:是文件系统管理层面的概念 file:是文件系统管理层面的概念
**************************************************************************************************/ **************************************************************************************************/
...@@ -132,8 +135,8 @@ void add_mapping(struct file *filep, const char *fullpath) ...@@ -132,8 +135,8 @@ void add_mapping(struct file *filep, const char *fullpath)
mapping = find_mapping_nolock(fullpath);//是否已有文件映射 mapping = find_mapping_nolock(fullpath);//是否已有文件映射
if (mapping) {//有映射过的情况 if (mapping) {//有映射过的情况
LOS_AtomicInc(&mapping->ref);//引用自增 LOS_AtomicInc(&mapping->ref);//引用自增
filep->f_mapping = mapping;//赋值 filep->f_mapping = mapping;//记录文件自己在内存的身份
mapping->host = filep; mapping->host = filep; //记录page_mapping的老板
(VOID)LOS_MuxUnlock(&g_file_mapping.lock);//释放锁 (VOID)LOS_MuxUnlock(&g_file_mapping.lock);//释放锁
return;//收工 return;//收工
} }
...@@ -227,7 +230,12 @@ void clear_file_mapping_nolock(const struct page_mapping *mapping) ...@@ -227,7 +230,12 @@ void clear_file_mapping_nolock(const struct page_mapping *mapping)
i++; i++;
} }
} }
//删除一个映射
/******************************************************************************
删除一个文件映射,需要有个三个地方删除才算断开了文件和内存的联系.
1. 进程文件
******************************************************************************/
int remove_mapping_nolock(const char *fullpath, const struct file *ex_filp) int remove_mapping_nolock(const char *fullpath, const struct file *ex_filp)
{ {
int fd; int fd;
...@@ -276,11 +284,11 @@ int remove_mapping_nolock(const char *fullpath, const struct file *ex_filp) ...@@ -276,11 +284,11 @@ int remove_mapping_nolock(const char *fullpath, const struct file *ex_filp)
} }
(VOID)LOS_MuxDestroy(&mapping->mux_lock); (VOID)LOS_MuxDestroy(&mapping->mux_lock);
clear_file_mapping_nolock(mapping);//清除自己 clear_file_mapping_nolock(mapping);//清除进程对page_mapping的映射
OsFileCacheRemove(mapping);//从缓存中删除自己 OsFileCacheRemove(mapping);//从页高速缓存中删除文件页
fmap = LOS_DL_LIST_ENTRY(mapping, fmap = LOS_DL_LIST_ENTRY(mapping,
struct file_map, mapping);//找到自己的inode struct file_map, mapping);//通过page_mapping找到fmap
LOS_ListDelete(&fmap->head);//从链表中摘除自己 LOS_ListDelete(&fmap->head);//从g_file_mapping链表上摘掉自己
LOS_MemFree(m_aucSysMem0, fmap); LOS_MemFree(m_aucSysMem0, fmap);
out: out:
......
...@@ -64,23 +64,24 @@ extern "C" { ...@@ -64,23 +64,24 @@ extern "C" {
#if 0 //@note_#if0 #if 0 //@note_#if0
//page_mapping描述的是一个文件在内存中被映射了多少页,<文件,文件页的关系> //page_mapping描述的是一个文件在内存中被映射了多少页,<文件,文件页的关系>
/* file mapped in VMM pages */ /* file mapped in VMM pages */
struct page_mapping { struct page_mapping {//记录文件页和文件关系的结构体,叫文件页映射
LOS_DL_LIST page_list; /* all pages */ //链表上挂的是属于该文件的所有FilePage,这些页的内容都来源同一个文件 LOS_DL_LIST page_list; /* all pages */ //链表上挂的是属于该文件的所有FilePage,这些页的内容都来源同一个文件
SPIN_LOCK_S list_lock; /* lock protecting it */ SPIN_LOCK_S list_lock; /* lock protecting it */ //操作page_list的自旋锁
LosMux mux_lock; /* mutex lock */ // LosMux mux_lock; /* mutex lock */ // //操作page_mapping的互斥量
unsigned long nrpages; /* number of total pages */ unsigned long nrpages; /* number of total pages */ //page_list的节点数量
unsigned long flags; unsigned long flags; //@note_why 全量代码中也没查到源码中对其操作
Atomic ref; /* reference counting */ Atomic ref; /* reference counting */ //引用次数(自增/自减),对应add_mapping/dec_mapping
struct file *host; /* owner of this mapping *///属于哪个文件的映射 struct file *host; /* owner of this mapping *///属于哪个文件的映射
}; };
/* map: full_path(owner) <-> mapping */ /* map: full_path(owner) <-> mapping */ //叫文件映射
struct file_map { struct file_map { //为在内核层面文件在内存的身份证,每个需映射到内存的文件必须创建一个file_map,都挂到全局g_file_mapping链表上
LOS_DL_LIST head; LOS_DL_LIST head; //链表节点,用于挂到g_file_mapping上
LosMux lock; /* lock to protect this mapping */ LosMux lock; /* lock to protect this mapping */
struct page_mapping mapping; struct page_mapping mapping; //每个文件都有唯一的page_mapping标识其在内存的身份
char *owner; /* owner: full path of file */ char *owner; /* owner: full path of file *///文件全路径来标识唯一性
}; };
#endif #endif
//文件页结构体 //文件页结构体
...@@ -89,7 +90,7 @@ typedef struct FilePage { ...@@ -89,7 +90,7 @@ typedef struct FilePage {
LOS_DL_LIST lru; //lru节点, 结合 LosVmPhysSeg: LOS_DL_LIST lruList[VM_NR_LRU_LISTS] 理解 LOS_DL_LIST lru; //lru节点, 结合 LosVmPhysSeg: LOS_DL_LIST lruList[VM_NR_LRU_LISTS] 理解
LOS_DL_LIST i_mmap; /* list of mappings */ //链表记录文件页被哪些进程映射 MapInfo.node挂上来 LOS_DL_LIST i_mmap; /* list of mappings */ //链表记录文件页被哪些进程映射 MapInfo.node挂上来
UINT32 n_maps; /* num of mapping */ //记录被进程映射的次数 UINT32 n_maps; /* num of mapping */ //记录被进程映射的次数
struct VmPhysSeg *physSeg; /* physical memory that file page belongs to */ //物理内存是唯一的 struct VmPhysSeg *physSeg; /* physical memory that file page belongs to */ //物理段:物理页框 = 1:N
struct VmPage *vmPage; //物理页框 struct VmPage *vmPage; //物理页框
struct page_mapping *mapping; //此结构由文件系统提供,用于描述装入点 见于 ..\third_party\NuttX\include\nuttx\fs\fs.h struct page_mapping *mapping; //此结构由文件系统提供,用于描述装入点 见于 ..\third_party\NuttX\include\nuttx\fs\fs.h
VM_OFFSET_T pgoff; //页标,文件被切成一页一页读到内存 VM_OFFSET_T pgoff; //页标,文件被切成一页一页读到内存
......
...@@ -95,11 +95,11 @@ typedef struct VmFault { ...@@ -95,11 +95,11 @@ typedef struct VmFault {
VADDR_T *pageKVaddr; /* KVaddr of pagefault's vm page's paddr */ //page cache中的虚拟地址 VADDR_T *pageKVaddr; /* KVaddr of pagefault's vm page's paddr */ //page cache中的虚拟地址
} LosVmPgFault; } LosVmPgFault;
//虚拟内存文件操作函数指针,上层开发可理解为 class 里的方法,注意是对线性区的操作 //虚拟内存文件操作函数指针,上层开发可理解为 class 里的方法,注意是对线性区的操作
struct VmFileOps {// 文件操作 struct VmFileOps {// 文件操作 见于g_commVmOps
void (*open)(struct VmMapRegion *region); //打开 void (*open)(struct VmMapRegion *region); //打开
void (*close)(struct VmMapRegion *region);//关闭 void (*close)(struct VmMapRegion *region);//关闭
int (*fault)(struct VmMapRegion *region, LosVmPgFault *pageFault);//缺页 int (*fault)(struct VmMapRegion *region, LosVmPgFault *pageFault);//缺页,OsVmmFileFault
void (*remove)(struct VmMapRegion *region, LosArchMmu *archMmu, VM_OFFSET_T offset);//删除 void (*remove)(struct VmMapRegion *region, LosArchMmu *archMmu, VM_OFFSET_T offset);//删除 OsVmmFileRemove
}; };
struct VmMapRegion {//线性区描述符,内核通过线性区管理虚拟地址,而线性地址就是虚拟地址 struct VmMapRegion {//线性区描述符,内核通过线性区管理虚拟地址,而线性地址就是虚拟地址
......
...@@ -147,7 +147,7 @@ STATIC STATUS_T OsDoReadFault(LosVmMapRegion *region, LosVmPgFault *vmPgFault)// ...@@ -147,7 +147,7 @@ STATIC STATUS_T OsDoReadFault(LosVmMapRegion *region, LosVmPgFault *vmPgFault)//
return LOS_ERRNO_VM_NO_MEMORY; return LOS_ERRNO_VM_NO_MEMORY;
} }
/* numap a page when cow happend only */ /* numap a page when cow happend only *///仅当写时拷贝发生时取消页面映射
STATIC LosVmPage *OsCowUnmapOrg(LosArchMmu *archMmu, LosVmMapRegion *region, LosVmPgFault *vmf) STATIC LosVmPage *OsCowUnmapOrg(LosArchMmu *archMmu, LosVmMapRegion *region, LosVmPgFault *vmf)
{ {
UINT32 intSave; UINT32 intSave;
...@@ -404,7 +404,7 @@ STATUS_T OsVmPageFaultHandler(VADDR_T vaddr, UINT32 flags, ExcContext *frame) ...@@ -404,7 +404,7 @@ STATUS_T OsVmPageFaultHandler(VADDR_T vaddr, UINT32 flags, ExcContext *frame)
vmPgFault.vaddr = vaddr;//虚拟地址 vmPgFault.vaddr = vaddr;//虚拟地址
vmPgFault.pgoff = ((vaddr - region->range.base) >> PAGE_SHIFT) + region->pgOff;//计算出文件读取位置 vmPgFault.pgoff = ((vaddr - region->range.base) >> PAGE_SHIFT) + region->pgOff;//计算出文件读取位置
vmPgFault.flags = flags; vmPgFault.flags = flags;
vmPgFault.pageKVaddr = NULL;//没有物理地址 vmPgFault.pageKVaddr = NULL;//缺失页初始化没有物理地址
status = OsDoFileFault(region, &vmPgFault, flags);//缺页处理 status = OsDoFileFault(region, &vmPgFault, flags);//缺页处理
if (status) { if (status) {
......
...@@ -171,7 +171,9 @@ STATIC LosFilePage *OsPagecacheGetPageAndFill(struct file *filp, VM_OFFSET_T pgO ...@@ -171,7 +171,9 @@ STATIC LosFilePage *OsPagecacheGetPageAndFill(struct file *filp, VM_OFFSET_T pgO
return page; return page;
} }
//从文件filp中读 size 数据到buf中 /******************************************************************************
从文件filp中读 size 数据到buf中
******************************************************************************/
ssize_t OsMappingRead(struct file *filp, char *buf, size_t size) ssize_t OsMappingRead(struct file *filp, char *buf, size_t size)
{ {
INT32 ret; INT32 ret;
...@@ -212,7 +214,7 @@ ssize_t OsMappingRead(struct file *filp, char *buf, size_t size) ...@@ -212,7 +214,7 @@ ssize_t OsMappingRead(struct file *filp, char *buf, size_t size)
readSize = MIN2((PAGE_SIZE - offInPage), readLeft); readSize = MIN2((PAGE_SIZE - offInPage), readLeft);
(VOID)memcpy_s((VOID *)buf, readLeft, (char *)kvaddr + offInPage, readSize); (VOID)memcpy_s((VOID *)buf, readLeft, (char *)kvaddr + offInPage, readSize);//将文件页的数据拷贝到buf
buf += readSize; buf += readSize;
readLeft -= readSize; readLeft -= readSize;
readTotal += readSize; readTotal += readSize;
...@@ -227,69 +229,71 @@ ssize_t OsMappingRead(struct file *filp, char *buf, size_t size) ...@@ -227,69 +229,71 @@ ssize_t OsMappingRead(struct file *filp, char *buf, size_t size)
return readTotal; return readTotal;
} }
/******************************************************************************
向页高速缓存中写入数据
******************************************************************************/
ssize_t OsMappingWrite(struct file *filp, const char *buf, size_t size) ssize_t OsMappingWrite(struct file *filp, const char *buf, size_t size)
{ {
VADDR_T kvaddr; VADDR_T kvaddr;
UINT32 intSave; UINT32 intSave;
INT32 writeSize = 0; INT32 writeSize = 0;
size_t writeLeft = size; size_t writeLeft = size;
VM_OFFSET_T pos = file_seek(filp, 0, SEEK_CUR); VM_OFFSET_T pos = file_seek(filp, 0, SEEK_CUR);//获得要从文件那个位置写入
VM_OFFSET_T pgOff = pos >> PAGE_SHIFT; VM_OFFSET_T pgOff = pos >> PAGE_SHIFT; //计算出文件页的序号
INT32 offInPage = pos % PAGE_SIZE; INT32 offInPage = pos % PAGE_SIZE; //计算出文件页内偏移量
LosFilePage *page = NULL; LosFilePage *page = NULL;
struct page_mapping *mapping = filp->f_mapping; struct page_mapping *mapping = filp->f_mapping;//得到文件页映射
INT32 nPages = (ROUNDUP(pos + size, PAGE_SIZE) - ROUNDDOWN(pos, PAGE_SIZE)) >> PAGE_SHIFT; INT32 nPages = (ROUNDUP(pos + size, PAGE_SIZE) - ROUNDDOWN(pos, PAGE_SIZE)) >> PAGE_SHIFT;
LOS_SpinLockSave(&mapping->list_lock, &intSave); LOS_SpinLockSave(&mapping->list_lock, &intSave);
for (INT32 i = 0; i < nPages; i++, pgOff++) { for (INT32 i = 0; i < nPages; i++, pgOff++) {//遍历pgOff,pgOff表示页序号
page = OsFindGetEntry(mapping, pgOff); page = OsFindGetEntry(mapping, pgOff);//通过页序号拿到页高速缓存中的文件页
if (page) { if (page) {//页高速缓存中有该页
kvaddr = (VADDR_T)(UINTPTR)OsVmPageToVaddr(page->vmPage); kvaddr = (VADDR_T)(UINTPTR)OsVmPageToVaddr(page->vmPage);//通过文件页获得虚拟地址(即数据写入地址)
OsSetPageLocked(page->vmPage); OsSetPageLocked(page->vmPage);//写操作要上锁
OsPageRefIncLocked(page); OsPageRefIncLocked(page);//因为页可能被其他进程所引用,所以也要上锁
} else { } else {//页高速缓存中没有该页
page = OsPageCacheAlloc(mapping, pgOff); page = OsPageCacheAlloc(mapping, pgOff);//分配一个文件页
if (page == NULL) { if (page == NULL) {
VM_ERR("Failed to alloc a page frame"); VM_ERR("Failed to alloc a page frame");
break; break;
} }
kvaddr = (VADDR_T)(UINTPTR)OsVmPageToVaddr(page->vmPage); kvaddr = (VADDR_T)(UINTPTR)OsVmPageToVaddr(page->vmPage);//通过文件页获得虚拟地址(即数据写入地址)
OsAddToPageacheLru(page, mapping, pgOff); OsAddToPageacheLru(page, mapping, pgOff);//将页加入页高速缓存中
OsSetPageLocked(page->vmPage); OsSetPageLocked(page->vmPage);//写操作要上锁,因为是新页,所以不要 OsPageRefIncLocked
} }
//准备工作做好了,才是写入页高速缓存,注意这里只是写缓存,而不是写最终的文件
writeSize = MIN2((PAGE_SIZE - offInPage), writeLeft);//计算出要写入的大小
writeSize = MIN2((PAGE_SIZE - offInPage), writeLeft); (VOID)memcpy_s((char *)(UINTPTR)kvaddr + offInPage, writeLeft, buf, writeSize);//将内容拷贝到页高速缓存
buf += writeSize; //计算出剩余未写完buf位置
writeLeft -= writeSize; //计算出剩余大小
(VOID)memcpy_s((char *)(UINTPTR)kvaddr + offInPage, writeLeft, buf, writeSize); OsMarkPageDirty(page, NULL, offInPage, writeSize);//标记文件页为脏页,从offInPage位置开始脏的.
buf += writeSize; //注意这里只是做标记,内核并不会马上将数据写入磁盘文件,而是在缺页时写入磁盘,就是著名的 写时拷贝技术.
writeLeft -= writeSize; offInPage = 0;//脏位置复位
OsMarkPageDirty(page, NULL, offInPage, writeSize); OsCleanPageLocked(page->vmPage);//释放文件页锁
offInPage = 0;
OsCleanPageLocked(page->vmPage);
} }
LOS_SpinUnlockRestore(&mapping->list_lock, intSave); LOS_SpinUnlockRestore(&mapping->list_lock, intSave);
file_seek(filp, pos + size - writeLeft, SEEK_SET); file_seek(filp, pos + size - writeLeft, SEEK_SET);//最后文件要seek到指定的位置
return (size - writeLeft); return (size - writeLeft);
} }
//解除文件页和进程的映射关系
STATIC VOID OsPageCacheUnmap(LosFilePage *fpage, LosArchMmu *archMmu, VADDR_T vaddr) STATIC VOID OsPageCacheUnmap(LosFilePage *fpage, LosArchMmu *archMmu, VADDR_T vaddr)
{ {
UINT32 intSave; UINT32 intSave;
LosMapInfo *info = NULL; LosMapInfo *info = NULL;
LOS_SpinLockSave(&fpage->physSeg->lruLock, &intSave); LOS_SpinLockSave(&fpage->physSeg->lruLock, &intSave);
info = OsGetMapInfo(fpage, archMmu, vaddr); info = OsGetMapInfo(fpage, archMmu, vaddr);//获取文件页在进程的映射信息
if (info == NULL) { if (info == NULL) {
VM_ERR("OsPageCacheUnmap get map info fail!"); VM_ERR("OsPageCacheUnmap get map info fail!");
} else { } else {
OsUnmapPageLocked(fpage, info); OsUnmapPageLocked(fpage, info);//解除进程和文件页映射关系
} }
if (!(OsIsPageMapped(fpage) && ((fpage->flags & VM_MAP_REGION_FLAG_PERM_EXECUTE) || if (!(OsIsPageMapped(fpage) && ((fpage->flags & VM_MAP_REGION_FLAG_PERM_EXECUTE) ||
OsIsPageDirty(fpage->vmPage)))) { OsIsPageDirty(fpage->vmPage)))) {
...@@ -397,13 +401,13 @@ STATIC INT32 OsFlushDirtyPage(LosFilePage *fpage) ...@@ -397,13 +401,13 @@ STATIC INT32 OsFlushDirtyPage(LosFilePage *fpage)
size_t len; size_t len;
char *buff = NULL; char *buff = NULL;
VM_OFFSET_T oldPos; VM_OFFSET_T oldPos;
struct file *file = fpage->mapping->host;//struct file *host; /* owner of this mapping */ 此映射的所有者 struct file *file = fpage->mapping->host;/* owner of this mapping */ //此映射属于哪个文件,注意<file,page_mapping>是1:1的关系.
if ((file == NULL) || (file->f_inode == NULL)) {//nuttx file 文件描述符 if ((file == NULL) || (file->f_inode == NULL)) {//nuttx file 文件描述符
VM_ERR("page cache file error"); VM_ERR("page cache file error");
return LOS_NOK; return LOS_NOK;
} }
oldPos = file_seek(file, 0, SEEK_CUR);//定位到当前位置 oldPos = file_seek(file, 0, SEEK_CUR);////先记录老位置,因为写完页数据后要seek回老位置
buff = (char *)OsVmPageToVaddr(fpage->vmPage);//获取页面的虚拟地址 buff = (char *)OsVmPageToVaddr(fpage->vmPage);//获取页面的虚拟地址
file_seek(file, (((UINT32)fpage->pgoff << PAGE_SHIFT) + fpage->dirtyOff), SEEK_SET);//移到页面脏数据位置,注意不是整个页面都脏了,可能只脏了一部分 file_seek(file, (((UINT32)fpage->pgoff << PAGE_SHIFT) + fpage->dirtyOff), SEEK_SET);//移到页面脏数据位置,注意不是整个页面都脏了,可能只脏了一部分
len = fpage->dirtyEnd - fpage->dirtyOff;//计算出脏数据长度 len = fpage->dirtyEnd - fpage->dirtyOff;//计算出脏数据长度
...@@ -413,7 +417,7 @@ STATIC INT32 OsFlushDirtyPage(LosFilePage *fpage) ...@@ -413,7 +417,7 @@ STATIC INT32 OsFlushDirtyPage(LosFilePage *fpage)
(VOID)file_seek(file, oldPos, SEEK_SET);//移回老位置 (VOID)file_seek(file, oldPos, SEEK_SET);//移回老位置
return LOS_OK; return LOS_OK;
} }
//i_mops: Operations on a mountpoint 可前往 nuttx 查看 fat_operations.writepage为null //
if (file->f_inode && file->f_inode->u.i_mops->writepage) {//如果mount驱动程序提供了写页的实现 if (file->f_inode && file->f_inode->u.i_mops->writepage) {//如果mount驱动程序提供了写页的实现
ret = file->f_inode->u.i_mops->writepage(file, (buff + fpage->dirtyOff), len);//使用mount接口回写数据 ret = file->f_inode->u.i_mops->writepage(file, (buff + fpage->dirtyOff), len);//使用mount接口回写数据
} else { } else {
...@@ -424,7 +428,7 @@ STATIC INT32 OsFlushDirtyPage(LosFilePage *fpage) ...@@ -424,7 +428,7 @@ STATIC INT32 OsFlushDirtyPage(LosFilePage *fpage)
} }
ret = (ret <= 0) ? LOS_NOK : LOS_OK; ret = (ret <= 0) ? LOS_NOK : LOS_OK;
OsCleanPageDirty(fpage->vmPage);//撕掉脏页标签,还清白之身,哈哈,臣妾又干净了. OsCleanPageDirty(fpage->vmPage);//撕掉脏页标签,还清白之身,哈哈,臣妾又干净了.
(VOID)file_seek(file, oldPos, SEEK_SET);//写完了脏数据,切回到老位置,一定要回到老位置,因为回写脏数据是内核的行为,而不是用户的行为 (VOID)file_seek(file, oldPos, SEEK_SET);//不管是否写正常,一定要切回到原来位置,很重要!!! 写页是内核的操作,不是用户的操作行为
return ret; return ret;
} }
...@@ -528,7 +532,7 @@ INT32 OsVmmFileFault(LosVmMapRegion *region, LosVmPgFault *vmf) ...@@ -528,7 +532,7 @@ INT32 OsVmmFileFault(LosVmMapRegion *region, LosVmPgFault *vmf)
/* get or create a new cache node */ /* get or create a new cache node */
LOS_SpinLockSave(&mapping->list_lock, &intSave); LOS_SpinLockSave(&mapping->list_lock, &intSave);
fpage = OsFindGetEntry(mapping, vmf->pgoff);//获取文件页 fpage = OsFindGetEntry(mapping, vmf->pgoff);//获取文件页
if (fpage != NULL) {//找到了,该页已经在页高速缓存中 if (fpage != NULL) {//找到了,说明该页已经在页高速缓存中
OsPageRefIncLocked(fpage); OsPageRefIncLocked(fpage);
} else {//真的缺页了,页高速缓存中没找到 } else {//真的缺页了,页高速缓存中没找到
fpage = OsPageCacheAlloc(mapping, vmf->pgoff);//分配一个文件页,将数据初始化好,包括vmpage(物理页框) fpage = OsPageCacheAlloc(mapping, vmf->pgoff);//分配一个文件页,将数据初始化好,包括vmpage(物理页框)
...@@ -537,36 +541,36 @@ INT32 OsVmmFileFault(LosVmMapRegion *region, LosVmPgFault *vmf) ...@@ -537,36 +541,36 @@ INT32 OsVmmFileFault(LosVmMapRegion *region, LosVmPgFault *vmf)
LOS_SpinUnlockRestore(&mapping->list_lock, intSave); LOS_SpinUnlockRestore(&mapping->list_lock, intSave);
return LOS_NOK; return LOS_NOK;
} }
newCache = true; newCache = true;//分配了新文件页
} }
OsSetPageLocked(fpage->vmPage);//对vmpage上锁 OsSetPageLocked(fpage->vmPage);//对vmpage上锁
LOS_SpinUnlockRestore(&mapping->list_lock, intSave); LOS_SpinUnlockRestore(&mapping->list_lock, intSave);
kvaddr = OsVmPageToVaddr(fpage->vmPage);//获取该页框在内核空间的虚拟地址 kvaddr = OsVmPageToVaddr(fpage->vmPage);//获取该页框在内核空间的虚拟地址,因为 page cache本身就是在内核空间,
/* read file to new page cache */ /* read file to new page cache */
if (newCache) {//新cache if (newCache) {//新cache
oldPos = file_seek(file, 0, SEEK_CUR);//相对当前位置偏移,记录偏移后的位置 NUTTX oldPos = file_seek(file, 0, SEEK_CUR);//先记录老位置,因为读完页数据后要seek回老位置
file_seek(file, fpage->pgoff << PAGE_SHIFT, SEEK_SET);// 相对开始位置偏移 file_seek(file, fpage->pgoff << PAGE_SHIFT, SEEK_SET);//定位到要开始读的位置,从fpage->pgoff << PAGE_SHIFT 文件位置开始读.
if (file->f_inode && file->f_inode->u.i_mops->readpage) {//见于 NUTTX fat_operations.readpage = NULL if (file->f_inode && file->f_inode->u.i_mops->readpage) {//见于 NUTTX fat_operations.readpage = NULL
ret = file->f_inode->u.i_mops->readpage(file, (char *)kvaddr, PAGE_SIZE);//从文件中读取一页数据到kvaddr ret = file->f_inode->u.i_mops->readpage(file, (char *)kvaddr, PAGE_SIZE);//从文件中读取一页数据到kvaddr
} else { } else {
ret = file_read(file, kvaddr, PAGE_SIZE);//将4K数据读到虚拟地址,磁盘数据进入物理页框 fpage->pgoff << PAGE_SHIFT 处 ret = file_read(file, kvaddr, PAGE_SIZE);//将磁盘4K数据读到物理页框的kvaddr地址中,真正写到了fpage->vmPage->physAddr中.
} }
file_seek(file, oldPos, SEEK_SET);//再切回原来位置,一定要切回,很重要!!! 因为缺页是内核的操作,不是用户逻辑行为 file_seek(file, oldPos, SEEK_SET);//不管是否读写正常,一定要切回到原来位置,很重要!!! 因为缺页->读页是内核的操作,不是用户的操作行为
if (ret == 0) { if (ret == 0) {
VM_ERR("Failed to read from file!"); VM_ERR("Failed to read from file!");
OsReleaseFpage(mapping, fpage); OsReleaseFpage(mapping, fpage);
return LOS_NOK; return LOS_NOK;
} }
LOS_SpinLockSave(&mapping->list_lock, &intSave); LOS_SpinLockSave(&mapping->list_lock, &intSave);
OsAddToPageacheLru(fpage, mapping, vmf->pgoff);//将fpage加入 pageCache 和 LruCache OsAddToPageacheLru(fpage, mapping, vmf->pgoff);//将fpage挂入pageCache 和 LruCache
LOS_SpinUnlockRestore(&mapping->list_lock, intSave); LOS_SpinUnlockRestore(&mapping->list_lock, intSave);
} }
LOS_SpinLockSave(&mapping->list_lock, &intSave); LOS_SpinLockSave(&mapping->list_lock, &intSave);
/* cow fault case no need to save mapinfo */ /* cow fault case no need to save mapinfo */
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))) {
OsAddMapInfo(fpage, &region->space->archMmu, (vaddr_t)vmf->vaddr);//既不共享,也不可写 就不用回写磁盘啦 OsAddMapInfo(fpage, &region->space->archMmu, (vaddr_t)vmf->vaddr);//添加<虚拟地址,文件页>的映射关系,如此进程以后就能通过虚拟地址操作文件页了.
fpage->flags = region->regionFlags; fpage->flags = region->regionFlags;
} }
...@@ -575,7 +579,7 @@ INT32 OsVmmFileFault(LosVmMapRegion *region, LosVmPgFault *vmf) ...@@ -575,7 +579,7 @@ INT32 OsVmmFileFault(LosVmMapRegion *region, LosVmPgFault *vmf)
OsMarkPageDirty(fpage, region, 0, 0);//标记为脏页,要回写磁盘,内核会在适当的时候回写磁盘 OsMarkPageDirty(fpage, region, 0, 0);//标记为脏页,要回写磁盘,内核会在适当的时候回写磁盘
} }
vmf->pageKVaddr = kvaddr;//将文件页的虚拟地址带给缺陷页 vmf->pageKVaddr = kvaddr;//缺陷页记录文件页的虚拟地址
LOS_SpinUnlockRestore(&mapping->list_lock, intSave); LOS_SpinUnlockRestore(&mapping->list_lock, intSave);
return LOS_OK; return LOS_OK;
} }
......
...@@ -197,7 +197,7 @@ VOID OsPageRefDecNoLock(LosFilePage *fpage) ...@@ -197,7 +197,7 @@ VOID OsPageRefDecNoLock(LosFilePage *fpage)
page = fpage->vmPage; page = fpage->vmPage;
isOrgActive = OsIsPageActive(page); isOrgActive = OsIsPageActive(page);
if (!OsIsPageReferenced(page) && OsIsPageActive(page)) { if (!OsIsPageReferenced(page) && OsIsPageActive(page)) {//[ref:0,act:1]的情况
OsCleanPageActive(page); OsCleanPageActive(page);
OsSetPageReferenced(page); OsSetPageReferenced(page);
} else if (OsIsPageReferenced(page)) { } else if (OsIsPageReferenced(page)) {
...@@ -208,7 +208,7 @@ VOID OsPageRefDecNoLock(LosFilePage *fpage) ...@@ -208,7 +208,7 @@ VOID OsPageRefDecNoLock(LosFilePage *fpage)
OsMoveToInactiveList(fpage); OsMoveToInactiveList(fpage);
} }
} }
//缩小活动页链表 //缩小活动页链表
VOID OsShrinkActiveList(LosVmPhysSeg *physSeg, int nScan) VOID OsShrinkActiveList(LosVmPhysSeg *physSeg, int nScan)
{ {
LosFilePage *fpage = NULL; LosFilePage *fpage = NULL;
......
#ifndef _NOTE_FS_H
#define _NOTE_FS_H
#if 0
共享内存写保持同步
文件页下标不变,写的内容大于4k时,产生缺页继续写page cache ,
脏页,凭文件页下标+脏页偏移量写回磁盘
#endif
#endif
#ifndef _NOTE_IPC_H #ifndef _NOTE_IPC_H
#define _NOTE_IPC_H #define _NOTE_IPC_H
#if 0
/****************************************************************
信号量是为了解决task的资源同步问题,有多少资源就设多大的资源最大值 信号量是为了解决task的资源同步问题,有多少资源就设多大的资源最大值
信号量机制: 信号量机制:
以一个停车场的运作为例。假设停车场只有三个车位,一开始三个车位都是空的。这时如果同时来了五辆车, 以一个停车场的运作为例。假设停车场只有三个车位,一开始三个车位都是空的。这时如果同时来了五辆车,
...@@ -20,11 +17,8 @@ ...@@ -20,11 +17,8 @@
当前资源计数器决定不会大于最大资源计数 当前资源计数器决定不会大于最大资源计数
最大资源计数,表示可以控件的最大资源数量 最大资源计数,表示可以控件的最大资源数量
当前资源计数,表示当前可用资源的数量 当前资源计数,表示当前可用资源的数量
#endif
****************************************************************/
/* */
/****************************************************************************** /******************************************************************************
******************************************************************************/ ******************************************************************************/
......
git add -A git add -A
git commit -m '注解1.对缺页中断的处理 2.物理地址的管理 git commit -m '注解写时复制(Copy On Write)技术在鸿蒙内核的实现
搜索 @note_pic 可以查看全部字符图 搜索 @note_pic 方便理解画的字符图
搜索 @note_why 是注者尚未看明白的地方,如果您看明白了,请告诉注者完善 搜索 @note_why 尚未看明白的地方,如果您看明白了,请告知完善
搜索 @note_thinking 是注者的思考和吐槽的地方 搜索 @note_thinking 一点思考和吐槽的地方
搜索 @note_#if0 是由第三方项目提供不由内核源码中定义的极为重要的结构体,为方便理解而添加的。 搜索 @note_#if0 由第三方项目提供不由内核源码中定义的极为重要的结构体,为方便理解而添加的
搜索 @note_good 是注者给源码点赞的地方 搜索 @note_good 给源码点赞
' '
git push origin master git push origin master
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册