更新日期: 2022/06/01 来源: https://gitee.com/weharmony/kernel_liteos_a_note
los_vm_filemap.c 文件参考

浏览源代码.

函数

VOID ResetPageCacheHitInfo (int *try, int *hit)
 
STATIC VOID OsPageCacheAdd (LosFilePage *page, struct page_mapping *mapping, VM_OFFSET_T pgoff)
 
VOID OsAddToPageacheLru (LosFilePage *page, struct page_mapping *mapping, VM_OFFSET_T pgoff)
 将页面加到活动文件页LRU链表上 更多...
 
VOID OsPageCacheDel (LosFilePage *fpage)
 从页高速缓存上删除页 更多...
 
VOID OsAddMapInfo (LosFilePage *page, LosArchMmu *archMmu, VADDR_T vaddr)
 
LosMapInfoOsGetMapInfo (LosFilePage *page, LosArchMmu *archMmu, VADDR_T vaddr)
 通过虚拟地址获取文件页映射信息,archMmu每个进程都有属于自己的mmu 更多...
 
VOID OsDeletePageCacheLru (LosFilePage *page)
 删除页高速缓存和LRU,对应 OsAddToPageacheLru 更多...
 
STATIC VOID OsPageCacheUnmap (LosFilePage *fpage, LosArchMmu *archMmu, VADDR_T vaddr)
 
VOID OsVmmFileRemove (LosVmMapRegion *region, LosArchMmu *archMmu, VM_OFFSET_T pgoff)
 删除文件 更多...
 
VOID OsMarkPageDirty (LosFilePage *fpage, LosVmMapRegion *region, INT32 off, INT32 len)
 标记page为脏页 进程修改了高速缓存里的数据时,该页就被内核标记为脏页 更多...
 
STATIC UINT32 GetDirtySize (LosFilePage *fpage, struct Vnode *vnode)
 
STATIC INT32 OsFlushDirtyPage (LosFilePage *fpage)
 冲洗脏页,回写磁盘 更多...
 
LosFilePageOsDumpDirtyPage (LosFilePage *oldFPage)
 备份脏页,老脏页撕掉脏页标签 更多...
 
VOID OsDoFlushDirtyPage (LosFilePage *fpage)
 冲洗脏页数据,将脏页数据回写磁盘 更多...
 
STATIC VOID OsReleaseFpage (struct page_mapping *mapping, LosFilePage *fpage)
 
VOID OsDelMapInfo (LosVmMapRegion *region, LosVmPgFault *vmf, BOOL cleanDirty)
 删除映射信息 更多...
 
INT32 OsVmmFileFault (LosVmMapRegion *region, LosVmPgFault *vmf)
 
VOID OsFileCacheFlush (struct page_mapping *mapping)
 文件缓存冲洗,把所有fpage冲洗一边,把脏页洗到dirtyList中,配合OsFileCacheRemove理解 更多...
 
VOID OsFileCacheRemove (struct page_mapping *mapping)
 
INT32 OsVfsFileMmap (struct file *filep, LosVmMapRegion *region)
 
STATUS_T OsNamedMMap (struct file *filep, LosVmMapRegion *region)
 
LosFilePageOsFindGetEntry (struct page_mapping *mapping, VM_OFFSET_T pgoff)
 
LosFilePageOsPageCacheAlloc (struct page_mapping *mapping, VM_OFFSET_T pgoff)
 

变量

static int g_totalPageCacheTry = 0
 
static int g_totalPageCacheHit = 0
 
LosVmFileOps g_commVmOps
 虚拟内存文件操作实现类 更多...
 

函数说明

◆ GetDirtySize()

STATIC UINT32 GetDirtySize ( LosFilePage fpage,
struct Vnode vnode 
)

在文件 los_vm_filemap.c251 行定义.

252{
253 UINT32 fileSize;
254 UINT32 dirtyBegin;
255 UINT32 dirtyEnd;
256 struct stat buf_stat;
257
258 if (stat(vnode->filePath, &buf_stat) != OK) {
259 VM_ERR("FlushDirtyPage get file size failed. (filePath=%s)", vnode->filePath);
260 return 0;
261 }
262
263 fileSize = buf_stat.st_size;
264 dirtyBegin = ((UINT32)fpage->pgoff << PAGE_SHIFT);
265 dirtyEnd = dirtyBegin + PAGE_SIZE;
266
267 if (dirtyBegin >= fileSize) {
268 return 0;
269 }
270
271 if (dirtyEnd >= fileSize) {
272 return fileSize - dirtyBegin;
273 }
274
275 return PAGE_SIZE;
276}
unsigned int UINT32
Definition: los_typedef.h:57
VM_OFFSET_T pgoff
页标,文件被切成一页一页读到内存
char * filePath
Definition: vnode.h:182
这是这个函数的调用关系图:

◆ OsAddMapInfo()

VOID OsAddMapInfo ( LosFilePage page,
LosArchMmu archMmu,
VADDR_T  vaddr 
)

在文件 los_vm_filemap.c127 行定义.

