更新日期: 2022/06/01 来源: https://gitee.com/weharmony/kernel_liteos_a_note
los_vm_filemap.c
浏览该文件的文档.
1/*
2 * Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
3 * Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this list of
9 * conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
12 * of conditions and the following disclaimer in the documentation and/or other materials
13 * provided with the distribution.
14 *
15 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
16 * to endorse or promote products derived from this software without specific prior written
17 * permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
26 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
28 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
29 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32/**
33 * @defgroup los_vm_filemap vm filemap definition
34 * @ingroup kernel
35 */
36
37#include "los_vm_filemap.h"
38#include "los_vm_page.h"
39#include "los_vm_phys.h"
40#include "los_vm_common.h"
41#include "los_vm_fault.h"
42#include "los_process_pri.h"
43#include "los_vm_lock.h"
44#ifdef LOSCFG_FS_VFS
45#include "vnode.h"
46#endif
47
48#ifndef UNUSED
49#define UNUSED(x) (VOID)(x)
50#endif
51
52#ifdef LOSCFG_DEBUG_VERSION
53static int g_totalPageCacheTry = 0;
54static int g_totalPageCacheHit = 0;
55#define TRACE_TRY_CACHE() do { g_totalPageCacheTry++; } while (0)
56#define TRACE_HIT_CACHE() do { g_totalPageCacheHit++; } while (0)
57
58VOID ResetPageCacheHitInfo(int *try, int *hit)
59{
64}
65#else
66#define TRACE_TRY_CACHE()
67#define TRACE_HIT_CACHE()
68#endif
69#ifdef LOSCFG_KERNEL_VM
70
71/**
72 * @brief
73 @verbatim
74 增加文件页到页高速缓存(page cache)
75 LosFilePage将一个文件切成了一页一页,因为读文件过程随机seek,所以文件页也不会是连续的,
76 pgoff记录文件的位置,并确保在cache的文件数据是按顺序排列的.
77 @endverbatim
78 * @param page
79 * @param mapping
80 * @param pgoff
81 * @return STATIC
82 */
83STATIC VOID OsPageCacheAdd(LosFilePage *page, struct page_mapping *mapping, VM_OFFSET_T pgoff)
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}
99///将页面加到活动文件页LRU链表上
100VOID OsAddToPageacheLru(LosFilePage *page, struct page_mapping *mapping, VM_OFFSET_T pgoff)
101{
102 OsPageCacheAdd(page, mapping, pgoff);
104}
105///从页高速缓存上删除页
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}
121/**************************************************************************************************
122每个进程都有自己的地址空间, 多个进程可以访问同一个LosFilePage,每个进程使用的虚拟地址都需要单独映射
123所以同一个LosFilePage会映射到多个进程空间.本函数记录页面被哪些进程映射过
124在两个地方会被被空间映射
1251.缺页中断 2.克隆地址空间
126**************************************************************************************************/
127VOID OsAddMapInfo(LosFilePage *page, LosArchMmu *archMmu, VADDR_T vaddr)
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}
143///通过虚拟地址获取文件页映射信息,archMmu每个进程都有属于自己的mmu
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}
156///删除页高速缓存和LRU,对应 OsAddToPageacheLru
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}
164
165//解除文件页和进程的映射关系
166STATIC VOID OsPageCacheUnmap(LosFilePage *fpage, LosArchMmu *archMmu, VADDR_T vaddr)
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}
185///删除文件
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}
232///标记page为脏页 进程修改了高速缓存里的数据时,该页就被内核标记为脏页
233VOID OsMarkPageDirty(LosFilePage *fpage, LosVmMapRegion *region, INT32 off, INT32 len)
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}
250
251STATIC UINT32 GetDirtySize(LosFilePage *fpage, struct Vnode *vnode)
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}
277///冲洗脏页,回写磁盘
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}
309///备份脏页,老脏页撕掉脏页标签
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}
325///冲洗脏页数据,将脏页数据回写磁盘
327{
328 if (fpage == NULL) {
329 return;
330 }
331 (VOID)OsFlushDirtyPage(fpage);
333}
334
335STATIC VOID OsReleaseFpage(struct page_mapping *mapping, LosFilePage *fpage)
336{
337 UINT32 intSave;
338 UINT32 lruSave;
339 SPIN_LOCK_S *lruLock = &fpage->physSeg->lruLock;
341 LOS_SpinLockSave(lruLock, &lruSave);
344 LOS_SpinUnlockRestore(lruLock, lruSave);
346}
347///删除映射信息
348VOID OsDelMapInfo(LosVmMapRegion *region, LosVmPgFault *vmf, BOOL cleanDirty)
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}
381/*!
382文件缺页时的处理,先读入磁盘数据,再重新读页数据
383被 OsDoReadFault(...),OsDoCowFault(...),OsDoSharedFault(...) 等调用
384*/
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}
452///文件缓存冲洗,把所有fpage冲洗一边,把脏页洗到dirtyList中,配合OsFileCacheRemove理解
453VOID OsFileCacheFlush(struct page_mapping *mapping)
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}
481
482/******************************************************************************
483 删除文件缓存,清空文件在page cache的痕迹
484 参数 mapping 可理解为文件在内存的身份证
485******************************************************************************/
486VOID OsFileCacheRemove(struct page_mapping *mapping)
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}
516///虚拟内存文件操作实现类
518 .open = NULL,
519 .close = NULL,
520 .fault = OsVmmFileFault, //缺页中断处理
521 .remove = OsVmmFileRemove,//删除页
522};
523//文件映射
524INT32 OsVfsFileMmap(struct file *filep, LosVmMapRegion *region)
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}
532/*!
533 有名映射,可理解为文件映射,跟匿名映射相对应
534 参数filep是广义的文件,在鸿蒙内核,目录/普通文件/字符设备/块设备/网络套接字/管道/链接 都是文件
535*/
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}
566
567/**************************************************************************************************
568 通过位置从文件映射页中找到的指定的文件页
569 举例:mapping->page_list上节点的数据可能只有是文件 1,3,4,6 页的数据,此时来找文件第5页的数据就会没有
570**************************************************************************************************/
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}
587
588/* need mutex & change memory to dma zone. */
589/*!
590以页高速缓存方式分配一个文件页 LosFilePage
591 Direct Memory Access(存储器直接访问)指一种高速的数据传输操作,允许在外部设备和存储器之间直接读写数据。
592 整个数据传输操作在一个称为"DMA控制器"的控制下进行的。CPU只需在数据传输开始和结束时做一点处理(开始和结束时候要做中断处理)
593*/
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}
637
638#ifndef LOSCFG_FS_VFS
639INT32 OsVfsFileMmap(struct file *filep, LosVmMapRegion *region)
640{
641 UNUSED(filep);
642 UNUSED(region);
643 return ENOERR;
644}
645#endif
646
647#endif
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_ListInit(LOS_DL_LIST *list)
Definition: los_list.h:104
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
LITE_OS_SEC_ALW_INLINE STATIC INLINE VOID LOS_ListDelete(LOS_DL_LIST *node)
Definition: los_list.h:292
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
UINT32 LOS_MemFree(VOID *pool, VOID *ptr)
释放从指定动态内存中申请的内存
Definition: los_memory.c:1369
UINT8 * m_aucSysMem0
异常交互动态内存池地址的起始地址,当不支持异常交互特性时,m_aucSysMem0等于m_aucSysMem1。
Definition: los_memory.c:107
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
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
signed int INT32
Definition: los_typedef.h:60
int status_t
Definition: los_typedef.h:205
unsigned long paddr_t
Definition: los_typedef.h:209
unsigned long vaddr_t
Definition: los_typedef.h:206
unsigned long VADDR_T
Definition: los_typedef.h:208
unsigned long VM_OFFSET_T
Definition: los_typedef.h:212
int STATUS_T
Definition: los_typedef.h:215
unsigned int UINT32
Definition: los_typedef.h:57
size_t BOOL
Definition: los_typedef.h:88
VOID OsDeletePageCacheLru(LosFilePage *page)
删除页高速缓存和LRU,对应 OsAddToPageacheLru
LosMapInfo * OsGetMapInfo(LosFilePage *page, LosArchMmu *archMmu, VADDR_T vaddr)
通过虚拟地址获取文件页映射信息,archMmu每个进程都有属于自己的mmu
VOID OsVmmFileRemove(LosVmMapRegion *region, LosArchMmu *archMmu, VM_OFFSET_T pgoff)
删除文件
VOID OsFileCacheFlush(struct page_mapping *mapping)
文件缓存冲洗,把所有fpage冲洗一边,把脏页洗到dirtyList中,配合OsFileCacheRemove理解
VOID OsAddToPageacheLru(LosFilePage *page, struct page_mapping *mapping, VM_OFFSET_T pgoff)
将页面加到活动文件页LRU链表上
INT32 OsVmmFileFault(LosVmMapRegion *region, LosVmPgFault *vmf)
LosFilePage * OsDumpDirtyPage(LosFilePage *oldFPage)
备份脏页,老脏页撕掉脏页标签
VOID OsAddMapInfo(LosFilePage *page, LosArchMmu *archMmu, VADDR_T vaddr)
INT32 OsVfsFileMmap(struct file *filep, LosVmMapRegion *region)
STATIC VOID OsReleaseFpage(struct page_mapping *mapping, LosFilePage *fpage)
STATIC UINT32 GetDirtySize(LosFilePage *fpage, struct Vnode *vnode)
VOID OsPageCacheDel(LosFilePage *fpage)
从页高速缓存上删除页
VOID OsDoFlushDirtyPage(LosFilePage *fpage)
冲洗脏页数据,将脏页数据回写磁盘
static int g_totalPageCacheTry
STATIC INT32 OsFlushDirtyPage(LosFilePage *fpage)
冲洗脏页,回写磁盘
LosFilePage * OsPageCacheAlloc(struct page_mapping *mapping, VM_OFFSET_T pgoff)
static int g_totalPageCacheHit
VOID OsMarkPageDirty(LosFilePage *fpage, LosVmMapRegion *region, INT32 off, INT32 len)
标记page为脏页 进程修改了高速缓存里的数据时,该页就被内核标记为脏页
STATIC VOID OsPageCacheAdd(LosFilePage *page, struct page_mapping *mapping, VM_OFFSET_T pgoff)
VOID ResetPageCacheHitInfo(int *try, int *hit)
VOID OsDelMapInfo(LosVmMapRegion *region, LosVmPgFault *vmf, BOOL cleanDirty)
删除映射信息
LosVmFileOps g_commVmOps
虚拟内存文件操作实现类
VOID OsFileCacheRemove(struct page_mapping *mapping)
LosFilePage * OsFindGetEntry(struct page_mapping *mapping, VM_OFFSET_T pgoff)
STATIC VOID OsPageCacheUnmap(LosFilePage *fpage, LosArchMmu *archMmu, VADDR_T vaddr)
STATUS_T OsNamedMMap(struct file *filep, LosVmMapRegion *region)
VOID OsPageRefDecNoLock(LosFilePage *page)
Definition: los_vm_scan.c:189
VOID OsPageRefIncLocked(LosFilePage *page)
Definition: los_vm_scan.c:145
VOID OsLruCacheAdd(LosFilePage *fpage, enum OsLruList lruType)
Definition: los_vm_scan.c:69
STATIC INLINE VOID OsCleanPageDirty(LosVmPage *page)
给页面撕掉数据被修改的标签
STATIC INLINE VOID OsCleanPageLocked(LosVmPage *page)
给页面撕掉被锁的标签
STATIC INLINE VOID OsSetPageDirty(LosVmPage *page)
给页面贴上数据被修改的标签
VOID OsUnmapAllLocked(LosFilePage *page)
解除文件页在所有进程的映射
Definition: los_vm_scan.c:57
VOID OsLruCacheDel(LosFilePage *fpage)
Definition: los_vm_scan.c:85
STATIC INLINE BOOL OsIsPageMapped(LosFilePage *page)
文件页是否映射过了
STATIC INLINE BOOL OsIsPageDirty(LosVmPage *page)
页面是否为脏页,所谓脏页就是页内数据是否被更新过,只有脏页才会有写时拷贝
struct FilePage LosFilePage
文件页结构体
VOID OsUnmapPageLocked(LosFilePage *page, LosMapInfo *info)
Definition: los_vm_scan.c:44
STATIC INLINE VOID OsSetPageLocked(LosVmPage *page)
给页面贴上被锁的标签
LOS_DL_LIST_HEAD(g_vmSpaceList)
初始化全局虚拟空间节点,所有虚拟空间都挂到此节点上.
STATIC INLINE VOID LOS_SetRegionTypeFile(LosVmMapRegion *region)
设置线性区为文件映射
Definition: los_vm_map.h:250
BOOL LOS_IsRegionFileValid(LosVmMapRegion *region)
映射类型为文件的线性区是否有效
Definition: los_vm_map.c:512
STATIC INLINE VOID LOS_SetRegionTypeDev(LosVmMapRegion *region)
设为设备映射线性区
Definition: los_vm_map.h:260
LosVmPage * LOS_VmPageGet(PADDR_T paddr)
通过物理地址获取页框
Definition: los_vm_page.c:120
@ VM_LRU_ACTIVE_FILE
活动文件页(磁盘)
Definition: los_vm_phys.h:78
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
VOID * OsVmPageToVaddr(LosVmPage *page)
通过page获取内核空间的虚拟地址 参考OsArchMmuInit #define SYS_MEM_BASE DDR_MEM_ADDR /* physical memory base 物理地址的起始...
Definition: los_vm_phys.c:288
内存管理单元(英语:memory management unit,缩写为MMU),有时称作分页内存管理单元(英语:paged memory management unit,缩写为PMMU)。
Definition: los_arch_mmu.h:86
文件页结构体
LOS_DL_LIST i_mmap
LOS_DL_LIST lru
lru节点, 结合 LosVmPhysSeg: LOS_DL_LIST lruList[VM_NR_LRU_LISTS] 理解
struct VmPage * vmPage
物理页框
UINT32 n_maps
struct VmPhysSeg * physSeg
UINT16 dirtyOff
脏页的页内偏移地址
VM_OFFSET_T pgoff
页标,文件被切成一页一页读到内存
struct page_mapping * mapping
此结构由文件系统提供,用于描述装入点 见于 ..\third_party\NuttX\fs\fs.h
LOS_DL_LIST node
节点,节点挂到page_mapping.page_list上,链表以 pgoff 从小到大方式排序.
UINT16 dirtyEnd
脏页的结束位置
UINT32 flags
标签
虚拟地址和文件页的映射信息,在一个进程使用文件页之前,需要提前做好文件页在此内存空间的映射关系,如此通过虚拟内存就可以对文件页读写操作.
LosFilePage * page
文件页中只记录物理地址,是不会变的.但它是需要被多个进程访问,和映射的.
VADDR_T vaddr
虚拟地址.每个进程访问同一个文件页的虚拟地址都是不一样的
LosArchMmu * archMmu
mmu完成vaddr和page->vmPage->physAddr物理地址的映射
LOS_DL_LIST node
节点,挂到page->i_mmap链表上.链表上记录要操作文件页的进程对这个page的映射信息
缺页结构信息体
Definition: los_vm_map.h:95
unsigned long pgoff
Definition: los_vm_map.h:97
VADDR_T * pageKVaddr
Definition: los_vm_map.h:99
VADDR_T vaddr
Definition: los_vm_map.h:98
UINT32 flags
Definition: los_vm_map.h:96
虚拟内存文件操作函数指针,上层开发可理解为 class 里的方法,注意是对线性区的操作 , 文件操作 见于g_commVmOps
Definition: los_vm_map.h:102
void(* open)(struct VmMapRegion *region)
打开
Definition: los_vm_map.h:103
VADDR_T base
Definition: los_vm_map.h:84
struct VmMapRegion::@4::VmRegionFile rf
union VmMapRegion::@4 unTypeData
UINT32 regionFlags
Definition: los_vm_map.h:125
LosVmSpace * space
所属虚拟空间,虚拟空间由多个线性区组成
Definition: los_vm_map.h:121
LosVmMapRange range
Definition: los_vm_map.h:123
VM_OFFSET_T pgOff
Definition: los_vm_map.h:124
物理页框描述符 虚拟内存体现的是程序对内存资源的需求,而物理内存是对该请求的供应。 伙伴算法的思想是:把内存中连续的空闲页框空间看成是空闲页框块,并按照它们的大小(连续页框的数目)分组
Definition: los_vm_page.h:53
Atomic refCounts
Definition: los_vm_page.h:57
物理段描述符
Definition: los_vm_phys.h:85
SPIN_LOCK_S lruLock
用于置换的自旋锁,用于操作lruList
Definition: los_vm_phys.h:91
LosArchMmu archMmu
Definition: los_vm_map.h:157
vnode并不包含文件名,因为 vnode和文件名是 1:N 的关系
Definition: vnode.h:164
enum VnodeType type
Definition: vnode.h:165
struct page_mapping mapping
Definition: vnode.h:183
struct VnodeOps * vop
Definition: vnode.h:174
int useCount
Definition: vnode.h:166
char * filePath
Definition: vnode.h:182
ssize_t(* WritePage)(struct Vnode *vnode, char *buffer, off_t pos, size_t buflen)
Definition: vnode.h:195
ssize_t(* ReadPage)(struct Vnode *vnode, char *buffer, off_t pos)
Definition: vnode.h:194
int f_oflags
unsigned long nrpages
SPIN_LOCK_S list_lock
LOS_DL_LIST page_list
struct file * host
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