更新日期: 2022/06/01 来源: https://gitee.com/weharmony/kernel_liteos_a_note
los_vm_scan.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#ifdef LOSCFG_FS_VFS
33
34#include "fs/file.h"
35#include "los_vm_filemap.h"
36
37#ifdef LOSCFG_KERNEL_VM
38
39/* unmap a lru page by map record info caller need lru lock */
40/**************************************************************************************************
41 解除文件页和进程(mmu)的映射关系
42 参数info记录了进程的MMU
43**************************************************************************************************/
45{
46 if (page == NULL || info == NULL) {
47 VM_ERR("UnmapPage error input null!");
48 return;
49 }
50 page->n_maps--;
51 LOS_ListDelete(&info->node);
53 LOS_ArchMmuUnmap(info->archMmu, info->vaddr, 1);
54 LOS_MemFree(m_aucSysMem0, info);//释放虚拟
55}
56///解除文件页在所有进程的映射
58{
59 LosMapInfo *info = NULL;
60 LosMapInfo *next = NULL;
61 LOS_DL_LIST *immap = &page->i_mmap;
62
63 LOS_DL_LIST_FOR_EACH_ENTRY_SAFE(info, next, immap, LosMapInfo, node) {//遍历 immap->info 链表
64 OsUnmapPageLocked(page, info);
65 }
66}
67
68/* add a new lru node to lru list, lruType can be file or anon */
69VOID OsLruCacheAdd(LosFilePage *fpage, enum OsLruList lruType)//在lru列表中添加一个新的lru节点,lruType可以是文件或匿名
70{
71 UINT32 intSave;
72 LosVmPhysSeg *physSeg = fpage->physSeg; //得到页面对应段
73 LosVmPage *page = fpage->vmPage; //得到物理页面
74
75 LOS_SpinLockSave(&physSeg->lruLock, &intSave);//自旋锁:最多只能被一个内核持有,CPU内核 互斥锁
76 OsSetPageActive(page); //设置页面为活动页
77 OsCleanPageReferenced(page);//清除页面被引用位
78 physSeg->lruSize[lruType]++; //lruType页总size++
79 LOS_ListTailInsert(&physSeg->lruList[lruType], &fpage->lru);//加入lruType页双循环链表中
80
81 LOS_SpinUnlockRestore(&physSeg->lruLock, intSave);//解锁
82}
83
84/* dellete a lru node, caller need hold lru_lock */
85VOID OsLruCacheDel(LosFilePage *fpage)//删除lru节点,调用者需要拿到lru锁
86{
87 LosVmPhysSeg *physSeg = fpage->physSeg; //得到页面对应段
88 int type = OsIsPageActive(fpage->vmPage) ? VM_LRU_ACTIVE_FILE : VM_LRU_INACTIVE_FILE;//得到页面LRU类型
89
90 physSeg->lruSize[type]--; //type页总size--
91 LOS_ListDelete(&fpage->lru);//将自己从lru链表中摘出来
92}
93///非活动文件页低于活动文件页吗
95{
96 return (physSeg->lruSize[VM_LRU_ACTIVE_FILE] >
97 physSeg->lruSize[VM_LRU_INACTIVE_FILE]) ? TRUE : FALSE;//直接对比size,效率杠杠的
98}
99
100/* move a page from inactive list to active list head */
101STATIC INLINE VOID OsMoveToActiveList(LosFilePage *fpage)//将页面从非活动列表移动到活动列表
102{
103 LosVmPhysSeg *physSeg = fpage->physSeg; //得到页面对应段
104
105 physSeg->lruSize[VM_LRU_ACTIVE_FILE]++; //活动页总size++
106 physSeg->lruSize[VM_LRU_INACTIVE_FILE]--; //不活动页总size--
107 LOS_ListDelete(&fpage->lru); //将自己从lru链表中摘出来
108 LOS_ListTailInsert(&physSeg->lruList[VM_LRU_ACTIVE_FILE], &fpage->lru);//加入活动页双循环链表中
109}
110
111/* move a page from active list to inactive list head */
112STATIC INLINE VOID OsMoveToInactiveList(LosFilePage *fpage)//将页面从活动列表移动到非活动列表
113{
114 LosVmPhysSeg *physSeg = fpage->physSeg; //得到页面对应段
115
116 physSeg->lruSize[VM_LRU_ACTIVE_FILE]--; //活动页总size--
117 physSeg->lruSize[VM_LRU_INACTIVE_FILE]++; //不活动页总size++
118 LOS_ListDelete(&fpage->lru); //将自己从lru链表中摘出来
119 LOS_ListTailInsert(&physSeg->lruList[VM_LRU_INACTIVE_FILE], &fpage->lru);//加入不活动页双循环链表中
120}
121
122/* move a page to the most active pos in lru list(active head) *///将页面移至lru列表中最活跃的位置
123STATIC INLINE VOID OsMoveToActiveHead(LosFilePage *fpage)
124{
125 LosVmPhysSeg *physSeg = fpage->physSeg; //得到页面对应段
126 LOS_ListDelete(&fpage->lru); //将自己从lru链表中摘出来
127 LOS_ListTailInsert(&physSeg->lruList[VM_LRU_ACTIVE_FILE], &fpage->lru);//加入活动页双循环链表中
128}
129
130/* move a page to the most active pos in lru list(inactive head) */
131STATIC INLINE VOID OsMoveToInactiveHead(LosFilePage *fpage)//鸿蒙会从inactive链表的尾部开始进行回收,跟linux一样
132{
133 LosVmPhysSeg *physSeg = fpage->physSeg; //得到页面对应段
134 LOS_ListDelete(&fpage->lru); //将自己从lru链表中摘出来
135 LOS_ListTailInsert(&physSeg->lruList[VM_LRU_INACTIVE_FILE], &fpage->lru);//加入不活动页双循环链表中
136}
137
138/* page referced add: (call by page cache get)
139----------inactive----------|----------active------------
140[ref:0,act:0], [ref:1,act:0]|[ref:0,act:1], [ref:1,act:1]
141ref:0, act:0 --> ref:1, act:0
142ref:1, act:0 --> ref:0, act:1
143ref:0, act:1 --> ref:1, act:1
144*/
145VOID OsPageRefIncLocked(LosFilePage *fpage)// ref ,act 标签转换功能
146{
147 BOOL isOrgActive;
148 UINT32 intSave;
149 LosVmPage *page = NULL;
150
151 if (fpage == NULL) {
152 return;
153 }
154
155 LOS_SpinLockSave(&fpage->physSeg->lruLock, &intSave);//要处理lruList,先拿锁
156
157 page = fpage->vmPage;//拿到物理页框
158 isOrgActive = OsIsPageActive(page);//页面是否在活动
159
160 if (OsIsPageReferenced(page) && !OsIsPageActive(page)) {//身兼 不活动和引用标签
161 OsCleanPageReferenced(page);//撕掉引用标签 ref:1, act:0 --> ref:0, act:1
162 OsSetPageActive(page); //贴上活动标签
163 } else if (!OsIsPageReferenced(page)) {
164 OsSetPageReferenced(page);//ref:0, act:0 --> ref:1, act:0
165 }
166
167 if (!isOrgActive && OsIsPageActive(page)) {
168 /* move inactive to active */
169 OsMoveToActiveList(fpage);
170 /* no change, move head */
171 } else {
172 if (OsIsPageActive(page)) {
173 OsMoveToActiveHead(fpage);
174 } else {
176 }
177 }
178
179 LOS_SpinUnlockRestore(&fpage->physSeg->lruLock, intSave);
180}
181
182/* page referced dec: (call by thrinker)
183----------inactive----------|----------active------------
184[ref:0,act:0], [ref:1,act:0]|[ref:0,act:1], [ref:1,act:1]
185ref:1, act:1 --> ref:0, act:1
186ref:0, act:1 --> ref:1, act:0
187ref:1, act:0 --> ref:0, act:0
188*/
189VOID OsPageRefDecNoLock(LosFilePage *fpage) // ref ,act 标签转换功能
190{
191 BOOL isOrgActive;
192 LosVmPage *page = NULL;
193
194 if (fpage == NULL) {
195 return;
196 }
197
198 page = fpage->vmPage;
199 isOrgActive = OsIsPageActive(page);
200
201 if (!OsIsPageReferenced(page) && OsIsPageActive(page)) {//[ref:0,act:1]的情况
202 OsCleanPageActive(page);
204 } else if (OsIsPageReferenced(page)) {
206 }
207
208 if (isOrgActive && !OsIsPageActive(page)) {
210 }
211}
212///缩小活动页链表
213VOID OsShrinkActiveList(LosVmPhysSeg *physSeg, int nScan)
214{
215 LosFilePage *fpage = NULL;
216 LosFilePage *fnext = NULL;
217 LOS_DL_LIST *activeFile = &physSeg->lruList[VM_LRU_ACTIVE_FILE];
218
219 LOS_DL_LIST_FOR_EACH_ENTRY_SAFE(fpage, fnext, activeFile, LosFilePage, lru) {//一页一页处理
220 if (LOS_SpinTrylock(&fpage->mapping->list_lock) != LOS_OK) {//尝试获取文件页所在的page_mapping锁
221 continue;//接着处理下一文件页
222 }
223
224 /* happend when caller hold cache lock and try reclaim this page *///调用方持有缓存锁并尝试回收此页时发生
225 if (OsIsPageLocked(fpage->vmPage)) {//页面是否被锁
226 LOS_SpinUnlock(&fpage->mapping->list_lock);//失败时,一定要释放page_mapping锁.
227 continue;//接着处理下一文件页
228 }
229
230 if (OsIsPageMapped(fpage) && (fpage->flags & VM_MAP_REGION_FLAG_PERM_EXECUTE)) {//文件页是否被映射而且是个可执行文件 ?
231 LOS_SpinUnlock(&fpage->mapping->list_lock);//是时,一定要释放page_mapping锁.
232 continue;//接着处理下一文件页
233 }
234 //找了可以收缩的文件页
235 OsPageRefDecNoLock(fpage); //将页面移到未活动文件链表
236
237 LOS_SpinUnlock(&fpage->mapping->list_lock); //释放page_mapping锁.
238
239 if (--nScan <= 0) {
240 break;
241 }
242 }
243}
244///缩小未活动页链表
245int OsShrinkInactiveList(LosVmPhysSeg *physSeg, int nScan, LOS_DL_LIST *list)
246{
247 UINT32 nrReclaimed = 0;
248 LosVmPage *page = NULL;
249 SPIN_LOCK_S *flock = NULL;
250 LosFilePage *fpage = NULL;
251 LosFilePage *fnext = NULL;
252 LosFilePage *ftemp = NULL;
253 LOS_DL_LIST *inactive_file = &physSeg->lruList[VM_LRU_INACTIVE_FILE];
254
255 LOS_DL_LIST_FOR_EACH_ENTRY_SAFE(fpage, fnext, inactive_file, LosFilePage, lru) {//遍历链表一页一页处理
256 flock = &fpage->mapping->list_lock;
257
258 if (LOS_SpinTrylock(flock) != LOS_OK) {//尝试获取文件页所在的page_mapping锁
259 continue;//接着处理下一文件页
260 }
261
262 page = fpage->vmPage;//获取物理页框
263 if (OsIsPageLocked(page)) {//页面是否被锁
264 LOS_SpinUnlock(flock);
265 continue;//接着处理下一文件页
266 }
267
268 if (OsIsPageMapped(fpage) && (OsIsPageDirty(page) || (fpage->flags & VM_MAP_REGION_FLAG_PERM_EXECUTE))) {
269 LOS_SpinUnlock(flock);//文件页是否被映射而且是个脏页获取是个可执行文件 ?
270 continue;//接着处理下一文件页
271 }
272
273 if (OsIsPageDirty(page)) {//是脏页
274 ftemp = OsDumpDirtyPage(fpage);//备份脏页
275 if (ftemp != NULL) {//备份成功了
276 LOS_ListTailInsert(list, &ftemp->node);//将脏页挂到参数链表上带走
277 }
278 }
279
280 OsDeletePageCacheLru(fpage);//将文件页从LRU和pagecache上摘除
281 LOS_SpinUnlock(flock);
282 nrReclaimed++;//成功回收了一页
283
284 if (--nScan <= 0) {//继续回收
285 break;
286 }
287 }
288
289 return nrReclaimed;
290}
291
292#ifdef LOSCFG_FS_VFS
293int OsTryShrinkMemory(size_t nPage)//尝试收缩文件页
294{
295 UINT32 intSave;
296 size_t totalPages;
297 size_t nReclaimed = 0;
298 LosVmPhysSeg *physSeg = NULL;
299 UINT32 index;
300 LOS_DL_LIST_HEAD(dirtyList);//初始化脏页链表,上面将挂所有脏页用于同步到磁盘后回收
301 LosFilePage *fpage = NULL;
302 LosFilePage *fnext = NULL;
303
304 if (nPage <= 0) {
305 nPage = VM_FILEMAP_MIN_SCAN;//
306 }
307
308 if (nPage > VM_FILEMAP_MAX_SCAN) {
309 nPage = VM_FILEMAP_MAX_SCAN;
310 }
311
312 for (index = 0; index < g_vmPhysSegNum; index++) {//遍历整个物理段组
313 physSeg = &g_vmPhysSeg[index];//一段段来
314 LOS_SpinLockSave(&physSeg->lruLock, &intSave);
315 totalPages = physSeg->lruSize[VM_LRU_ACTIVE_FILE] + physSeg->lruSize[VM_LRU_INACTIVE_FILE];//统计所有文件页
316 if (totalPages < VM_FILEMAP_MIN_SCAN) {//文件页占用内存不多的情况下,怎么处理?
317 LOS_SpinUnlockRestore(&physSeg->lruLock, intSave);
318 continue;//放过这一段,找下一段
319 }
320
321 if (OsInactiveListIsLow(physSeg)) {
322 OsShrinkActiveList(physSeg, (nPage < VM_FILEMAP_MIN_SCAN) ? VM_FILEMAP_MIN_SCAN : nPage);//缩小活动页
323 }
324
325 nReclaimed += OsShrinkInactiveList(physSeg, nPage, &dirtyList);//缩小未活动页,带出脏页链表
326 LOS_SpinUnlockRestore(&physSeg->lruLock, intSave);
327
328 if (nReclaimed >= nPage) {//够了,够了,达到目的了.
329 break;//退出收缩
330 }
331 }
332
333 LOS_DL_LIST_FOR_EACH_ENTRY_SAFE(fpage, fnext, &dirtyList, LosFilePage, node) {//遍历处理脏页数据
334 OsDoFlushDirtyPage(fpage);//冲洗脏页数据,将脏页数据回写磁盘
335 }
336
337 return nReclaimed;
338}
339#else
340int OsTryShrinkMemory(size_t nPage)
341{
342 return 0;
343}
344#endif
345#endif
346
347#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_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
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
VOID LOS_SpinUnlockRestore(SPIN_LOCK_S *lock, UINT32 intSave)
Definition: los_spinlock.c:108
INT32 LOS_SpinTrylock(SPIN_LOCK_S *lock)
Definition: los_spinlock.c:61
VOID LOS_SpinLockSave(SPIN_LOCK_S *lock, UINT32 *intSave)
Definition: los_spinlock.c:98
VOID LOS_SpinUnlock(SPIN_LOCK_S *lock)
Definition: los_spinlock.c:84
unsigned int UINT32
Definition: los_typedef.h:57
size_t BOOL
Definition: los_typedef.h:88
STATIC INLINE VOID OsSetPageActive(LosVmPage *page)
给页面贴上活动的标签
VOID OsDeletePageCacheLru(LosFilePage *page)
删除页高速缓存和LRU,对应 OsAddToPageacheLru
STATIC INLINE VOID OsCleanPageReferenced(LosVmPage *page)
给页面撕掉被引用的标签
VOID OsDoFlushDirtyPage(LosFilePage *fpage)
冲洗脏页数据,将脏页数据回写磁盘
STATIC INLINE BOOL OsIsPageMapped(LosFilePage *page)
文件页是否映射过了
STATIC INLINE BOOL OsIsPageActive(LosVmPage *page)
页面是否活动
STATIC INLINE BOOL OsIsPageDirty(LosVmPage *page)
页面是否为脏页,所谓脏页就是页内数据是否被更新过,只有脏页才会有写时拷贝
STATIC INLINE BOOL OsIsPageReferenced(LosVmPage *page)
页面是否被引用,只被一个进程引用的页叫私有页,多个进程引用就是共享页,此为共享内存的本质所在
STATIC INLINE VOID OsSetPageReferenced(LosVmPage *page)
给页面贴上被引用的标签
STATIC INLINE VOID OsCleanPageActive(LosVmPage *page)
给页面撕掉活动的标签
STATIC INLINE BOOL OsIsPageLocked(LosVmPage *page)
页面是否被锁
LosFilePage * OsDumpDirtyPage(LosFilePage *oldPage)
备份脏页,老脏页撕掉脏页标签
LOS_DL_LIST_HEAD(g_vmSpaceList)
初始化全局虚拟空间节点,所有虚拟空间都挂到此节点上.
INT32 g_vmPhysSegNum
段总数
Definition: los_vm_phys.c:88
OsLruList
Lru全称是Least Recently Used,即最近最久未使用的意思 针对匿名页和文件页各拆成对应链表。
Definition: los_vm_phys.h:74
@ VM_LRU_ACTIVE_FILE
活动文件页(磁盘)
Definition: los_vm_phys.h:78
@ VM_LRU_INACTIVE_FILE
非活动文件页(磁盘)
Definition: los_vm_phys.h:77
struct VmPhysSeg g_vmPhysSeg[VM_PHYS_SEG_MAX]
物理内存采用段页式管理,先切段后伙伴算法页
Definition: los_vm_phys.c:87
BOOL OsInactiveListIsLow(LosVmPhysSeg *physSeg)
非活动文件页低于活动文件页吗
Definition: los_vm_scan.c:94
VOID OsLruCacheAdd(LosFilePage *fpage, enum OsLruList lruType)
Definition: los_vm_scan.c:69
int OsShrinkInactiveList(LosVmPhysSeg *physSeg, int nScan, LOS_DL_LIST *list)
缩小未活动页链表
Definition: los_vm_scan.c:245
VOID OsShrinkActiveList(LosVmPhysSeg *physSeg, int nScan)
缩小活动页链表
Definition: los_vm_scan.c:213
VOID OsPageRefDecNoLock(LosFilePage *fpage)
Definition: los_vm_scan.c:189
VOID OsUnmapAllLocked(LosFilePage *page)
解除文件页在所有进程的映射
Definition: los_vm_scan.c:57
STATIC INLINE VOID OsMoveToActiveList(LosFilePage *fpage)
Definition: los_vm_scan.c:101
VOID OsLruCacheDel(LosFilePage *fpage)
Definition: los_vm_scan.c:85
STATIC INLINE VOID OsMoveToInactiveList(LosFilePage *fpage)
Definition: los_vm_scan.c:112
STATIC INLINE VOID OsMoveToActiveHead(LosFilePage *fpage)
Definition: los_vm_scan.c:123
VOID OsUnmapPageLocked(LosFilePage *page, LosMapInfo *info)
Definition: los_vm_scan.c:44
VOID OsPageRefIncLocked(LosFilePage *fpage)
Definition: los_vm_scan.c:145
int OsTryShrinkMemory(size_t nPage)
Definition: los_vm_scan.c:293
STATIC INLINE VOID OsMoveToInactiveHead(LosFilePage *fpage)
Definition: los_vm_scan.c:131
文件页结构体
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
struct page_mapping * mapping
此结构由文件系统提供,用于描述装入点 见于 ..\third_party\NuttX\fs\fs.h
LOS_DL_LIST node
节点,节点挂到page_mapping.page_list上,链表以 pgoff 从小到大方式排序.
UINT32 flags
标签
虚拟地址和文件页的映射信息,在一个进程使用文件页之前,需要提前做好文件页在此内存空间的映射关系,如此通过虚拟内存就可以对文件页读写操作.
VADDR_T vaddr
虚拟地址.每个进程访问同一个文件页的虚拟地址都是不一样的
LosArchMmu * archMmu
mmu完成vaddr和page->vmPage->physAddr物理地址的映射
LOS_DL_LIST node
节点,挂到page->i_mmap链表上.链表上记录要操作文件页的进程对这个page的映射信息
物理页框描述符 虚拟内存体现的是程序对内存资源的需求,而物理内存是对该请求的供应。 伙伴算法的思想是:把内存中连续的空闲页框空间看成是空闲页框块,并按照它们的大小(连续页框的数目)分组
Definition: los_vm_page.h:53
Atomic refCounts
Definition: los_vm_page.h:57
物理段描述符
Definition: los_vm_phys.h:85
LOS_DL_LIST lruList[VM_NR_LRU_LISTS]
页面置换算法,5个双循环链表头,它们分别描述五中不同类型的链表
Definition: los_vm_phys.h:93
SPIN_LOCK_S lruLock
用于置换的自旋锁,用于操作lruList
Definition: los_vm_phys.h:91
size_t lruSize[VM_NR_LRU_LISTS]
5个双循环链表大小,如此方便得到size
Definition: los_vm_phys.h:92
SPIN_LOCK_S list_lock