128{
129 LosMapInfo *info = NULL;
130
131 info = (LosMapInfo *)LOS_MemAlloc(m_aucSysMem0, sizeof(LosMapInfo));//分配一个映射信息
132 if (info == NULL) {
133 VM_ERR("OsAddMapInfo alloc memory failed!");
134 return;
135 }
136 info->page = page; //文件页
137 info->archMmu = archMmu;//进程MMU,完成虚实地址转换
138 info->vaddr = vaddr; //虚拟地址
139
140 LOS_ListAdd(&page->i_mmap, &info->node);//将 LosMapInfo 节点挂入链表
141 page->n_maps++;//映射总数++
142}
LITE_OS_SEC_ALW_INLINE STATIC INLINE VOID LOS_ListAdd(LOS_DL_LIST *list, LOS_DL_LIST *node)
Insert a new node to a doubly linked list.
Definition: los_list.h:217
VOID * LOS_MemAlloc(VOID *pool, UINT32 size)
从指定内存池中申请size长度的内存,注意这可不是从内核堆空间中申请内存
Definition: los_memory.c:1123
UINT8 * m_aucSysMem0
异常交互动态内存池地址的起始地址,当不支持异常交互特性时,m_aucSysMem0等于m_aucSysMem1。
Definition: los_memory.c:107
LOS_DL_LIST i_mmap
UINT32 n_maps
虚拟地址和文件页的映射信息,在一个进程使用文件页之前,需要提前做好文件页在此内存空间的映射关系,如此通过虚拟内存就可以对文件页读写操作.
LosFilePage * page
文件页中只记录物理地址,是不会变的.但它是需要被多个进程访问,和映射的.
VADDR_T vaddr
虚拟地址.每个进程访问同一个文件页的虚拟地址都是不一样的
LosArchMmu * archMmu
mmu完成vaddr和page->vmPage->physAddr物理地址的映射
LOS_DL_LIST node
节点,挂到page->i_mmap链表上.链表上记录要操作文件页的进程对这个page的映射信息
函数调用图:
这是这个函数的调用关系图:

◆ OsAddToPageacheLru()

VOID OsAddToPageacheLru ( LosFilePage page,
struct page_mapping mapping,
VM_OFFSET_T  pgoff 
)

将页面加到活动文件页LRU链表上

在文件 los_vm_filemap.c100 行定义.

101{
102 OsPageCacheAdd(page, mapping, pgoff);
104}
STATIC VOID OsPageCacheAdd(LosFilePage *page, struct page_mapping *mapping, VM_OFFSET_T pgoff)
VOID OsLruCacheAdd(LosFilePage *fpage, enum OsLruList lruType)
Definition: los_vm_scan.c:69
@ VM_LRU_ACTIVE_FILE
活动文件页(磁盘)
Definition: los_vm_phys.h:78
函数调用图:
这是这个函数的调用关系图:

◆ OsDeletePageCacheLru()

VOID OsDeletePageCacheLru ( LosFilePage page)

删除页高速缓存和LRU,对应 OsAddToPageacheLru

在文件 los_vm_filemap.c157 行定义.

158{
159 /* delete form lru list */
160 OsLruCacheDel(page); //将页面从lru列表中删除
161 /* delete from cache lits and free pmm if need */
162 OsPageCacheDel(page); //从page缓存中删除
163}
VOID OsPageCacheDel(LosFilePage *fpage)
从页高速缓存上删除页
VOID OsLruCacheDel(LosFilePage *fpage)
Definition: los_vm_scan.c:85
函数调用图:
这是这个函数的调用关系图:

◆ OsDelMapInfo()

VOID OsDelMapInfo ( LosVmMapRegion region,
LosVmPgFault vmf,
BOOL  cleanDirty 
)

删除映射信息

在文件 los_vm_filemap.c348 行定义.

349{
350 UINT32 intSave;
351 LosMapInfo *info = NULL;
352 LosFilePage *fpage = NULL;
353 struct page_mapping *mapping = NULL;
354
355 if (!LOS_IsRegionFileValid(region) || (region->unTypeData.rf.vnode == NULL) || (vmf == NULL)) {
356 return;
357 }
358
359 mapping = &region->unTypeData.rf.vnode->mapping;
360 LOS_SpinLockSave(&mapping->list_lock, &intSave);
361 fpage = OsFindGetEntry(mapping, vmf->pgoff);
362 if (fpage == NULL) {
363 LOS_SpinUnlockRestore(&mapping->list_lock, intSave);
364 return;
365 }
366
367 if (cleanDirty) {
368 OsCleanPageDirty(fpage->vmPage);//恢复干净页
369 }
370 info = OsGetMapInfo(fpage, &region->space->archMmu, (vaddr_t)vmf->vaddr);//通过虚拟地址获取映射信息
371 if (info != NULL) {
372 fpage->n_maps--;
373 LOS_ListDelete(&info->node);
375 LOS_SpinUnlockRestore(&mapping->list_lock, intSave);
377 return;
378 }
379 LOS_SpinUnlockRestore(&mapping->list_lock, intSave);
380}
STATIC INLINE VOID LOS_AtomicDec(Atomic *v)
Atomic auto-decrement. | 对32bit原子数据做减1
Definition: los_atomic.h:323
LITE_OS_SEC_ALW_INLINE STATIC INLINE VOID LOS_ListDelete(LOS_DL_LIST *node)
Definition: los_list.h:292
UINT32 LOS_MemFree(VOID *pool, VOID *ptr)
释放从指定动态内存中申请的内存
Definition: los_memory.c:1369
VOID LOS_SpinUnlockRestore(SPIN_LOCK_S *lock, UINT32 intSave)
Definition: los_spinlock.c:108
VOID LOS_SpinLockSave(SPIN_LOCK_S *lock, UINT32 *intSave)
Definition: los_spinlock.c:98
unsigned long vaddr_t
Definition: los_typedef.h:206
LosMapInfo * OsGetMapInfo(LosFilePage *page, LosArchMmu *archMmu, VADDR_T vaddr)
通过虚拟地址获取文件页映射信息,archMmu每个进程都有属于自己的mmu
LosFilePage * OsFindGetEntry(struct page_mapping *mapping, VM_OFFSET_T pgoff)
STATIC INLINE VOID OsCleanPageDirty(LosVmPage *page)
给页面撕掉数据被修改的标签
BOOL LOS_IsRegionFileValid(LosVmMapRegion *region)
映射类型为文件的线性区是否有效
Definition: los_vm_map.c:512
文件页结构体
struct VmPage * vmPage
物理页框
unsigned long pgoff
Definition: los_vm_map.h:97
VADDR_T vaddr
Definition: los_vm_map.h:98
struct VmMapRegion::@4::VmRegionFile rf
union VmMapRegion::@4 unTypeData
LosVmSpace * space
所属虚拟空间,虚拟空间由多个线性区组成
Definition: los_vm_map.h:121
Atomic refCounts
Definition: los_vm_page.h:57
LosArchMmu archMmu
Definition: los_vm_map.h:157
SPIN_LOCK_S list_lock
函数调用图:
这是这个函数的调用关系图:

