更新日期: 2022/06/01 来源: https://gitee.com/weharmony/kernel_liteos_a_note
los_vm_map.c
浏览该文件的文档.
1/*!
2 * @file los_vm_map.c
3 * @brief 虚拟内存管理
4 * @link vm http://weharmonyos.com/openharmony/zh-cn/device-dev/kernel/kernel-small-basic-memory-virtual.html @endlink
5 @verbatim
6基本概念
7 虚拟内存管理是计算机系统管理内存的一种技术。每个进程都有连续的虚拟地址空间,虚拟地址空间的大小由CPU的位数决定,
8 32位的硬件平台可以提供的最大的寻址空间为0-4GiB。整个4GiB空间分成两部分,LiteOS-A内核占据3GiB的高地址空间,
9 1GiB的低地址空间留给进程使用。各个进程空间的虚拟地址空间是独立的,代码、数据互不影响。
10
11 系统将虚拟内存分割为称为虚拟页的内存块,大小一般为4KiB或64KiB,LiteOS-A内核默认的页的大小是4KiB,
12 根据需要可以对MMU(Memory Management Units)进行配置。虚拟内存管理操作的最小单位就是一个页,
13 LiteOS-A内核中一个虚拟地址区间region包含地址连续的多个虚拟页,也可只有一个页。同样,物理内存也会按照页大小进行分割,
14 分割后的每个内存块称为页帧。虚拟地址空间划分:内核态占高地址3GiB(0x40000000 ~ 0xFFFFFFFF),
15 用户态占低地址1GiB(0x01000000 ~ 0x3F000000),具体见下表,详细可以查看或配置los_vm_zone.h。
16
17内核态地址规划:
18 Zone名称 描述 属性
19 ----------------------------------------------------------------------------
20 DMA zone 供IO设备的DMA使用。 Uncache
21
22 Normal zone 加载内核代码段、数据段、堆和栈的地址区间。 Cache
23
24 high mem zone可以分配连续的虚拟内存,但其所映射的物理内存不一定连续。Cache
25
26用户态地址规划:
27 Zone名称 描述 属性
28 ----------------------------------------------------------------------------
29 代码段 用户态代码段地址区间。 Cache
30 堆 用户态堆地址区间。 Cache
31 栈 用户态栈地址区间。 Cache
32 共享库 用于加载用户态共享库的地址区间,包括mmap所映射的区间。 Cache
33
34运行机制
35 虚拟内存管理中,虚拟地址空间是连续的,但是其映射的物理内存并不一定是连续的,如下图所示。
36 可执行程序加载运行,CPU访问虚拟地址空间的代码或数据时存在两种情况:
37
38 1. CPU访问的虚拟地址所在的页,如V0,已经与具体的物理页P0做映射,CPU通过找到进程对应的页表条目(详见虚实映射),
39 根据页表条目中的物理地址信息访问物理内存中的内容并返回。
40 2. CPU访问的虚拟地址所在的页,如V2,没有与具体的物理页做映射,系统会触发缺页异常,系统申请一个物理页,
41 并把相应的信息拷贝到物理页中,并且把物理页的起始地址更新到页表条目中。此时CPU重新执行访问虚拟内存的指令
42 便能够访问到具体的代码或数据。
43
44 @endverbatim
45 * @version
46 * @author weharmonyos.com | 鸿蒙研究站 | 每天死磕一点点
47 * @date 2021-11-25
48 */
49/*
50 * Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
51 * Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved.
52 *
53 * Redistribution and use in source and binary forms, with or without modification,
54 * are permitted provided that the following conditions are met:
55 *
56 * 1. Redistributions of source code must retain the above copyright notice, this list of
57 * conditions and the following disclaimer.
58 *
59 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
60 * of conditions and the following disclaimer in the documentation and/or other materials
61 * provided with the distribution.
62 *
63 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
64 * to endorse or promote products derived from this software without specific prior written
65 * permission.
66 *
67 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
68 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
69 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
70 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
71 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
72 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
73 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
74 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
75 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
76 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
77 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
78 */
79
80#include "los_vm_map.h"
81#include "los_vm_page.h"
82#include "los_vm_phys.h"
83#include "los_vm_dump.h"
84#include "los_vm_lock.h"
85#include "los_vm_zone.h"
86#include "los_vm_common.h"
87#include "los_vm_filemap.h"
88#include "los_vm_shm_pri.h"
89#include "los_arch_mmu.h"
90#include "los_process_pri.h"
91#ifdef LOSCFG_FS_VFS
92#include "fs/file.h"
93#include "vnode.h"
94#endif
95#include "los_task.h"
96#include "los_memory_pri.h"
97#include "los_vm_boot.h"
98
99
100#ifdef LOSCFG_KERNEL_VM
101
102#define VM_MAP_WASTE_MEM_LEVEL (PAGE_SIZE >> 2) ///< 浪费内存大小(1K)
103LosMux g_vmSpaceListMux; ///< 用于锁g_vmSpaceList的互斥量
104LOS_DL_LIST_HEAD(g_vmSpaceList); ///< 初始化全局虚拟空间节点,所有虚拟空间都挂到此节点上.
105LosVmSpace g_kVmSpace; ///< 内核非分配空间,用于内核运行栈,代码区,数据区
106LosVmSpace g_vMallocSpace; ///< 内核分配空间,用于内核分配内存
107
108/************************************************************
109* 获取进程空间系列接口
110************************************************************/
111/// 获取当前进程空间结构体指针
113{
114 return OsCurrProcessGet()->vmSpace;
115}
116/// 获取虚拟地址对应的进程空间结构体指针
118{
119 if (LOS_IsKernelAddress(vaddr)) { //是否为内核空间
120 return LOS_GetKVmSpace(); //获取内核空间
121 } else if (LOS_IsUserAddress(vaddr)) {//是否为用户空间
122 return LOS_CurrSpaceGet();
123 } else if (LOS_IsVmallocAddress(vaddr)) {//是否为内核分配空间
124 return LOS_GetVmallocSpace();//获取内核分配空间
125 } else {
126 return NULL;
127 }
128}
129///内核空间只有g_kVmSpace一个,所有的内核进程都共用一个内核空间
131{
132 return &g_kVmSpace;
133}
134///获取进程空间链表指针 g_vmSpaceList中挂的是进程空间 g_kVmSpace, g_vMallocSpace,所有用户进程的空间(独有一个进程空间)
136{
137 return &g_vmSpaceList;
138}
139///获取内核堆空间的全局变量
141{
142 return &g_vMallocSpace;
143}
144
145/************************************************************
146* 虚拟地址区间region相关的操作
147************************************************************/
148///释放挂在红黑树上节点,等于释放了线性区
150{
151 LOS_MemFree(m_aucSysMem0, pstNode);
152 return LOS_OK;
153}
154///通过红黑树节点找到对应的线性区
156{
157 LosVmMapRegion *region = (LosVmMapRegion *)LOS_DL_LIST_ENTRY(pstNode, LosVmMapRegion, rbNode);
158 return (VOID *)&region->range;
159}
160///比较两个红黑树节点
161ULONG_T OsRegionRbCmpKeyFn(const VOID *pNodeKeyA, const VOID *pNodeKeyB)
162{
163 LosVmMapRange rangeA = *(LosVmMapRange *)pNodeKeyA;
164 LosVmMapRange rangeB = *(LosVmMapRange *)pNodeKeyB;
165 UINT32 startA = rangeA.base;
166 UINT32 endA = rangeA.base + rangeA.size - 1;
167 UINT32 startB = rangeB.base;
168 UINT32 endB = rangeB.base + rangeB.size - 1;
169
170 if (startA > endB) {// A基地址大于B的结束地址
171 return RB_BIGGER; //说明线性区A更大,在右边
172 } else if (startA >= startB) {
173 if (endA <= endB) {
174 return RB_EQUAL; //相等,说明 A在B中
175 } else {
176 return RB_BIGGER; //说明 A的结束地址更大
177 }
178 } else if (startA <= startB) { //A基地址小于等于B的基地址
179 if (endA >= endB) {
180 return RB_EQUAL; //相等 说明 B在A中
181 } else {
182 return RB_SMALLER;//说明A的结束地址更小
183 }
184 } else if (endA < startB) {//A结束地址小于B的开始地址
185 return RB_SMALLER;//说明A在
186 }
187 return RB_EQUAL;
188}
189
190/*!
191 * @brief OsVmSpaceInitCommon 初始化进程虚拟空间,必须提供L1表的虚拟内存地址
192 *
193 * @param virtTtb L1表的地址,TTB表地址
194 * @param vmSpace
195 * @return
196 *
197 * @see
198 */
199STATIC BOOL OsVmSpaceInitCommon(LosVmSpace *vmSpace, VADDR_T *virtTtb)
200{
201 LOS_RbInitTree(&vmSpace->regionRbTree, OsRegionRbCmpKeyFn, OsRegionRbFreeFn, OsRegionRbGetKeyFn);//初始化虚拟存储空间-以红黑树组织方式
202
203 status_t retval = LOS_MuxInit(&vmSpace->regionMux, NULL);//初始化互斥量
204 if (retval != LOS_OK) {
205 VM_ERR("Create mutex for vm space failed, status: %d", retval);
206 return FALSE;
207 }
208
210 LOS_ListAdd(&g_vmSpaceList, &vmSpace->node);//将虚拟空间挂入全局虚拟空间双循环链表上
212
213 return OsArchMmuInit(&vmSpace->archMmu, virtTtb);//对mmu初始化
214}
215///@note_thinking 这个函数名称和内容不太搭
216VOID OsVmMapInit(VOID)
217{
218 status_t retval = LOS_MuxInit(&g_vmSpaceListMux, NULL);//初始化虚拟空间的互斥量
219 if (retval != LOS_OK) {
220 VM_ERR("Create mutex for g_vmSpaceList failed, status: %d", retval);
221 }
222}
223///初始化内核虚拟空间
224BOOL OsKernVmSpaceInit(LosVmSpace *vmSpace, VADDR_T *virtTtb)//内核空间页表是编译时放在bbs段指定的,共用 L1表
225{
226 vmSpace->base = KERNEL_ASPACE_BASE;//内核空间基地址, 线性区将分配在此范围
227 vmSpace->size = KERNEL_ASPACE_SIZE;//内核空间大小
228 vmSpace->mapBase = KERNEL_VMM_BASE;//内核空间映射区基地址
229 vmSpace->mapSize = KERNEL_VMM_SIZE;//内核空间映射区大小
230#ifdef LOSCFG_DRIVERS_TZDRIVER
231 vmSpace->codeStart = 0; //代码区开始地址
232 vmSpace->codeEnd = 0; //代码区结束地址
233#endif
234 return OsVmSpaceInitCommon(vmSpace, virtTtb);//virtTtb 用于初始化 mmu
235}
236///初始化内核堆空间
237BOOL OsVMallocSpaceInit(LosVmSpace *vmSpace, VADDR_T *virtTtb)//内核动态空间的页表是动态申请得来,共用 L1表
238{
239 vmSpace->base = VMALLOC_START; //内核堆空间基地址
240 vmSpace->size = VMALLOC_SIZE; //内核堆空间大小
241 vmSpace->mapBase = VMALLOC_START; //内核堆空间映射基地址
242 vmSpace->mapSize = VMALLOC_SIZE; //内核堆空间映射区大小
243#ifdef LOSCFG_DRIVERS_TZDRIVER
244 vmSpace->codeStart = 0;
245 vmSpace->codeEnd = 0;
246#endif
247 return OsVmSpaceInitCommon(vmSpace, virtTtb);//创建MMU,为后续的虚实映射做好初始化的工作
248}
249///内核虚拟空间初始化
250VOID OsKSpaceInit(VOID)
251{
252 OsVmMapInit();//初始化后续操作 g_vmSpaceList 的互斥锁
253 OsKernVmSpaceInit(&g_kVmSpace, OsGFirstTableGet()); //初始化内核进程虚拟空间
254 OsVMallocSpaceInit(&g_vMallocSpace, OsGFirstTableGet());//初始化内核动态分配空间
255}
256/*!
257 * @brief OsUserVmSpaceInit 用户空间的TTB表是动态申请得来,每个进程有属于自己的L1,L2表
258 * 初始化用户进程虚拟空间,主要划分数据区,堆区,映射区和创建mmu
259 * @param virtTtb
260 * @param vmSpace
261 * @return
262 *
263 * @see
264 */
266{
267 vmSpace->base = USER_ASPACE_BASE;//用户空间基地址
268 vmSpace->size = USER_ASPACE_SIZE;//用户空间大小
269 vmSpace->mapBase = USER_MAP_BASE;//用户空间映射基地址
270 vmSpace->mapSize = USER_MAP_SIZE;//用户空间映射大小
271 vmSpace->heapBase = USER_HEAP_BASE;//用户堆区开始地址,只有用户进程需要设置这里,动态内存的开始地址
272 vmSpace->heapNow = USER_HEAP_BASE;//堆区最新指向地址,用户堆空间大小可通过系统调用 do_brk()扩展
273 vmSpace->heap = NULL; //最近分配的一个堆线性区
274#ifdef LOSCFG_DRIVERS_TZDRIVER
275 vmSpace->codeStart = 0;
276 vmSpace->codeEnd = 0;
277#endif
278 return OsVmSpaceInitCommon(vmSpace, virtTtb);//创建MMU,为后续的虚实映射做好初始化的工作
279}
280/// 创建用户进程空间
282{
283 BOOL retVal = FALSE;
284
285 LosVmSpace *space = LOS_MemAlloc(m_aucSysMem0, sizeof(LosVmSpace));//在内核空间申请用户进程空间
286 if (space == NULL) {
287 return NULL;
288 }
289 //此处为何直接申请物理页帧存放用户进程的页表,大概是因为所有页表都被存放在内核空间(g_kVmSpace)而非内核分配空间(g_vMallocSpace)
290 VADDR_T *ttb = LOS_PhysPagesAllocContiguous(1);//分配一个物理页用于存放虚实映射关系表, 即:L1表
291 if (ttb == NULL) {//若连页表都没有,剩下的也别玩了.
292 (VOID)LOS_MemFree(m_aucSysMem0, space);
293 return NULL;
294 }
295
296 (VOID)memset_s(ttb, PAGE_SIZE, 0, PAGE_SIZE);//4K空间置0
297 retVal = OsUserVmSpaceInit(space, ttb);//初始化用户空间,mmu
298 LosVmPage *vmPage = OsVmVaddrToPage(ttb);//找到所在物理页框
299 if ((retVal == FALSE) || (vmPage == NULL)) {
300 (VOID)LOS_MemFree(m_aucSysMem0, space);
302 return NULL;
303 }
304 LOS_ListAdd(&space->archMmu.ptList, &(vmPage->node));//页表链表,先挂上L1,后续还会挂上 N个L2表
305
306 return space;
307}
308
309STATIC BOOL OsVmSpaceParamCheck(LosVmSpace *vmSpace)//这么简单也要写个函数?
310{
311 if (vmSpace == NULL) {
312 return FALSE;
313 }
314 return TRUE;
315}
316
317//虚拟内存空间克隆,被用于fork进程
319{
320 LosVmMapRegion *oldRegion = NULL;
321 LosVmMapRegion *newRegion = NULL;
322 LosRbNode *pstRbNode = NULL;
323 LosRbNode *pstRbNodeNext = NULL;
324 STATUS_T ret = LOS_OK;
325 UINT32 numPages;
326 PADDR_T paddr;
327 VADDR_T vaddr;
328 UINT32 intSave;
329 LosVmPage *page = NULL;
330 UINT32 flags;
331 UINT32 i;
332
333 if ((OsVmSpaceParamCheck(oldVmSpace) == FALSE) || (OsVmSpaceParamCheck(newVmSpace) == FALSE)) {
334 return LOS_ERRNO_VM_INVALID_ARGS;
335 }
336
337 if ((OsIsVmRegionEmpty(oldVmSpace) == TRUE) || (oldVmSpace == &g_kVmSpace)) {//不允许clone内核空间,内核空间是独一无二的.
338 return LOS_ERRNO_VM_INVALID_ARGS;
339 }
340 //空间克隆的主体实现是:线性区重新一个个分配物理内存,重新映射.
341 /* search the region list */
342 newVmSpace->mapBase = oldVmSpace->mapBase; //复制映射区基址
343 newVmSpace->heapBase = oldVmSpace->heapBase; //复制堆区基址
344 newVmSpace->heapNow = oldVmSpace->heapNow; //复制堆区当前使用到哪了
345 (VOID)LOS_MuxAcquire(&oldVmSpace->regionMux);
346 RB_SCAN_SAFE(&oldVmSpace->regionRbTree, pstRbNode, pstRbNodeNext)//红黑树循环开始
347 oldRegion = (LosVmMapRegion *)pstRbNode;
348 newRegion = OsVmRegionDup(newVmSpace, oldRegion, oldRegion->range.base, oldRegion->range.size);//复制线性区
349 if (newRegion == NULL) {
350 VM_ERR("dup new region failed");
351 ret = LOS_ERRNO_VM_NO_MEMORY;
352 break;
353 }
354
355#ifdef LOSCFG_KERNEL_SHM
356 if (oldRegion->regionFlags & VM_MAP_REGION_FLAG_SHM) {//如果老线性区是共享内存
357 OsShmFork(newVmSpace, oldRegion, newRegion);//fork共享线性区,如此新虚拟空间也能用那个线性区
358 continue;//不往下走了,因为共享内存不需要重新映射,下面无非就是需要MMU映射虚拟地址<-->物理地址
359 }
360#endif
361
362 if (oldRegion == oldVmSpace->heap) {//如果这个线性区是堆区
363 newVmSpace->heap = newRegion;//那么新的线性区也是新虚拟空间的堆区
364 }
365
366 numPages = newRegion->range.size >> PAGE_SHIFT;//计算线性区页数
367 for (i = 0; i < numPages; i++) {//一页一页进行重新映射
368 vaddr = newRegion->range.base + (i << PAGE_SHIFT);
369 if (LOS_ArchMmuQuery(&oldVmSpace->archMmu, vaddr, &paddr, &flags) != LOS_OK) {//先查物理地址
370 continue;
371 }
372
373 page = LOS_VmPageGet(paddr);//通过物理页获取物理内存的页框
374 if (page != NULL) {
375 LOS_AtomicInc(&page->refCounts);//refCounts 自增
376 }
377 if (flags & VM_MAP_REGION_FLAG_PERM_WRITE) {//可写入区标签
378 LOS_ArchMmuUnmap(&oldVmSpace->archMmu, vaddr, 1);//先删除老空间映射
379 LOS_ArchMmuMap(&oldVmSpace->archMmu, vaddr, paddr, 1, flags & ~VM_MAP_REGION_FLAG_PERM_WRITE);//老空间重新映射
380 }
381 LOS_ArchMmuMap(&newVmSpace->archMmu, vaddr, paddr, 1, flags & ~VM_MAP_REGION_FLAG_PERM_WRITE);//映射新空间
382
383#ifdef LOSCFG_FS_VFS //文件系统开关
384 if (LOS_IsRegionFileValid(oldRegion)) {//是都是一个文件映射线性区
385 LosFilePage *fpage = NULL;
386 LOS_SpinLockSave(&oldRegion->unTypeData.rf.vnode->mapping.list_lock, &intSave);
387 fpage = OsFindGetEntry(&oldRegion->unTypeData.rf.vnode->mapping, newRegion->pgOff + i);
388 if ((fpage != NULL) && (fpage->vmPage == page)) { /* cow page no need map */
389 OsAddMapInfo(fpage, &newVmSpace->archMmu, vaddr);//添加文件页映射,记录页面被进程映射过
390 }
391 LOS_SpinUnlockRestore(&oldRegion->unTypeData.rf.vnode->mapping.list_lock, intSave);
392 }
393#endif
394 }
395 RB_SCAN_SAFE_END(&oldVmSpace->regionRbTree, pstRbNode, pstRbNodeNext)//红黑树循环结束
396 (VOID)LOS_MuxRelease(&oldVmSpace->regionMux);
397 return ret;
398}
399///通过虚拟(线性)地址查找所属线性区,红黑树
400LosVmMapRegion *OsFindRegion(LosRbTree *regionRbTree, VADDR_T vaddr, size_t len)
401{
402 LosVmMapRegion *regionRst = NULL;
403 LosRbNode *pstRbNode = NULL;
404 LosVmMapRange rangeKey;
405 rangeKey.base = vaddr;
406 rangeKey.size = len;
407
408 if (LOS_RbGetNode(regionRbTree, (VOID *)&rangeKey, &pstRbNode)) {
409 regionRst = (LosVmMapRegion *)LOS_DL_LIST_ENTRY(pstRbNode, LosVmMapRegion, rbNode);
410 }
411 return regionRst;
412}
413/// 查找线性区 根据起始地址在进程空间内查找是否存在
415{
416 LosVmMapRegion *region = NULL;
417
418 (VOID)LOS_MuxAcquire(&vmSpace->regionMux);//因进程空间是隔离的,所以此处只会涉及到任务(线程)之间的竞争,故使用互斥锁,而自旋锁则用于CPU核间的竞争
419 region = OsFindRegion(&vmSpace->regionRbTree, addr, 1);
420 (VOID)LOS_MuxRelease(&vmSpace->regionMux);
421
422 return region;
423}
424/// 查找线性区 根据地址区间在进程空间内查找是否存在
426{
427 LosVmMapRegion *region = NULL;
428
429 (VOID)LOS_MuxAcquire(&vmSpace->regionMux);
430 region = OsFindRegion(&vmSpace->regionRbTree, addr, len);
431 (VOID)LOS_MuxRelease(&vmSpace->regionMux);
432
433 return region;
434}
435/// 分配指定长度的线性区
436VADDR_T OsAllocRange(LosVmSpace *vmSpace, size_t len)
437{
438 LosVmMapRegion *curRegion = NULL;
439 LosRbNode *pstRbNode = NULL;
440 LosRbNode *pstRbNodeTmp = NULL;
441 LosRbTree *regionRbTree = &vmSpace->regionRbTree;
442 VADDR_T curEnd = vmSpace->mapBase;//获取映射区基地址
443 VADDR_T nextStart;
444
445 curRegion = LOS_RegionFind(vmSpace, vmSpace->mapBase);
446 if (curRegion != NULL) {
447 pstRbNode = &curRegion->rbNode;
448 curEnd = curRegion->range.base + curRegion->range.size;
449 RB_MID_SCAN(regionRbTree, pstRbNode)
450 curRegion = (LosVmMapRegion *)pstRbNode;
451 nextStart = curRegion->range.base;
452 if (nextStart < curEnd) {
453 continue;
454 }
455 if ((nextStart - curEnd) >= len) {
456 return curEnd;
457 } else {
458 curEnd = curRegion->range.base + curRegion->range.size;
459 }
460 RB_MID_SCAN_END(regionRbTree, pstRbNode)
461 } else {//红黑树扫描排序,从小到大
462 /* rbtree scan is sorted, from small to big */
463 RB_SCAN_SAFE(regionRbTree, pstRbNode, pstRbNodeTmp)
464 curRegion = (LosVmMapRegion *)pstRbNode;
465 nextStart = curRegion->range.base;
466 if (nextStart < curEnd) {
467 continue;
468 }
469 if ((nextStart - curEnd) >= len) {
470 return curEnd;
471 } else {
472 curEnd = curRegion->range.base + curRegion->range.size;
473 }
474 RB_SCAN_SAFE_END(regionRbTree, pstRbNode, pstRbNodeTmp)
475 }
476
477 nextStart = vmSpace->mapBase + vmSpace->mapSize;
478 if ((nextStart >= curEnd) && ((nextStart - curEnd) >= len)) {
479 return curEnd;
480 }
481
482 return 0;
483}
484/// 分配指定开始地址和长度的线性区
485VADDR_T OsAllocSpecificRange(LosVmSpace *vmSpace, VADDR_T vaddr, size_t len, UINT32 regionFlags)
486{
487 STATUS_T status;
488
489 if (LOS_IsRangeInSpace(vmSpace, vaddr, len) == FALSE) {//虚拟地址是否在进程空间范围内
490 return 0;
491 }
492
493 if ((LOS_RegionFind(vmSpace, vaddr) != NULL) ||
494 (LOS_RegionFind(vmSpace, vaddr + len - 1) != NULL) ||
495 (LOS_RegionRangeFind(vmSpace, vaddr, len - 1) != NULL)) {//没找到的情况
496 if ((regionFlags & VM_MAP_REGION_FLAG_FIXED_NOREPLACE) != 0) {
497 return 0;
498 } else if ((regionFlags & VM_MAP_REGION_FLAG_FIXED) != 0) {//线性区未填满,则解除这部分空间的映射
499 status = LOS_UnMMap(vaddr, len);//解除映射
500 if (status != LOS_OK) {
501 VM_ERR("unmap specific range va: %#x, len: %#x failed, status: %d", vaddr, len, status);
502 return 0;
503 }
504 } else {
505 return OsAllocRange(vmSpace, len);//默认分配一个
506 }
507 }
508
509 return vaddr;
510}
511///映射类型为文件的线性区是否有效
513{
514 if ((region != NULL) && (LOS_IsRegionTypeFile(region)) &&
515 (region->unTypeData.rf.vnode != NULL)) {
516 return TRUE;
517 }
518 return FALSE;
519}
520///向红黑树中插入线性区
522{
523 if (LOS_RbAddNode(regionRbTree, (LosRbNode *)region) == FALSE) {
524 VM_ERR("insert region failed, base: %#x, size: %#x", region->range.base, region->range.size);
525 OsDumpAspace(region->space);
526 return FALSE;
527 }
528 return TRUE;
529}
530///创建一个线性区
531LosVmMapRegion *OsCreateRegion(VADDR_T vaddr, size_t len, UINT32 regionFlags, unsigned long offset)
532{
533 LosVmMapRegion *region = LOS_MemAlloc(m_aucSysMem0, sizeof(LosVmMapRegion));//只是分配一个线性区结构体
534 if (region == NULL) {
535 VM_ERR("memory allocate for LosVmMapRegion failed");
536 return region;
537 }
538 //创建线性区的本质就是在画饼,见如下操作:
539 (void)memset_s(region, sizeof(LosVmMapRegion), 0, sizeof(LosVmMapRegion));
540 region->range.base = vaddr; //虚拟地址作为线性区的基地址
541 region->range.size = len; //线性区大小,这是线性区构思最巧妙的地方,只要不过分,蓝图随便画。
542 region->pgOff = offset; //页标
543 region->regionFlags = regionFlags;//标识,可读/可写/可执行
544 region->regionType = VM_MAP_REGION_TYPE_NONE;//未映射
545 region->forkFlags = 0; //
546 region->shmid = -1; //默认线性区为不共享,无共享资源ID
547 return region;
548}
549///通过虚拟地址查询映射的物理地址
551{
552 PADDR_T paddr = 0;
553 STATUS_T status;
554 LosVmSpace *space = NULL;
555 LosArchMmu *archMmu = NULL;
556 //先取出对应空间的mmu
557 if (LOS_IsKernelAddress((VADDR_T)(UINTPTR)vaddr)) {//是否内核空间地址
558 archMmu = &g_kVmSpace.archMmu;
559 } else if (LOS_IsUserAddress((VADDR_T)(UINTPTR)vaddr)) {//是否为用户空间地址
560 space = OsCurrProcessGet()->vmSpace;
561 archMmu = &space->archMmu;
562 } else if (LOS_IsVmallocAddress((VADDR_T)(UINTPTR)vaddr)) {//是否为分配空间地址,堆区地址
563 archMmu = &g_vMallocSpace.archMmu;
564 } else {
565 VM_ERR("vaddr is beyond range");
566 return 0;
567 }
568
569 status = LOS_ArchMmuQuery(archMmu, (VADDR_T)(UINTPTR)vaddr, &paddr, 0);//查询物理地址
570 if (status == LOS_OK) {
571 return paddr;
572 } else {
573 return 0;
574 }
575}
576
577/*!
578 * 这里不是真的分配物理内存,而是逻辑上画一个连续的区域,标记这个区域可以拿用,表示内存已经归你了。
579 但真正的物理内存的占用会延迟到使用的时候才由缺页中断调入内存
580*/
581LosVmMapRegion *LOS_RegionAlloc(LosVmSpace *vmSpace, VADDR_T vaddr, size_t len, UINT32 regionFlags, VM_OFFSET_T pgoff)
582{
583 VADDR_T rstVaddr;
584 LosVmMapRegion *newRegion = NULL;
585 BOOL isInsertSucceed = FALSE;
586 /**
587 * If addr is NULL, then the kernel chooses the address at which to create the mapping;
588 * this is the most portable method of creating a new mapping. If addr is not NULL,
589 * then the kernel takes it as where to place the mapping;
590 */
591 (VOID)LOS_MuxAcquire(&vmSpace->regionMux);//获得互斥锁
592 if (vaddr == 0) {//如果地址是0,根据线性区管理的实际情况,自动创建虚拟地址, 这是创建新映射的最便捷的方法。
593 rstVaddr = OsAllocRange(vmSpace, len);
594 } else {
595 /* if it is already mmapped here, we unmmap it | 如果已经被映射了, 则解除映射关系*/
596 rstVaddr = OsAllocSpecificRange(vmSpace, vaddr, len, regionFlags);//创建包含指定虚拟地址的线性区, rstVaddr != vaddr || rstVaddr == vaddr
597 if (rstVaddr == 0) {
598 VM_ERR("alloc specific range va: %#x, len: %#x failed", vaddr, len);
599 goto OUT;
600 }
601 }
602 if (rstVaddr == 0) {//没有可供映射的虚拟地址
603 goto OUT;
604 }
605
606 newRegion = OsCreateRegion(rstVaddr, len, regionFlags, pgoff);//创建一个线性区,指定线性区的开始地址rstVaddr ...
607 if (newRegion == NULL) {
608 goto OUT;
609 }
610 newRegion->space = vmSpace;
611 isInsertSucceed = OsInsertRegion(&vmSpace->regionRbTree, newRegion);//插入红黑树和双循环链表中管理
612 if (isInsertSucceed == FALSE) {//插入失败
613 (VOID)LOS_MemFree(m_aucSysMem0, newRegion);//从内存池中释放
614 newRegion = NULL;
615 }
616
617OUT:
618 (VOID)LOS_MuxRelease(&vmSpace->regionMux);//释放互斥锁
619 return newRegion;
620}
621/*!
622 * 删除匿名页,匿名页就是内存映射页
623 * 1.解除映射关系 2.释放物理内存
624*/
625STATIC VOID OsAnonPagesRemove(LosArchMmu *archMmu, VADDR_T vaddr, UINT32 count)
626{
627 status_t status;
628 paddr_t paddr;
629 LosVmPage *page = NULL;
630
631 if ((archMmu == NULL) || (vaddr == 0) || (count == 0)) {
632 VM_ERR("OsAnonPagesRemove invalid args, archMmu %p, vaddr %p, count %d", archMmu, vaddr, count);
633 return;
634 }
635
636 while (count > 0) {//一页页操作
637 count--;
638 status = LOS_ArchMmuQuery(archMmu, vaddr, &paddr, NULL);//通过虚拟地址拿到物理地址
639 if (status != LOS_OK) {//失败,拿下一页的物理地址
640 vaddr += PAGE_SIZE;
641 continue;
642 }
643
644 LOS_ArchMmuUnmap(archMmu, vaddr, 1);//解除一页的映射
645
646 page = LOS_VmPageGet(paddr);//通过物理地址获取所在物理页框的起始地址
647 if (page != NULL) {//获取成功
648 if (!OsIsPageShared(page)) {//不是共享页,共享页会有专门的共享标签,共享本质是有无多个进程对该页的引用
649 LOS_PhysPageFree(page);//释放物理页框
650 }
651 }
652 vaddr += PAGE_SIZE;
653 }
654}
655
656STATIC VOID OsDevPagesRemove(LosArchMmu *archMmu, VADDR_T vaddr, UINT32 count)
657{
658 status_t status;
659
660 if ((archMmu == NULL) || (vaddr == 0) || (count == 0)) {
661 VM_ERR("OsDevPagesRemove invalid args, archMmu %p, vaddr %p, count %d", archMmu, vaddr, count);
662 return;
663 }
664
665 status = LOS_ArchMmuQuery(archMmu, vaddr, NULL, NULL);
666 if (status != LOS_OK) {
667 return;
668 }
669
670 /* in order to unmap section */
671 LOS_ArchMmuUnmap(archMmu, vaddr, count);
672}
673
674#ifdef LOSCFG_FS_VFS
675STATIC VOID OsFilePagesRemove(LosVmSpace *space, LosVmMapRegion *region)
676{
677 VM_OFFSET_T offset;
678 size_t size;
679
680 if ((space == NULL) || (region == NULL) || (region->unTypeData.rf.vmFOps == NULL)) {
681 return;
682 }
683
684 offset = region->pgOff;
685 size = region->range.size;
686 while (size >= PAGE_SIZE) {
687 region->unTypeData.rf.vmFOps->remove(region, &space->archMmu, offset);
688 offset++;
689 size -= PAGE_SIZE;
690 }
691}
692#endif
693/// 释放进程空间指定线性区
695{
696 if ((space == NULL) || (region == NULL)) {
697 VM_ERR("args error, aspace %p, region %p", space, region);
698 return LOS_ERRNO_VM_INVALID_ARGS;
699 }
700
701 (VOID)LOS_MuxAcquire(&space->regionMux);
702
703#ifdef LOSCFG_FS_VFS //文件开关
704 if (LOS_IsRegionFileValid(region)) {//是否为文件线性区
705 OsFilePagesRemove(space, region);//删除文件页
706 VnodeHold();
707 region->unTypeData.rf.vnode->useCount--;
708 VnodeDrop();
709 } else
710#endif
711#ifdef LOSCFG_KERNEL_SHM //共享内存开关
712 if (OsIsShmRegion(region)) { //是否为共享内存线性区
713 OsShmRegionFree(space, region);//释放共享线性区
714 } else if (LOS_IsRegionTypeDev(region)) {
715#else
716 if (LOS_IsRegionTypeDev(region)) {//如果是设备线性区
717#endif
718 OsDevPagesRemove(&space->archMmu, region->range.base, region->range.size >> PAGE_SHIFT);//删除映射设备
719 } else {
720 OsAnonPagesRemove(&space->archMmu, region->range.base, region->range.size >> PAGE_SHIFT);//删除匿名映射
721 }
722
723 /* remove it from space */
724 LOS_RbDelNode(&space->regionRbTree, &region->rbNode);//从红黑树中删除线性区
725 /* free it */
726 LOS_MemFree(m_aucSysMem0, region);//释放线性区结构体占用的内存
727 (VOID)LOS_MuxRelease(&space->regionMux);
728 return LOS_OK;
729}
730/// 复制线性区
731LosVmMapRegion *OsVmRegionDup(LosVmSpace *space, LosVmMapRegion *oldRegion, VADDR_T vaddr, size_t size)
732{
733 LosVmMapRegion *newRegion = NULL;
734 UINT32 regionFlags;
735
736 (VOID)LOS_MuxAcquire(&space->regionMux);
737 regionFlags = oldRegion->regionFlags;
738 if (vaddr == 0) {//不指定地址
739 regionFlags &= ~(VM_MAP_REGION_FLAG_FIXED | VM_MAP_REGION_FLAG_FIXED_NOREPLACE); //撕掉两个标签
740 } else {
741 regionFlags |= VM_MAP_REGION_FLAG_FIXED; //贴上填满线性区标签
742 }
743 newRegion = LOS_RegionAlloc(space, vaddr, size, regionFlags, oldRegion->pgOff); //分配一个线性区
744 if (newRegion == NULL) {
745 VM_ERR("LOS_RegionAlloc failed");
746 goto REGIONDUPOUT;
747 }
748 newRegion->regionType = oldRegion->regionType;//复制线性区类型(文件,设备,匿名)
749
750#ifdef LOSCFG_KERNEL_SHM
751 if (OsIsShmRegion(oldRegion)) {//如果是共享内存
752 newRegion->shmid = oldRegion->shmid;//复制共享ID
753 }
754#endif
755
756#ifdef LOSCFG_FS_VFS //文件开关
757 if (LOS_IsRegionTypeFile(oldRegion)) {//如果是文件线性区
758 newRegion->unTypeData.rf.vmFOps = oldRegion->unTypeData.rf.vmFOps; //文件操作接口
759 newRegion->unTypeData.rf.vnode = oldRegion->unTypeData.rf.vnode; //文件索引节点
760 newRegion->unTypeData.rf.f_oflags = oldRegion->unTypeData.rf.f_oflags;//读写标签
761 VnodeHold();
762 newRegion->unTypeData.rf.vnode->useCount++;//索引节点使用数增加
763 VnodeDrop();
764 }
765#endif
766
767REGIONDUPOUT:
768 (VOID)LOS_MuxRelease(&space->regionMux);
769 return newRegion;
770}
771/// 劈开线性区
772STATIC LosVmMapRegion *OsVmRegionSplit(LosVmMapRegion *oldRegion, VADDR_T newRegionStart)
773{
774 LosVmMapRegion *newRegion = NULL;
775 LosVmSpace *space = oldRegion->space;
776 size_t size = LOS_RegionSize(newRegionStart, LOS_RegionEndAddr(oldRegion));//获取线性区大小
777
778 oldRegion->range.size = LOS_RegionSize(oldRegion->range.base, newRegionStart - 1);//获取旧线性区大小
779 if (oldRegion->range.size == 0) {
780 LOS_RbDelNode(&space->regionRbTree, &oldRegion->rbNode);
781 }
782
783 newRegion = OsVmRegionDup(oldRegion->space, oldRegion, newRegionStart, size);
784 if (newRegion == NULL) {
785 VM_ERR("OsVmRegionDup fail");
786 return NULL;
787 }
788#ifdef LOSCFG_FS_VFS
789 newRegion->pgOff = oldRegion->pgOff + ((newRegionStart - oldRegion->range.base) >> PAGE_SHIFT);
790#endif
791 return newRegion;
792}
793///对线性区进行调整
794STATUS_T OsVmRegionAdjust(LosVmSpace *space, VADDR_T newRegionStart, size_t size)
795{
796 LosVmMapRegion *region = NULL;
797 VADDR_T nextRegionBase = newRegionStart + size;
798 LosVmMapRegion *newRegion = NULL;
799
800 region = LOS_RegionFind(space, newRegionStart);//先找到线性区
801 if ((region != NULL) && (newRegionStart > region->range.base)) {
802 newRegion = OsVmRegionSplit(region, newRegionStart);
803 if (newRegion == NULL) {
804 VM_ERR("region split fail");
805 return LOS_ERRNO_VM_NO_MEMORY;
806 }
807 }
808
809 region = LOS_RegionFind(space, nextRegionBase - 1);
810 if ((region != NULL) && (nextRegionBase < LOS_RegionEndAddr(region))) {
811 newRegion = OsVmRegionSplit(region, nextRegionBase);
812 if (newRegion == NULL) {
813 VM_ERR("region split fail");
814 return LOS_ERRNO_VM_NO_MEMORY;
815 }
816 }
817
818 return LOS_OK;
819}
820///删除线性区
821STATUS_T OsRegionsRemove(LosVmSpace *space, VADDR_T regionBase, size_t size)
822{
823 STATUS_T status;
824 VADDR_T regionEnd = regionBase + size - 1;
825 LosVmMapRegion *regionTemp = NULL;
826 LosRbNode *pstRbNodeTemp = NULL;
827 LosRbNode *pstRbNodeNext = NULL;
828
829 (VOID)LOS_MuxAcquire(&space->regionMux);
830
831 status = OsVmRegionAdjust(space, regionBase, size);//线性区调整
832 if (status != LOS_OK) {
833 goto ERR_REGION_SPLIT;
834 }
835
836 RB_SCAN_SAFE(&space->regionRbTree, pstRbNodeTemp, pstRbNodeNext)//扫描虚拟空间内的线性区
837 regionTemp = (LosVmMapRegion *)pstRbNodeTemp;
838 if (regionTemp->range.base > regionEnd) {
839 break;
840 }
841 if (regionBase <= regionTemp->range.base && regionEnd >= LOS_RegionEndAddr(regionTemp)) {
842 status = LOS_RegionFree(space, regionTemp);
843 if (status != LOS_OK) {
844 VM_ERR("fail to free region, status=%d", status);
845 goto ERR_REGION_SPLIT;
846 }
847 }
848
849 RB_SCAN_SAFE_END(&space->regionRbTree, pstRbNodeTemp, pstRbNodeNext)
850
851ERR_REGION_SPLIT:
852 (VOID)LOS_MuxRelease(&space->regionMux);
853 return status;
854}
855///根据指定参数范围[addr,addr+len] 释放用户空间中堆区所占用的物理内存
856INT32 OsUserHeapFree(LosVmSpace *vmSpace, VADDR_T addr, size_t len)
857{
858 LosVmMapRegion *vmRegion = NULL;
859 LosVmPage *vmPage = NULL;
860 PADDR_T paddr = 0;
861 VADDR_T vaddr;
862 STATUS_T ret;
863
864 if (vmSpace == LOS_GetKVmSpace() || vmSpace->heap == NULL) {//虚拟空间堆区必须在非内核空间
865 return -1;
866 }
867
868 vmRegion = LOS_RegionFind(vmSpace, addr);//通过参数虚拟地址红黑树找到线性区,线性区范围内包含了参数虚拟地址
869 if (vmRegion == NULL) {
870 return -1;
871 }
872
873 if (vmRegion == vmSpace->heap) {//地址所在的线性区为堆区
874 vaddr = addr;
875 while (len > 0) {//参数0 代表不获取 flags 信息
876 if (LOS_ArchMmuQuery(&vmSpace->archMmu, vaddr, &paddr, 0) == LOS_OK) {//通过虚拟地址查到物理地址
877 ret = LOS_ArchMmuUnmap(&vmSpace->archMmu, vaddr, 1);//解除映射关系以页为单位,这里解除1页
878 if (ret <= 0) {
879 VM_ERR("unmap failed, ret = %d", ret);
880 }
881 vmPage = LOS_VmPageGet(paddr);//获取物理页面信息
882 LOS_PhysPageFree(vmPage);//释放页
883 }
884 vaddr += PAGE_SIZE;
885 len -= PAGE_SIZE;
886 }
887 return 0;
888 }
889
890 return -1;
891}
892///线性区是否支持扩展
893STATUS_T OsIsRegionCanExpand(LosVmSpace *space, LosVmMapRegion *region, size_t size)
894{
895 LosVmMapRegion *nextRegion = NULL;
896
897 if ((space == NULL) || (region == NULL)) {
898 return LOS_NOK;
899 }
900
901 nextRegion = (LosVmMapRegion *)LOS_RbSuccessorNode(&space->regionRbTree, &region->rbNode);
902 /* if the gap is larger than size, then we can expand */
903 if ((nextRegion != NULL) && ((nextRegion->range.base - region->range.base) >= size)) {
904 return LOS_OK;
905 }
906
907 return LOS_NOK;
908}
909///解除一定范围的虚拟地址的映射关系
910STATUS_T OsUnMMap(LosVmSpace *space, VADDR_T addr, size_t size)
911{
912 size = LOS_Align(size, PAGE_SIZE);
913 addr = LOS_Align(addr, PAGE_SIZE);
914 (VOID)LOS_MuxAcquire(&space->regionMux);
915 STATUS_T status = OsRegionsRemove(space, addr, size);//删除线性区
916 if (status != LOS_OK) {
917 status = -EINVAL;
918 VM_ERR("region_split failed");
919 goto ERR_REGION_SPLIT;
920 }
921
922ERR_REGION_SPLIT:
923 (VOID)LOS_MuxRelease(&space->regionMux);
924 return status;
925}
926/// 释放所有线性区
927STATIC VOID OsVmSpaceAllRegionFree(LosVmSpace *space)
928{
929 LosRbNode *pstRbNode = NULL;
930 LosRbNode *pstRbNodeNext = NULL;
931
932 /* free all of the regions */
933 RB_SCAN_SAFE(&space->regionRbTree, pstRbNode, pstRbNodeNext) //遍历红黑树
934 LosVmMapRegion *region = (LosVmMapRegion *)pstRbNode;//拿到线性区
935 if (region->range.size == 0) {
936 VM_ERR("space free, region: %#x flags: %#x, base:%#x, size: %#x",
937 region, region->regionFlags, region->range.base, region->range.size);
938 }
939 STATUS_T ret = LOS_RegionFree(space, region);//释放线性区
940 if (ret != LOS_OK) {
941 VM_ERR("free region error, space %p, region %p", space, region);
942 }
943 RB_SCAN_SAFE_END(&space->regionRbTree, pstRbNode, pstRbNodeNext)//要好好研究下这几个宏,有点意思
944
945 return;
946}
947/// 释放虚拟空间
949{
950 if (space == NULL) {
951 return LOS_ERRNO_VM_INVALID_ARGS;
952 }
953
954 if (space == &g_kVmSpace) {
955 VM_ERR("try to free kernel aspace, not allowed");
956 return LOS_OK;
957 }
958
959 (VOID)LOS_MuxAcquire(&space->regionMux);
960 OsVmSpaceAllRegionFree(space);
961 (VOID)LOS_MuxRelease(&space->regionMux);
962
963 return LOS_OK;
964}
965///释放虚拟空间,注意内核空间不能被释放掉,永驻内存
967{
968 if (space == NULL) {
969 return LOS_ERRNO_VM_INVALID_ARGS;
970 }
971
972 if (space == &g_kVmSpace) {//不能释放内核虚拟空间,内核空间常驻内存
973 VM_ERR("try to free kernel aspace, not allowed");
974 return LOS_OK;
975 }
976
977 /* pop it out of the global aspace list */
978 (VOID)LOS_MuxAcquire(&space->regionMux);
979 LOS_ListDelete(&space->node);//从g_vmSpaceList链表里删除,g_vmSpaceList记录了所有空间节点。
980
981 OsVmSpaceAllRegionFree(space);
982
983 /* make sure the current thread does not map the aspace */
984 LosProcessCB *currentProcess = OsCurrProcessGet();
985 if (currentProcess->vmSpace == space) {
986 LOS_TaskLock();
987 currentProcess->vmSpace = NULL;
990 }
991
992 /* destroy the arch portion of the space */
994
995 (VOID)LOS_MuxRelease(&space->regionMux);
996 (VOID)LOS_MuxDestroy(&space->regionMux);
997
998 /* free the aspace */
1000 return LOS_OK;
1001}
1002///虚拟地址和size是否在空间
1003BOOL LOS_IsRangeInSpace(const LosVmSpace *space, VADDR_T vaddr, size_t size)
1004{
1005 /* is the starting address within the address space */
1006 if (vaddr < space->base || vaddr > space->base + space->size - 1) {
1007 return FALSE;
1008 }
1009 if (size == 0) {
1010 return TRUE;
1011 }
1012 /* see if the size is enough to wrap the integer */
1013 if (vaddr + size - 1 < vaddr) {
1014 return FALSE;
1015 }
1016 /* see if the end address is within the address space's */
1017 if (vaddr + size - 1 > space->base + space->size - 1) {
1018 return FALSE;
1019 }
1020 return TRUE;
1021}
1022/// 在进程空间中预留一块内存空间
1023STATUS_T LOS_VmSpaceReserve(LosVmSpace *space, size_t size, VADDR_T vaddr)
1024{
1025 UINT32 regionFlags = 0;
1026
1027 if ((space == NULL) || (size == 0) || (!IS_PAGE_ALIGNED(vaddr) || !IS_PAGE_ALIGNED(size))) {
1028 return LOS_ERRNO_VM_INVALID_ARGS;
1029 }
1030
1031 if (!LOS_IsRangeInSpace(space, vaddr, size)) {
1032 return LOS_ERRNO_VM_OUT_OF_RANGE;
1033 }
1034
1035 /* lookup how it's already mapped */
1036 (VOID)LOS_ArchMmuQuery(&space->archMmu, vaddr, NULL, &regionFlags);
1037
1038 /* build a new region structure */
1039 LosVmMapRegion *region = LOS_RegionAlloc(space, vaddr, size, regionFlags | VM_MAP_REGION_FLAG_FIXED, 0);
1040
1041 return region ? LOS_OK : LOS_ERRNO_VM_NO_MEMORY;
1042}
1043///实现从虚拟地址到物理地址的映射,将指定长度的物理地址区间与虚拟地址区间做映射,需提前申请物理地址区间
1044STATUS_T LOS_VaddrToPaddrMmap(LosVmSpace *space, VADDR_T vaddr, PADDR_T paddr, size_t len, UINT32 flags)
1045{
1046 STATUS_T ret;
1047 LosVmMapRegion *region = NULL;
1048 LosVmPage *vmPage = NULL;
1049
1050 if ((vaddr != ROUNDUP(vaddr, PAGE_SIZE)) ||
1051 (paddr != ROUNDUP(paddr, PAGE_SIZE)) ||
1052 (len != ROUNDUP(len, PAGE_SIZE))) {
1053 VM_ERR("vaddr :0x%x paddr:0x%x len: 0x%x not page size align", vaddr, paddr, len);
1054 return LOS_ERRNO_VM_NOT_VALID;
1055 }
1056
1057 if (space == NULL) {
1058 space = OsCurrProcessGet()->vmSpace;//获取当前进程的空间
1059 }
1060
1061 region = LOS_RegionFind(space, vaddr);//通过虚拟地址查找线性区
1062 if (region != NULL) {//已经被映射过了,失败返回
1063 VM_ERR("vaddr : 0x%x already used!", vaddr);
1064 return LOS_ERRNO_VM_BUSY;
1065 }
1066
1067 region = LOS_RegionAlloc(space, vaddr, len, flags, 0);//通过虚拟地址 创建一个region
1068 if (region == NULL) {
1069 VM_ERR("failed");
1070 return LOS_ERRNO_VM_NO_MEMORY;//内存不够
1071 }
1072
1073 while (len > 0) {
1074 vmPage = LOS_VmPageGet(paddr);
1075 if (vmPage == NULL) {
1076 LOS_RegionFree(space, region);
1077 VM_ERR("Page is NULL");
1078 return LOS_ERRNO_VM_NOT_VALID;
1079 }
1080 LOS_AtomicInc(&vmPage->refCounts);//ref自增
1081
1082 ret = LOS_ArchMmuMap(&space->archMmu, vaddr, paddr, 1, region->regionFlags);//mmu map
1083 if (ret <= 0) {
1084 VM_ERR("LOS_ArchMmuMap failed: %d", ret);
1085 LOS_RegionFree(space, region);
1086 return ret;
1087 }
1088
1089 paddr += PAGE_SIZE;
1090 vaddr += PAGE_SIZE;
1091 len -= PAGE_SIZE;
1092 }
1093 return LOS_OK;
1094}
1095
1096//对外接口|申请内核堆空间内存
1097VOID *LOS_VMalloc(size_t size)
1098{
1099 LosVmSpace *space = &g_vMallocSpace;//从内核动态空间申请
1100 LosVmMapRegion *region = NULL;
1101 size_t sizeCount;
1102 size_t count;
1103 LosVmPage *vmPage = NULL;
1104 VADDR_T va;
1105 PADDR_T pa;
1106 STATUS_T ret;
1107
1108 size = LOS_Align(size, PAGE_SIZE);//
1109 if ((size == 0) || (size > space->size)) {
1110 return NULL;
1111 }
1112 sizeCount = size >> PAGE_SHIFT;//按页申请所以需右移12位
1113
1114 LOS_DL_LIST_HEAD(pageList);
1115 (VOID)LOS_MuxAcquire(&space->regionMux);//获得互斥锁
1116
1117 count = LOS_PhysPagesAlloc(sizeCount, &pageList);//一页一页申请,并从pageList尾部插入
1118 if (count < sizeCount) {
1119 VM_ERR("failed to allocate enough pages (ask %zu, got %zu)", sizeCount, count);
1120 goto ERROR;
1121 }
1122
1123 /* allocate a region and put it in the aspace list *///分配一个可读写的线性区,并挂在space
1124 region = LOS_RegionAlloc(space, 0, size, VM_MAP_REGION_FLAG_PERM_READ | VM_MAP_REGION_FLAG_PERM_WRITE, 0);//注意第二个参数是 vaddr = 0 !!!
1125 if (region == NULL) {
1126 VM_ERR("alloc region failed, size = %x", size);
1127 goto ERROR;
1128 }
1129
1130 va = region->range.base;//va 该区范围基地址为虚拟地址的开始位置,理解va怎么来的是理解线性地址的关键!
1131 while ((vmPage = LOS_ListRemoveHeadType(&pageList, LosVmPage, node))) {//从pageList循环拿page
1132 pa = vmPage->physAddr;//获取page物理地址,因上面是通过LOS_PhysPagesAlloc分配
1133 LOS_AtomicInc(&vmPage->refCounts);//refCounts 自增
1134 ret = LOS_ArchMmuMap(&space->archMmu, va, pa, 1, region->regionFlags);//一页一页的map
1135 if (ret != 1) {
1136 VM_ERR("LOS_ArchMmuMap failed!, err;%d", ret);
1137 }
1138 va += PAGE_SIZE;//一页映射完成,进入下一页
1139 }//va 注意 region的虚拟地址页是连续的,但物理页可以不连续! 很重要!!!
1140
1141 (VOID)LOS_MuxRelease(&space->regionMux);//释放互斥锁
1142 return (VOID *)(UINTPTR)region->range.base;//返回虚拟基地址供应用使用
1143
1144ERROR:
1145 (VOID)LOS_PhysPagesFree(&pageList);//释放物理内存页
1146 (VOID)LOS_MuxRelease(&space->regionMux);//释放互斥锁
1147 return NULL;
1148}
1149///对外接口|释放内核堆空间内存
1150VOID LOS_VFree(const VOID *addr)
1151{
1152 LosVmSpace *space = &g_vMallocSpace;
1153 LosVmMapRegion *region = NULL;
1154 STATUS_T ret;
1155
1156 if (addr == NULL) {
1157 VM_ERR("addr is NULL!");
1158 return;
1159 }
1160
1161 (VOID)LOS_MuxAcquire(&space->regionMux);
1162
1163 region = LOS_RegionFind(space, (VADDR_T)(UINTPTR)addr);//先找到线性区
1164 if (region == NULL) {
1165 VM_ERR("find region failed");
1166 goto DONE;
1167 }
1168
1169 ret = LOS_RegionFree(space, region);//释放线性区
1170 if (ret) {
1171 VM_ERR("free region failed, ret = %d", ret);
1172 }
1173
1174DONE:
1175 (VOID)LOS_MuxRelease(&space->regionMux);
1176}
1177
1179{
1180 return &g_vmSpaceListMux;
1181}
1182STATIC INLINE BOOL OsMemLargeAlloc(UINT32 size)//是不是分配浪费大于1K的内存
1183{
1184 if (g_kHeapInited == FALSE) {
1185 return FALSE;
1186 }
1187
1188 if (size < KMALLOC_LARGE_SIZE) {
1189 return FALSE;
1190 }
1191
1192 return TRUE;
1193}
1194#else
1195PADDR_T LOS_PaddrQuery(VOID *vaddr)
1196{
1197 if (!LOS_IsKernelAddress((VADDR_T)vaddr)) {
1198 return 0;
1199 }
1200
1201 return (PADDR_T)VMM_TO_DMA_ADDR((VADDR_T)vaddr);
1202}
1203#endif
1204///内核空间内存分配,申请小于16KiB的内存则通过堆内存池获取,否则申请多个连续物理页
1205VOID *LOS_KernelMalloc(UINT32 size)
1206{
1207 VOID *ptr = NULL;
1208 //从本函数可知,内核空间的分配有两种方式
1209#ifdef LOSCFG_KERNEL_VM
1210 if (OsMemLargeAlloc(size)) {//是不是分配浪费小于1K的内存
1211 ptr = LOS_PhysPagesAllocContiguous(ROUNDUP(size, PAGE_SIZE) >> PAGE_SHIFT);//分配连续的物理内存页
1212 } else
1213#endif
1214 {
1215 ptr = LOS_MemAlloc(OS_SYS_MEM_ADDR, size);//从内存池分配
1216 }
1217
1218 return ptr;
1219}
1220/// 申请具有对齐属性的内存,申请规则:申请小于16KiB的内存则通过堆内存池获取,否则申请多个连续物理页
1221VOID *LOS_KernelMallocAlign(UINT32 size, UINT32 boundary)
1222{
1223 VOID *ptr = NULL;
1224
1225#ifdef LOSCFG_KERNEL_VM
1226 if (OsMemLargeAlloc(size) && IS_ALIGNED(PAGE_SIZE, boundary)) {
1227 ptr = LOS_PhysPagesAllocContiguous(ROUNDUP(size, PAGE_SIZE) >> PAGE_SHIFT);
1228 } else
1229#endif
1230 {
1231 ptr = LOS_MemAllocAlign(OS_SYS_MEM_ADDR, size, boundary);
1232 }
1233
1234 return ptr;
1235}
1236/// 重新分配内核内存空间
1237VOID *LOS_KernelRealloc(VOID *ptr, UINT32 size)
1238{
1239 VOID *tmpPtr = NULL;
1240
1241#ifdef LOSCFG_KERNEL_VM
1242 LosVmPage *page = NULL;
1243 errno_t ret;
1244
1245 if (ptr == NULL) {
1246 tmpPtr = LOS_KernelMalloc(size);
1247 } else {
1248 if (OsMemIsHeapNode(ptr) == FALSE) {
1249 page = OsVmVaddrToPage(ptr);
1250 if (page == NULL) {
1251 VM_ERR("page of ptr(%#x) is null", ptr);
1252 return NULL;
1253 }
1254 tmpPtr = LOS_KernelMalloc(size);
1255 if (tmpPtr == NULL) {
1256 VM_ERR("alloc memory failed");
1257 return NULL;
1258 }
1259 ret = memcpy_s(tmpPtr, size, ptr, page->nPages << PAGE_SHIFT);
1260 if (ret != EOK) {
1261 LOS_KernelFree(tmpPtr);
1262 VM_ERR("KernelRealloc memcpy error");
1263 return NULL;
1264 }
1265 OsMemLargeNodeFree(ptr);
1266 } else {
1267 tmpPtr = LOS_MemRealloc(OS_SYS_MEM_ADDR, ptr, size);
1268 }
1269 }
1270#else
1271 tmpPtr = LOS_MemRealloc(OS_SYS_MEM_ADDR, ptr, size);
1272#endif
1273
1274 return tmpPtr;
1275}
1276
1277VOID LOS_KernelFree(VOID *ptr)
1278{
1279#ifdef LOSCFG_KERNEL_VM
1280 UINT32 ret;
1281 if (OsMemIsHeapNode(ptr) == FALSE) {//判断地址是否在堆区
1282 ret = OsMemLargeNodeFree(ptr);
1283 if (ret != LOS_OK) {
1284 VM_ERR("KernelFree %p failed", ptr);
1285 return;
1286 }
1287 } else
1288#endif
1289 {
1290 (VOID)LOS_MemFree(OS_SYS_MEM_ADDR, ptr);//从内存池中释放
1291 }
1292}
1293
1294
STATIC INLINE VOID LOS_AtomicInc(Atomic *v)
Atomic addSelf.
Definition: los_atomic.h:253
LITE_OS_SEC_TEXT UINTPTR LOS_Align(UINTPTR addr, UINT32 boundary)
Align the value (addr) by some bytes (boundary) you specify.
Definition: los_misc.c:35
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
VOID * LOS_MemRealloc(VOID *pool, VOID *ptr, UINT32 size)
按size大小重新分配内存块,并将原内存块内容拷贝到新内存块。如果新内存块申请成功,则释放原内存块
Definition: los_memory.c:1510
VOID * LOS_MemAllocAlign(VOID *pool, UINT32 size, UINT32 boundary)
从指定内存池中申请size长度的内存且地址按boundary字节对齐的内存
Definition: los_memory.c:1150
UINT32 LOS_MemFree(VOID *pool, VOID *ptr)
释放从指定动态内存中申请的内存
Definition: los_memory.c:1369
UINT8 * m_aucSysMem0
异常交互动态内存池地址的起始地址,当不支持异常交互特性时,m_aucSysMem0等于m_aucSysMem1。
Definition: los_memory.c:107
LITE_OS_SEC_TEXT UINT32 LOS_MuxInit(LosMux *mutex, const LosMuxAttr *attr)
初始化互斥锁
Definition: los_mux.c:262
LITE_OS_SEC_TEXT UINT32 LOS_MuxDestroy(LosMux *mutex)
销毁互斥锁
Definition: los_mux.c:289
LITE_OS_SEC_TEXT_MINOR VOID LOS_TaskUnlock(VOID)
Unlock the task scheduling.
Definition: los_task.c:1148
LITE_OS_SEC_TEXT_MINOR VOID LOS_TaskLock(VOID)
Lock the task scheduling.
Definition: los_task.c:1139
https://blog.csdn.net/qq_38410730/article/details/81036768
VOID LOS_ArchMmuContextSwitch(LosArchMmu *archMmu)
LOS_ArchMmuContextSwitch 切换MMU上下文
STATUS_T LOS_ArchMmuDestroy(LosArchMmu *archMmu)
LOS_ArchMmuDestroy 销毁MMU 和 initMmu 相呼应,释放页表页
STATUS_T LOS_ArchMmuMap(LosArchMmu *archMmu, VADDR_T vaddr, PADDR_T paddr, size_t count, UINT32 flags)
LOS_ArchMmuMap 映射进程空间虚拟地址区间与物理地址区间 所谓的map就是生成L1,L2页表项的过程
Definition: los_arch_mmu.c:891
VADDR_T * OsGFirstTableGet(VOID)
Definition: los_arch_mmu.c:186
BOOL OsArchMmuInit(LosArchMmu *archMmu, VADDR_T *virtTtb)
Definition: los_arch_mmu.c:537
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
BOOL OsMemIsHeapNode(const VOID *ptr)
Definition: los_memory.c:2126
UINT32 OsMemLargeNodeFree(const VOID *ptr)
大内存释放
Definition: los_memory.c:382
STATIC INLINE LosProcessCB * OsCurrProcessGet(VOID)
ULONG_T LOS_RbAddNode(LosRbTree *pstTree, LosRbNode *pstNew)
Definition: los_rbtree.c:705
VOID LOS_RbInitTree(LosRbTree *pstTree, pfRBCmpKeyFn pfCmpKey, pfRBFreeFn pfFree, pfRBGetKeyFn pfGetKey)
Definition: los_rbtree.c:524
VOID LOS_RbDelNode(LosRbTree *pstTree, LosRbNode *pstNode)
Definition: los_rbtree.c:700
VOID * LOS_RbSuccessorNode(LosRbTree *pstTree, VOID *pstData)
Definition: los_rbtree.c:588
ULONG_T LOS_RbGetNode(LosRbTree *pstTree, VOID *pKey, LosRbNode **ppstNode)
Definition: los_rbtree.c:656
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 PADDR_T
Definition: los_typedef.h:207
signed int INT32
Definition: los_typedef.h:60
int status_t
Definition: los_typedef.h:205
unsigned int ULONG_T
Definition: los_typedef.h:214
unsigned long paddr_t
Definition: los_typedef.h:209
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 long UINTPTR
Definition: los_typedef.h:68
unsigned int UINT32
Definition: los_typedef.h:57
size_t BOOL
Definition: los_typedef.h:88
BOOL g_kHeapInited
内核堆区初始化变量
Definition: los_vm_boot.c:46
VOID OsDumpAspace(LosVmSpace *space)
dump 指定虚拟空间的信息
Definition: los_vm_dump.c:396
VOID OsAddMapInfo(LosFilePage *page, LosArchMmu *archMmu, VADDR_T vaddr)
STATIC INLINE BOOL OsIsPageShared(LosVmPage *page)
是否为共享页
LosFilePage * OsFindGetEntry(struct page_mapping *mapping, VM_OFFSET_T pgoff)
STATIC INLINE STATUS_T LOS_MuxAcquire(LosMux *m)
Definition: los_vm_lock.h:48
STATIC INLINE STATUS_T LOS_MuxRelease(LosMux *m)
Definition: los_vm_lock.h:53
STATIC BOOL OsVmSpaceInitCommon(LosVmSpace *vmSpace, VADDR_T *virtTtb)
OsVmSpaceInitCommon 初始化进程虚拟空间,必须提供L1表的虚拟内存地址
Definition: los_vm_map.c:199
LosVmSpace * LOS_CurrSpaceGet(VOID)
获取当前进程空间结构体指针
Definition: los_vm_map.c:112
LosMux g_vmSpaceListMux
用于锁g_vmSpaceList的互斥量
Definition: los_vm_map.c:103
ULONG_T OsRegionRbCmpKeyFn(const VOID *pNodeKeyA, const VOID *pNodeKeyB)
比较两个红黑树节点
Definition: los_vm_map.c:161
STATIC VOID OsAnonPagesRemove(LosArchMmu *archMmu, VADDR_T vaddr, UINT32 count)
Definition: los_vm_map.c:625
PADDR_T LOS_PaddrQuery(VOID *vaddr)
通过虚拟地址查询映射的物理地址
Definition: los_vm_map.c:550
STATIC BOOL OsVmSpaceParamCheck(LosVmSpace *vmSpace)
Definition: los_vm_map.c:309
ULONG_T OsRegionRbFreeFn(LosRbNode *pstNode)
释放挂在红黑树上节点,等于释放了线性区
Definition: los_vm_map.c:149
LOS_DL_LIST_HEAD(g_vmSpaceList)
初始化全局虚拟空间节点,所有虚拟空间都挂到此节点上.
BOOL LOS_IsRegionFileValid(LosVmMapRegion *region)
映射类型为文件的线性区是否有效
Definition: los_vm_map.c:512
LosVmSpace * LOS_SpaceGet(VADDR_T vaddr)
获取虚拟地址对应的进程空间结构体指针
Definition: los_vm_map.c:117
LosVmSpace * OsCreateUserVmSpace(VOID)
创建用户进程空间
Definition: los_vm_map.c:281
STATIC VOID OsFilePagesRemove(LosVmSpace *space, LosVmMapRegion *region)
Definition: los_vm_map.c:675
LosVmSpace * LOS_GetVmallocSpace(VOID)
获取内核堆空间的全局变量
Definition: los_vm_map.c:140
BOOL OsKernVmSpaceInit(LosVmSpace *vmSpace, VADDR_T *virtTtb)
初始化内核虚拟空间
Definition: los_vm_map.c:224
LOS_DL_LIST * LOS_GetVmSpaceList(VOID)
获取进程空间链表指针 g_vmSpaceList中挂的是进程空间 g_kVmSpace, g_vMallocSpace,所有用户进程的空间(独有一个进程空间)
Definition: los_vm_map.c:135
LosVmMapRegion * OsCreateRegion(VADDR_T vaddr, size_t len, UINT32 regionFlags, unsigned long offset)
创建一个线性区
Definition: los_vm_map.c:531
STATIC VOID OsDevPagesRemove(LosArchMmu *archMmu, VADDR_T vaddr, UINT32 count)
Definition: los_vm_map.c:656
LosVmMapRegion * LOS_RegionFind(LosVmSpace *vmSpace, VADDR_T addr)
查找线性区 根据起始地址在进程空间内查找是否存在
Definition: los_vm_map.c:414
VADDR_T OsAllocRange(LosVmSpace *vmSpace, size_t len)
分配指定长度的线性区
Definition: los_vm_map.c:436
STATUS_T LOS_VmSpaceClone(LosVmSpace *oldVmSpace, LosVmSpace *newVmSpace)
Definition: los_vm_map.c:318
LosVmSpace * LOS_GetKVmSpace(VOID)
内核空间只有g_kVmSpace一个,所有的内核进程都共用一个内核空间
Definition: los_vm_map.c:130
BOOL OsVMallocSpaceInit(LosVmSpace *vmSpace, VADDR_T *virtTtb)
初始化内核堆空间
Definition: los_vm_map.c:237
VADDR_T OsAllocSpecificRange(LosVmSpace *vmSpace, VADDR_T vaddr, size_t len, UINT32 regionFlags)
分配指定开始地址和长度的线性区
Definition: los_vm_map.c:485
STATUS_T LOS_RegionFree(LosVmSpace *space, LosVmMapRegion *region)
释放进程空间指定线性区
Definition: los_vm_map.c:694
VOID OsVmMapInit(VOID)
@note_thinking 这个函数名称和内容不太搭
Definition: los_vm_map.c:216
LosVmSpace g_vMallocSpace
内核分配空间,用于内核分配内存
Definition: los_vm_map.c:106
VOID * OsRegionRbGetKeyFn(LosRbNode *pstNode)
通过红黑树节点找到对应的线性区
Definition: los_vm_map.c:155
LosVmMapRegion * LOS_RegionAlloc(LosVmSpace *vmSpace, VADDR_T vaddr, size_t len, UINT32 regionFlags, VM_OFFSET_T pgoff)
Definition: los_vm_map.c:581
BOOL OsUserVmSpaceInit(LosVmSpace *vmSpace, VADDR_T *virtTtb)
OsUserVmSpaceInit 用户空间的TTB表是动态申请得来,每个进程有属于自己的L1,L2表 初始化用户进程虚拟空间,主要划分数据区,堆区,映射区和创建mmu
Definition: los_vm_map.c:265
BOOL OsInsertRegion(LosRbTree *regionRbTree, LosVmMapRegion *region)
向红黑树中插入线性区
Definition: los_vm_map.c:521
LosVmMapRegion * LOS_RegionRangeFind(LosVmSpace *vmSpace, VADDR_T addr, size_t len)
查找线性区 根据地址区间在进程空间内查找是否存在
Definition: los_vm_map.c:425
LosVmSpace g_kVmSpace
内核非分配空间,用于内核运行栈,代码区,数据区
Definition: los_vm_map.c:105
VOID OsKSpaceInit(VOID)
内核虚拟空间初始化
Definition: los_vm_map.c:250
LosVmMapRegion * OsFindRegion(LosRbTree *regionRbTree, VADDR_T vaddr, size_t len)
通过虚拟(线性)地址查找所属线性区,红黑树
Definition: los_vm_map.c:400
VOID LOS_VFree(const VOID *addr)
STATIC INLINE BOOL OsIsVmRegionEmpty(LosVmSpace *vmSpace)
是否为一个空线性区
Definition: los_vm_map.h:293
STATIC INLINE BOOL LOS_IsKernelAddress(VADDR_T vaddr)
虚拟地址是否在内核空间
Definition: los_vm_map.h:213
STATIC INLINE size_t LOS_RegionSize(VADDR_T start, VADDR_T end)
获取线性区大小
Definition: los_vm_map.h:229
INT32 OsUserHeapFree(LosVmSpace *vmSpace, VADDR_T addr, size_t len)
STATIC INLINE BOOL LOS_IsRegionTypeDev(LosVmMapRegion *region)
是否为设备映射线性区 /dev/...
Definition: los_vm_map.h:255
STATIC INLINE BOOL LOS_IsVmallocAddress(VADDR_T vaddr)
Definition: los_vm_map.h:287
STATUS_T LOS_VmSpaceFree(LosVmSpace *space)
STATIC INLINE BOOL LOS_IsRegionTypeFile(LosVmMapRegion *region)
是否为文件映射区
Definition: los_vm_map.h:234
LosMux * OsGVmSpaceMuxGet(VOID)
STATUS_T OsRegionsRemove(LosVmSpace *space, VADDR_T vaddr, size_t size)
BOOL LOS_IsRangeInSpace(const LosVmSpace *space, VADDR_T vaddr, size_t size)
STATIC INLINE VADDR_T LOS_RegionEndAddr(LosVmMapRegion *region)
获取线性区的结束地址
Definition: los_vm_map.h:224
VOID * LOS_KernelMalloc(UINT32 size)
STATIC INLINE BOOL LOS_IsUserAddress(VADDR_T vaddr)
虚拟地址是否在用户空间
Definition: los_vm_map.h:275
LosVmMapRegion * OsVmRegionDup(LosVmSpace *space, LosVmMapRegion *oldRegion, VADDR_T vaddr, size_t size)
STATUS_T OsIsRegionCanExpand(LosVmSpace *space, LosVmMapRegion *region, size_t size)
STATUS_T LOS_VaddrToPaddrMmap(LosVmSpace *space, VADDR_T vaddr, PADDR_T paddr, size_t len, UINT32 flags)
struct VmMapRegion LosVmMapRegion
Definition: los_vm_map.h:89
VOID * LOS_KernelRealloc(VOID *ptr, UINT32 size)
STATUS_T OsVmSpaceRegionFree(LosVmSpace *space)
VOID LOS_KernelFree(VOID *ptr)
VOID * LOS_VMalloc(size_t size)
STATUS_T LOS_VmSpaceReserve(LosVmSpace *space, size_t size, VADDR_T vaddr)
STATUS_T OsUnMMap(LosVmSpace *space, VADDR_T addr, size_t size)
STATUS_T OsVmRegionAdjust(LosVmSpace *space, VADDR_T vaddr, size_t size)
VOID * LOS_KernelMallocAlign(UINT32 size, UINT32 boundary)
LosVmPage * LOS_VmPageGet(PADDR_T paddr)
通过物理地址获取页框
Definition: los_vm_page.c:120
VOID * LOS_PhysPagesAllocContiguous(size_t nPages)
分配连续的物理页
Definition: los_vm_phys.c:478
VOID LOS_PhysPageFree(LosVmPage *page)
释放一个物理页框
Definition: los_vm_phys.c:546
VOID LOS_PhysPagesFreeContiguous(VOID *ptr, size_t nPages)
释放指定页数地址连续的物理内存
Definition: los_vm_phys.c:494
size_t LOS_PhysPagesAlloc(size_t nPages, LOS_DL_LIST *list)
LOS_PhysPagesAlloc 分配nPages页个物理页框,并将页框挂入list 返回已分配的页面大小,不负责一定能分配到nPages的页框
Definition: los_vm_phys.c:581
size_t LOS_PhysPagesFree(LOS_DL_LIST *list)
释放双链表中的所有节点内存,本质是回归到伙伴orderlist中
Definition: los_vm_phys.c:661
LosVmPage * OsVmVaddrToPage(VOID *ptr)
通过虚拟地址找映射的物理页框
Definition: los_vm_phys.c:295
VOID OsShmFork(LosVmSpace *space, LosVmMapRegion *oldRegion, LosVmMapRegion *newRegion)
fork 一个共享线性区
Definition: shm.c:393
BOOL OsIsShmRegion(LosVmMapRegion *region)
是否为共享线性区,是否有标签?
Definition: shm.c:435
VOID OsShmRegionFree(LosVmSpace *space, LosVmMapRegion *region)
释放共享线性区
Definition: shm.c:412
STATUS_T LOS_UnMMap(VADDR_T addr, size_t size)
解除映射关系
内存管理单元(英语:memory management unit,缩写为MMU),有时称作分页内存管理单元(英语:paged memory management unit,缩写为PMMU)。
Definition: los_arch_mmu.h:86
LOS_DL_LIST ptList
Definition: los_arch_mmu.h:93
文件页结构体
struct VmPage * vmPage
物理页框
Definition: los_mux.h:73
LosVmSpace * vmSpace
UINT32 size
Definition: los_vm_map.h:85
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
LosRbNode rbNode
Definition: los_vm_map.h:120
UINT32 shmid
Definition: los_vm_map.h:126
UINT8 forkFlags
Definition: los_vm_map.h:127
UINT8 regionType
Definition: los_vm_map.h:128
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
LOS_DL_LIST node
Definition: los_vm_page.h:54
UINT16 nPages
Definition: los_vm_page.h:61
PADDR_T physAddr
Definition: los_vm_page.h:56
虚拟空间,每个进程都有一个属于自己的虚拟内存地址空间
Definition: los_vm_map.h:146
VADDR_T base
Definition: los_vm_map.h:150
VADDR_T codeStart
Definition: los_vm_map.h:159
LosVmMapRegion * heap
Definition: los_vm_map.h:154
LosRbTree regionRbTree
Definition: los_vm_map.h:148
VADDR_T codeEnd
Definition: los_vm_map.h:160
VADDR_T mapBase
Definition: los_vm_map.h:155
VADDR_T heapBase
Definition: los_vm_map.h:152
LosMux regionMux
Definition: los_vm_map.h:149
LOS_DL_LIST node
Definition: los_vm_map.h:147
VADDR_T heapNow
Definition: los_vm_map.h:153
LosArchMmu archMmu
Definition: los_vm_map.h:157
UINT32 mapSize
Definition: los_vm_map.h:156
UINT32 size
Definition: los_vm_map.h:151
ARG_NUM_3 ARG_NUM_1 ARG_NUM_2 ARG_NUM_2 ARG_NUM_3 ARG_NUM_1 ARG_NUM_4 ARG_NUM_2 ARG_NUM_2 ARG_NUM_5 ARG_NUM_2 void
int VnodeDrop(void)
归还锁
Definition: vnode.c:292
int VnodeHold(void)
拿锁,封装互斥量
Definition: vnode.c:283