◆ OsDoFlushDirtyPage()

VOID OsDoFlushDirtyPage ( LosFilePage fpage)

冲洗脏页数据,将脏页数据回写磁盘

在文件 los_vm_filemap.c326 行定义.

327{
328 if (fpage == NULL) {
329 return;
330 }
331 (VOID)OsFlushDirtyPage(fpage);
333}
STATIC INT32 OsFlushDirtyPage(LosFilePage *fpage)
冲洗脏页,回写磁盘
函数调用图:
这是这个函数的调用关系图:

◆ OsDumpDirtyPage()

LosFilePage * OsDumpDirtyPage ( LosFilePage oldFPage)

备份脏页,老脏页撕掉脏页标签

在文件 los_vm_filemap.c310 行定义.

311{
312 LosFilePage *newFPage = NULL;
313
314 newFPage = (LosFilePage *)LOS_MemAlloc(m_aucSysMem0, sizeof(LosFilePage));
315 if (newFPage == NULL) {
316 VM_ERR("Failed to allocate for temp page!");
317 return NULL;
318 }
319
320 OsCleanPageDirty(oldFPage->vmPage);
321 (VOID)memcpy_s(newFPage, sizeof(LosFilePage), oldFPage, sizeof(LosFilePage));//直接内存拷贝
322
323 return newFPage;
324}
struct FilePage LosFilePage
文件页结构体
函数调用图:
这是这个函数的调用关系图:

◆ OsFileCacheFlush()

VOID OsFileCacheFlush ( struct page_mapping mapping)

文件缓存冲洗,把所有fpage冲洗一边,把脏页洗到dirtyList中,配合OsFileCacheRemove理解

在文件 los_vm_filemap.c453 行定义.

454{
455 UINT32 intSave;
456 UINT32 lruLock;
457 LOS_DL_LIST_HEAD(dirtyList);//LOS_DL_LIST list = { &(list), &(list) };
458 LosFilePage *ftemp = NULL;
459 LosFilePage *fpage = NULL;
460
461 if (mapping == NULL) {
462 return;
463 }
464 LOS_SpinLockSave(&mapping->list_lock, &intSave);
465 LOS_DL_LIST_FOR_EACH_ENTRY(fpage, &mapping->page_list, LosFilePage, node) {//循环从page_list中取node给fpage
466 LOS_SpinLockSave(&fpage->physSeg->lruLock, &lruLock);
467 if (OsIsPageDirty(fpage->vmPage)) {//是否为脏页
468 ftemp = OsDumpDirtyPage(fpage);//这里挺妙的,copy出一份新页,老页变成了非脏页继续用
469 if (ftemp != NULL) {
470 LOS_ListTailInsert(&dirtyList, &ftemp->node);//将新页插入脏页List,等待回写磁盘
471 }
472 }
473 LOS_SpinUnlockRestore(&fpage->physSeg->lruLock, lruLock);
474 }
475 LOS_SpinUnlockRestore(&mapping->list_lock, intSave);
476
477 LOS_DL_LIST_FOR_EACH_ENTRY_SAFE(fpage, ftemp, &dirtyList, LosFilePage, node) {//仔细看这个宏,关键在 &(item)->member != (list);
478 OsDoFlushDirtyPage(fpage);//立马洗掉,所以dirtyList可以不是全局变量
479 }
480}
LITE_OS_SEC_ALW_INLINE STATIC INLINE VOID LOS_ListTailInsert(LOS_DL_LIST *list, LOS_DL_LIST *node)
Insert a node to the tail of a doubly linked list.
Definition: los_list.h:244
LosFilePage * OsDumpDirtyPage(LosFilePage *oldFPage)
备份脏页,老脏页撕掉脏页标签
VOID OsDoFlushDirtyPage(LosFilePage *fpage)
冲洗脏页数据,将脏页数据回写磁盘
STATIC INLINE BOOL OsIsPageDirty(LosVmPage *page)
页面是否为脏页,所谓脏页就是页内数据是否被更新过,只有脏页才会有写时拷贝
LOS_DL_LIST_HEAD(g_vmSpaceList)
初始化全局虚拟空间节点,所有虚拟空间都挂到此节点上.
struct VmPhysSeg * physSeg
LOS_DL_LIST node
节点,节点挂到page_mapping.page_list上,链表以 pgoff 从小到大方式排序.
SPIN_LOCK_S lruLock
用于置换的自旋锁,用于操作lruList
Definition: los_vm_phys.h:91
LOS_DL_LIST page_list
函数调用图:
这是这个函数的调用关系图:

◆ OsFileCacheRemove()

VOID OsFileCacheRemove ( struct page_mapping mapping)

在文件 los_vm_filemap.c486 行定义.

487{
488 UINT32 intSave;
489 UINT32 lruSave;
490 SPIN_LOCK_S *lruLock = NULL;
491 LOS_DL_LIST_HEAD(dirtyList);//定义一个叫dirtyList的双循环链表并初始化,用于挂脏页
492 LosFilePage *ftemp = NULL;
493 LosFilePage *fpage = NULL;
494 LosFilePage *fnext = NULL;
495
496 LOS_SpinLockSave(&mapping->list_lock, &intSave);//多进程操作,必须上锁.
497 LOS_DL_LIST_FOR_EACH_ENTRY_SAFE(fpage, fnext, &mapping->page_list, LosFilePage, node) {//遍历文件在内存中产生的所有文件页(例如1,4,8页)不一定连续,取决于用户的读取顺序
498 lruLock = &fpage->physSeg->lruLock;
499 LOS_SpinLockSave(lruLock, &lruSave);//@note_why 自旋锁有必要从这里开始上锁吗?
500 if (OsIsPageDirty(fpage->vmPage)) {//数据是脏页吗,脏页就是被修改过数据的页
501 ftemp = OsDumpDirtyPage(fpage);//做这个拷贝动作是为了fpage的统一下线,因为数据回写磁盘的速度是很慢的,如果直接在这里处理脏数据
502 if (ftemp != NULL) {//会导致函数持有mapping->list_lock自旋锁的时间太长了,影响其他CPU的处理效率
503 LOS_ListTailInsert(&dirtyList, &ftemp->node);//将临时脏页挂到记录脏页链表上
504 }
505 }
506
507 OsDeletePageCacheLru(fpage);//删除高速缓存和从置换链表中下线
508 LOS_SpinUnlockRestore(lruLock, lruSave);
509 }
510 LOS_SpinUnlockRestore(&mapping->list_lock, intSave);//恢复自旋锁,不能让别的CPU等太久
511
512 LOS_DL_LIST_FOR_EACH_ENTRY_SAFE(fpage, fnext, &dirtyList, LosFilePage, node) {//到这里,再来慢慢的统一处理脏页数据
513 OsDoFlushDirtyPage(fpage);//遍历脏页链表,一页一页处理
514 }
515}
VOID OsDeletePageCacheLru(LosFilePage *page)
删除页高速缓存和LRU,对应 OsAddToPageacheLru
函数调用图:
这是这个函数的调用关系图:

◆ OsFindGetEntry()

LosFilePage * OsFindGetEntry ( struct page_mapping mapping,
VM_OFFSET_T  pgoff 
)

在文件 los_vm_filemap.c571 行定义.

572{
573 LosFilePage *fpage = NULL;
574
575 LOS_DL_LIST_FOR_EACH_ENTRY(fpage, &mapping->page_list, LosFilePage, node) {//遍历文件页
576 if (fpage->pgoff == pgoff) {//找到指定的页,
577 return fpage;
578 }
579
580 if (fpage->pgoff > pgoff) {//大于之前还没有找到,说明不在链表中,往后的也不用找了,
581 break;//因为 mapping->page_list节点上的数据都是按 fpage->pgoff 从小到大的顺序排列的.
582 }
583 }
584
585 return NULL;
586}
这是这个函数的调用关系图:

◆ OsFlushDirtyPage()

STATIC INT32 OsFlushDirtyPage ( LosFilePage fpage)

冲洗脏页,回写磁盘

在文件 los_vm_filemap.c278 行定义.

279{
280 UINT32 ret;
281 size_t len;
282 char *buff = NULL;
283 struct Vnode *vnode = fpage->mapping->host;/* owner of this mapping */ //此映射属于哪个文件,注意<file,page_mapping>是1:1的关系.
284 if (vnode == NULL) {
285 VM_ERR("page cache vnode error");
286 return LOS_NOK;
287 }
288
289 len = fpage->dirtyEnd - fpage->dirtyOff;//计算出脏数据长度
290 len = (len == 0) ? GetDirtySize(fpage, vnode) : len;
291 if (len == 0) {//没有脏数据
292 OsCleanPageDirty(fpage->vmPage);//页面取消脏标签
293 return LOS_OK;
294 }
295
296 buff = (char *)OsVmPageToVaddr(fpage->vmPage);
297
298 /* actually, we did not update the fpage->dirtyOff */
299 ret = vnode->vop->WritePage(vnode, (VOID *)buff, fpage->pgoff, len);
300 if (ret <= 0) {
301 VM_ERR("WritePage error ret %d", ret);
302 } else {
303 OsCleanPageDirty(fpage->vmPage);
304 }
305 ret = (ret <= 0) ? LOS_NOK : LOS_OK;
306
307 return ret;
308}
STATIC UINT32 GetDirtySize(LosFilePage *fpage, struct Vnode *vnode)
VOID * OsVmPageToVaddr(LosVmPage *page)
通过page获取内核空间的虚拟地址 参考OsArchMmuInit #define SYS_MEM_BASE DDR_MEM_ADDR /* physical memory base 物理地址的起始...
Definition: los_vm_phys.c:288
UINT16 dirtyOff
脏页的页内偏移地址
struct page_mapping * mapping
此结构由文件系统提供,用于描述装入点 见于 ..\third_party\NuttX\fs\fs.h
UINT16 dirtyEnd
脏页的结束位置
vnode并不包含文件名,因为 vnode和文件名是 1:N 的关系
Definition: vnode.h:164
struct VnodeOps * vop
Definition: vnode.h:174
ssize_t(* WritePage)(struct Vnode *vnode, char *buffer, off_t pos, size_t buflen)
Definition: vnode.h:195
struct file * host
函数调用图:
这是这个函数的调用关系图:

◆ OsGetMapInfo()

LosMapInfo * OsGetMapInfo ( LosFilePage page,
LosArchMmu archMmu,
VADDR_T  vaddr 
)

通过虚拟地址获取文件页映射信息,archMmu每个进程都有属于自己的mmu

在文件 los_vm_filemap.c144 行定义.

145{
146 LosMapInfo *info = NULL;
147 LOS_DL_LIST *immap = &page->i_mmap;//一个文件页被多个进程映射
148
149 LOS_DL_LIST_FOR_EACH_ENTRY(info, immap, LosMapInfo, node) {//遍历每个节点
150 if ((info->archMmu == archMmu) && (info->vaddr == vaddr) && (info->page == page)) {//全等时返回
151 return info;
152 }
153 }
154 return NULL;
155}
这是这个函数的调用关系图:

◆ OsMarkPageDirty()

VOID OsMarkPageDirty ( LosFilePage fpage,
LosVmMapRegion region,
INT32  off,
INT32  len 
)

标记page为脏页 进程修改了高速缓存里的数据时,该页就被内核标记为脏页

在文件 los_vm_filemap.c233 行定义.

234{
235 if (region != NULL) {
236 OsSetPageDirty(fpage->vmPage);//设置为脏页
237 fpage->dirtyOff = off;//脏页偏移位置
238 fpage->dirtyEnd = len;//脏页结束位置
239 } else {
240 OsSetPageDirty(fpage->vmPage);//设置为脏页
241 if ((off + len) > fpage->dirtyEnd) {
242 fpage->dirtyEnd = off + len;
243 }
244
245 if (off < fpage->dirtyOff) {
246 fpage->dirtyOff = off;
247 }
248 }
249}
STATIC INLINE VOID OsSetPageDirty(LosVmPage *page)
给页面贴上数据被修改的标签
函数调用图:
这是这个函数的调用关系图:

◆ OsNamedMMap()

STATUS_T OsNamedMMap ( struct file filep,
LosVmMapRegion region 
)

有名映射,可理解为文件映射,跟匿名映射相对应 参数filep是广义的文件,在鸿蒙内核,目录/普通文件/字符设备/块设备/网络套接字/管道/链接 都是文件

在文件 los_vm_filemap.c536 行定义.

537{
538 struct Vnode *vnode = NULL;
539 if (filep == NULL) {
540 return LOS_ERRNO_VM_MAP_FAILED;
541 }
542 file_hold(filep);
543 vnode = filep->f_vnode;
544 VnodeHold();
545 vnode->useCount++;
546 VnodeDrop();
547 if (filep->ops != NULL && filep->ops->mmap != NULL) {
548 if (vnode->type == VNODE_TYPE_CHR || vnode->type == VNODE_TYPE_BLK) {//块设备或者字符设备 /dev/..
549 LOS_SetRegionTypeDev(region);//设置为设备类型
550 } else {
551 LOS_SetRegionTypeFile(region);//设置为文件类型
552 }
553 int ret = filep->ops->mmap(filep, region);
554 if (ret != LOS_OK) {
555 file_release(filep);
556 return LOS_ERRNO_VM_MAP_FAILED;
557 }
558 } else {
559 VM_ERR("mmap file type unknown");
560 file_release(filep);
561 return LOS_ERRNO_VM_MAP_FAILED;
562 }
563 file_release(filep);
564 return LOS_OK;
565}
STATIC INLINE VOID LOS_SetRegionTypeFile(LosVmMapRegion *region)
设置线性区为文件映射
Definition: los_vm_map.h:250
STATIC INLINE VOID LOS_SetRegionTypeDev(LosVmMapRegion *region)
设为设备映射线性区
Definition: los_vm_map.h:260
enum VnodeType type
Definition: vnode.h:165
int useCount
Definition: vnode.h:166
int VnodeDrop(void)
归还锁
Definition: vnode.c:292
@ VNODE_TYPE_CHR
Definition: vnode.h:139
@ VNODE_TYPE_BLK
Definition: vnode.h:138
int VnodeHold(void)
拿锁,封装互斥量
Definition: vnode.c:283
函数调用图:
这是这个函数的调用关系图:

◆ OsPageCacheAdd()

STATIC VOID OsPageCacheAdd ( LosFilePage page,
struct page_mapping mapping,
VM_OFFSET_T  pgoff 
)
 增加文件页到页高速缓存(page cache)
 LosFilePage将一个文件切成了一页一页,因为读文件过程随机seek,所以文件页也不会是连续的,
 pgoff记录文件的位置,并确保在cache的文件数据是按顺序排列的.
参数
page
mapping
pgoff
返回
STATIC

在文件 los_vm_filemap.c83 行定义.

84{
85 LosFilePage *fpage = NULL;
86
87 LOS_DL_LIST_FOR_EACH_ENTRY(fpage, &mapping->page_list, LosFilePage, node) {//遍历page_list链表
88 if (fpage->pgoff > pgoff) {//插入的条件,这样插入保证了按pgoff 从小到大排序
89 LOS_ListTailInsert(&fpage->node, &page->node);//等于挂到fpage节点的前面了
90 goto done_add;
91 }
92 }
93
94 LOS_ListTailInsert(&mapping->page_list, &page->node);//将页挂到文件映射的链表上,相当于挂到了最后
95
96done_add:
97 mapping->nrpages++; //文件在缓存中多了一个 文件页
98}
unsigned long nrpages
函数调用图:
这是这个函数的调用关系图:

◆ OsPageCacheAlloc()

LosFilePage * OsPageCacheAlloc ( struct page_mapping mapping,
VM_OFFSET_T  pgoff 
)

以页高速缓存方式分配一个文件页 LosFilePage Direct Memory Access(存储器直接访问)指一种高速的数据传输操作,允许在外部设备和存储器之间直接读写数据。 整个数据传输操作在一个称为"DMA控制器"的控制下进行的。CPU只需在数据传输开始和结束时做一点处理(开始和结束时候要做中断处理)

在文件 los_vm_filemap.c594 行定义.

595{
596 VOID *kvaddr = NULL;
597 LosVmPhysSeg *physSeg = NULL;
598 LosVmPage *vmPage = NULL;
599 LosFilePage *fpage = NULL;
600
601 vmPage = LOS_PhysPageAlloc(); //先分配一个物理页
602 if (vmPage == NULL) {
603 VM_ERR("alloc vm page failed");
604 return NULL;
605 }
606 physSeg = OsVmPhysSegGet(vmPage);//通过页获取所在seg
607 kvaddr = OsVmPageToVaddr(vmPage);//获取内核空间的虚拟地址,具体点进去看函数说明,这里一定要理解透彻!
608 if ((physSeg == NULL) || (kvaddr == NULL)) {
609 LOS_PhysPageFree(vmPage); //异常情况要释放vmPage
610 VM_ERR("alloc vm page failed!");
611 return NULL;
612 }
613
614 fpage = (LosFilePage *)LOS_MemAlloc(m_aucSysMem0, sizeof(LosFilePage));//从内存池中分配一个filePage
615 if (fpage == NULL) {
616 LOS_PhysPageFree(vmPage); //异常情况要释放vmPage
617 VM_ERR("Failed to allocate for page!");
618 return NULL;
619 }
620
621 (VOID)memset_s((VOID *)fpage, sizeof(LosFilePage), 0, sizeof(LosFilePage));//调标准库函数 置0
622
623 LOS_ListInit(&fpage->i_mmap); //初始化映射,链表上挂 MapInfo
624 LOS_ListInit(&fpage->node); //节点初始化
625 LOS_ListInit(&fpage->lru); //LRU初始化
626 fpage->n_maps = 0; //映射次数
627 fpage->dirtyOff = PAGE_SIZE; //默认页尾部,相当于没有脏数据
628 fpage->dirtyEnd = 0; //脏页结束位置
629 fpage->physSeg = physSeg; //页框所属段.其中包含了 LRU LIST ==
630 fpage->vmPage = vmPage; //物理页框
631 fpage->mapping = mapping; //记录所有文件页映射
632 fpage->pgoff = pgoff; //将文件切成一页页,页标
633 (VOID)memset_s(kvaddr, PAGE_SIZE, 0, PAGE_SIZE);//页内数据清0
634
635 return fpage;
636}
LITE_OS_SEC_ALW_INLINE STATIC INLINE VOID LOS_ListInit(LOS_DL_LIST *list)
Definition: los_list.h:104
VOID LOS_PhysPageFree(LosVmPage *page)
释放一个物理页框
Definition: los_vm_phys.c:546
struct VmPhysSeg * OsVmPhysSegGet(LosVmPage *page)
获取物理页框所在段
Definition: los_vm_phys.c:643
LosVmPage * LOS_PhysPageAlloc(VOID)
申请一个物理页
Definition: los_vm_phys.c:566
LOS_DL_LIST lru
lru节点, 结合 LosVmPhysSeg: LOS_DL_LIST lruList[VM_NR_LRU_LISTS] 理解
物理页框描述符 虚拟内存体现的是程序对内存资源的需求,而物理内存是对该请求的供应。 伙伴算法的思想是:把内存中连续的空闲页框空间看成是空闲页框块,并按照它们的大小(连续页框的数目)分组
Definition: los_vm_page.h:53
物理段描述符
Definition: los_vm_phys.h:85
函数调用图:
这是这个函数的调用关系图:

◆ OsPageCacheDel()

VOID OsPageCacheDel ( LosFilePage fpage)

从页高速缓存上删除页

在文件 los_vm_filemap.c106 行定义.

107{
108 /* delete from file cache list */
109 LOS_ListDelete(&fpage->node);//将自己从链表上摘除
110 fpage->mapping->nrpages--;//文件映射的页总数减少
111
112 /* unmap and remove map info */
113 if (OsIsPageMapped(fpage)) {//是否映射过
114 OsUnmapAllLocked(fpage);
115 }
116
117 LOS_PhysPageFree(fpage->vmPage);//释放物理内存
118
119 LOS_MemFree(m_aucSysMem0, fpage);//释放文件页结构体内存
120}
VOID OsUnmapAllLocked(LosFilePage *page)
解除文件页在所有进程的映射
Definition: los_vm_scan.c:57
STATIC INLINE BOOL OsIsPageMapped(LosFilePage *page)
文件页是否映射过了
函数调用图:
这是这个函数的调用关系图:

◆ OsPageCacheUnmap()

STATIC VOID OsPageCacheUnmap ( LosFilePage fpage,
LosArchMmu archMmu,
VADDR_T  vaddr 
)

在文件 los_vm_filemap.c166 行定义.

167{
168 UINT32 intSave;
169 LosMapInfo *info = NULL;
170
171 LOS_SpinLockSave(&fpage->physSeg->lruLock, &intSave);
172 info = OsGetMapInfo(fpage, archMmu, vaddr);//获取文件页在进程的映射信息
173 if (info == NULL) {
174 VM_ERR("OsPageCacheUnmap get map info failed!");
175 } else {
176 OsUnmapPageLocked(fpage, info);//解除进程和文件页映射关系
177 }
178 if (!(OsIsPageMapped(fpage) && ((fpage->flags & VM_MAP_REGION_FLAG_PERM_EXECUTE) ||
179 OsIsPageDirty(fpage->vmPage)))) {
180 OsPageRefDecNoLock(fpage);
181 }
182
183 LOS_SpinUnlockRestore(&fpage->physSeg->lruLock, intSave);
184}
VOID OsPageRefDecNoLock(LosFilePage *page)
Definition: los_vm_scan.c:189
VOID OsUnmapPageLocked(LosFilePage *page, LosMapInfo *info)
Definition: los_vm_scan.c:44
UINT32 flags
标签
函数调用图:
这是这个函数的调用关系图:

◆ OsReleaseFpage()

STATIC VOID OsReleaseFpage ( struct page_mapping mapping,
LosFilePage fpage 
)

在文件 los_vm_filemap.c335 行定义.

336{
337 UINT32 intSave;
338 UINT32 lruSave;
339 SPIN_LOCK_S *lruLock = &fpage->physSeg->lruLock;
340 LOS_SpinLockSave(&mapping->list_lock, &intSave);
341 LOS_SpinLockSave(lruLock, &lruSave);
344 LOS_SpinUnlockRestore(lruLock, lruSave);
345 LOS_SpinUnlockRestore(&mapping->list_lock, intSave);
346}
STATIC INLINE VOID OsCleanPageLocked(LosVmPage *page)
给页面撕掉被锁的标签
函数调用图:
这是这个函数的调用关系图:

◆ OsVfsFileMmap()

INT32 OsVfsFileMmap ( struct file filep,
LosVmMapRegion region 
)

在文件 los_vm_filemap.c524 行定义.

525{
526 region->unTypeData.rf.vmFOps = &g_commVmOps;//文件操作
527 region->unTypeData.rf.vnode = filep->f_vnode;
528 region->unTypeData.rf.f_oflags = filep->f_oflags;
529
530 return ENOERR;
531}
LosVmFileOps g_commVmOps
虚拟内存文件操作实现类
int f_oflags

◆ OsVmmFileFault()

INT32 OsVmmFileFault ( LosVmMapRegion region,
LosVmPgFault vmf 
)

文件缺页时的处理,先读入磁盘数据,再重新读页数据 被 OsDoReadFault(...),OsDoCowFault(...),OsDoSharedFault(...) 等调用

在文件 los_vm_filemap.c385 行定义.

386{
387 INT32 ret;
388 VOID *kvaddr = NULL;
389
390 UINT32 intSave;
391 bool newCache = false;
392 struct Vnode *vnode = NULL;
393 struct page_mapping *mapping = NULL;
394 LosFilePage *fpage = NULL;
395
396 if (!LOS_IsRegionFileValid(region) || (region->unTypeData.rf.vnode == NULL) || (vmf == NULL)) {//文件是否映射到了内存
397 VM_ERR("Input param is NULL");
398 return LOS_NOK;
399 }
400 vnode = region->unTypeData.rf.vnode;
401 mapping = &vnode->mapping;
402
403 /* get or create a new cache node */
404 LOS_SpinLockSave(&mapping->list_lock, &intSave);
405 fpage = OsFindGetEntry(mapping, vmf->pgoff);//获取文件页
406 TRACE_TRY_CACHE();
407 if (fpage != NULL) {//找到了,说明该页已经在页高速缓存中
408 TRACE_HIT_CACHE();
409 OsPageRefIncLocked(fpage);
410 } else {//真的缺页了,页高速缓存中没找到
411 fpage = OsPageCacheAlloc(mapping, vmf->pgoff);//分配一个文件页,将数据初始化好,包括vmpage(物理页框)
412 if (fpage == NULL) {
413 LOS_SpinUnlockRestore(&mapping->list_lock, intSave);
414 VM_ERR("Failed to alloc a page frame");
415 return LOS_NOK;
416 }
417 newCache = true;//分配了新文件页
418 }
419 OsSetPageLocked(fpage->vmPage);//对vmpage上锁
420 LOS_SpinUnlockRestore(&mapping->list_lock, intSave);
421 kvaddr = OsVmPageToVaddr(fpage->vmPage);//获取该页框在内核空间的虚拟地址,因为 page cache本身就是在内核空间,
422
423 /* read file to new page cache */
424 if (newCache) {//新cache
425 ret = vnode->vop->ReadPage(vnode, kvaddr, fpage->pgoff << PAGE_SHIFT);
426 if (ret == 0) {
427 VM_ERR("Failed to read from file!");
428 OsReleaseFpage(mapping, fpage);
429 return LOS_NOK;
430 }
431 LOS_SpinLockSave(&mapping->list_lock, &intSave);
432 OsAddToPageacheLru(fpage, mapping, vmf->pgoff);//将fpage挂入pageCache 和 LruCache
433 LOS_SpinUnlockRestore(&mapping->list_lock, intSave);
434 }
435
436 LOS_SpinLockSave(&mapping->list_lock, &intSave);
437 /* cow fault case no need to save mapinfo */
438 if (!((vmf->flags & VM_MAP_PF_FLAG_WRITE) && !(region->regionFlags & VM_MAP_REGION_FLAG_SHARED))) {
439 OsAddMapInfo(fpage, &region->space->archMmu, (vaddr_t)vmf->vaddr);//添加<虚拟地址,文件页>的映射关系,如此进程以后就能通过虚拟地址操作文件页了.
440 fpage->flags = region->regionFlags;
441 }
442
443 /* share page fault, mark the page dirty */
444 if ((vmf->flags & VM_MAP_PF_FLAG_WRITE) && (region->regionFlags & VM_MAP_REGION_FLAG_SHARED)) {//有过写操作或者为共享线性区
445 OsMarkPageDirty(fpage, region, 0, 0);//标记为脏页,要回写磁盘,内核会在适当的时候回写磁盘
446 }
447
448 vmf->pageKVaddr = kvaddr;//缺陷页记录文件页的虚拟地址
449 LOS_SpinUnlockRestore(&mapping->list_lock, intSave);
450 return LOS_OK;
451}
signed int INT32
Definition: los_typedef.h:60
VOID OsAddToPageacheLru(LosFilePage *page, struct page_mapping *mapping, VM_OFFSET_T pgoff)
将页面加到活动文件页LRU链表上
VOID OsAddMapInfo(LosFilePage *page, LosArchMmu *archMmu, VADDR_T vaddr)
STATIC VOID OsReleaseFpage(struct page_mapping *mapping, LosFilePage *fpage)
LosFilePage * OsPageCacheAlloc(struct page_mapping *mapping, VM_OFFSET_T pgoff)
VOID OsMarkPageDirty(LosFilePage *fpage, LosVmMapRegion *region, INT32 off, INT32 len)
标记page为脏页 进程修改了高速缓存里的数据时,该页就被内核标记为脏页
VOID OsPageRefIncLocked(LosFilePage *page)
Definition: los_vm_scan.c:145
STATIC INLINE VOID OsSetPageLocked(LosVmPage *page)
给页面贴上被锁的标签
VADDR_T * pageKVaddr
Definition: los_vm_map.h:99
UINT32 flags
Definition: los_vm_map.h:96
UINT32 regionFlags
Definition: los_vm_map.h:125
struct page_mapping mapping
Definition: vnode.h:183
ssize_t(* ReadPage)(struct Vnode *vnode, char *buffer, off_t pos)
Definition: vnode.h:194
函数调用图:

◆ OsVmmFileRemove()

VOID OsVmmFileRemove ( LosVmMapRegion region,
LosArchMmu archMmu,
VM_OFFSET_T  pgoff 
)

删除文件

在文件 los_vm_filemap.c186 行定义.

187{
188 UINT32 intSave;
189 vaddr_t vaddr;
190 paddr_t paddr = 0;
191 struct Vnode *vnode = NULL;
192 struct page_mapping *mapping = NULL;
193 LosFilePage *fpage = NULL;
194 LosFilePage *tmpPage = NULL;
195 LosVmPage *mapPage = NULL;
196
197 if (!LOS_IsRegionFileValid(region) || (region->unTypeData.rf.vnode == NULL)) {
198 return;//判断是否为文件映射,是否已map
199 }
200 vnode = region->unTypeData.rf.vnode;
201 mapping = &vnode->mapping;
202 vaddr = region->range.base + ((UINT32)(pgoff - region->pgOff) << PAGE_SHIFT);//得到虚拟地址
203
204 status_t status = LOS_ArchMmuQuery(archMmu, vaddr, &paddr, NULL);//获取物理地址
205 if (status != LOS_OK) {
206 return;
207 }
208
209 mapPage = LOS_VmPageGet(paddr);//获取物理页框
210
211 /* is page is in cache list */
212 LOS_SpinLockSave(&mapping->list_lock, &intSave);
213 fpage = OsFindGetEntry(mapping, pgoff);//获取fpage
214 /* no cache or have cache but not map(cow), free it direct */
215 if ((fpage == NULL) || (fpage->vmPage != mapPage)) {//没有缓存或有缓存但没有映射(cow),直接释放它
216 LOS_PhysPageFree(mapPage);//释放物理页框
217 LOS_ArchMmuUnmap(archMmu, vaddr, 1);//取消虚拟地址的映射
218 /* this is a page cache map! */
219 } else {
220 OsPageCacheUnmap(fpage, archMmu, vaddr);////取消缓存中的映射
221 if (OsIsPageDirty(fpage->vmPage)) {//脏页处理
222 tmpPage = OsDumpDirtyPage(fpage);//dump 脏页
223 }
224 }
225 LOS_SpinUnlockRestore(&mapping->list_lock, intSave);
226
227 if (tmpPage) {
228 OsDoFlushDirtyPage(tmpPage);
229 }
230 return;
231}
STATUS_T LOS_ArchMmuUnmap(LosArchMmu *archMmu, VADDR_T vaddr, size_t count)
LOS_ArchMmuUnmap 解除进程空间虚拟地址区间与物理地址区间的映射关系
Definition: los_arch_mmu.c:619
STATUS_T LOS_ArchMmuQuery(const LosArchMmu *archMmu, VADDR_T vaddr, PADDR_T *paddr, UINT32 *flags)
LOS_ArchMmuQuery 获取进程空间虚拟地址对应的物理地址以及映射属性。 本函数是内核高频函数,通过MMU查询虚拟地址是否映射过,带走映射的物理地址和权限
Definition: los_arch_mmu.c:569
int status_t
Definition: los_typedef.h:205
unsigned long paddr_t
Definition: los_typedef.h:209
STATIC VOID OsPageCacheUnmap(LosFilePage *fpage, LosArchMmu *archMmu, VADDR_T vaddr)
LosVmPage * LOS_VmPageGet(PADDR_T paddr)
通过物理地址获取页框
Definition: los_vm_page.c:120
VADDR_T base
Definition: los_vm_map.h:84
LosVmMapRange range
Definition: los_vm_map.h:123
VM_OFFSET_T pgOff
Definition: los_vm_map.h:124
函数调用图:

◆ ResetPageCacheHitInfo()

VOID ResetPageCacheHitInfo ( int try,
int hit 
)

在文件 los_vm_filemap.c58 行定义.

59{
64}
static int g_totalPageCacheTry
static int g_totalPageCacheHit
这是这个函数的调用关系图:

变量说明

◆ g_commVmOps

LosVmFileOps g_commVmOps
初始值:
= {
.open = NULL,
.close = NULL,
.fault = OsVmmFileFault,
.remove = OsVmmFileRemove,
}
VOID OsVmmFileRemove(LosVmMapRegion *region, LosArchMmu *archMmu, VM_OFFSET_T pgoff)
删除文件
INT32 OsVmmFileFault(LosVmMapRegion *region, LosVmPgFault *vmf)

虚拟内存文件操作实现类

在文件 los_vm_filemap.c517 行定义.

◆ g_totalPageCacheHit

int g_totalPageCacheHit = 0
static

在文件 los_vm_filemap.c54 行定义.

◆ g_totalPageCacheTry

int g_totalPageCacheTry = 0
static

在文件 los_vm_filemap.c53 行定义.