更新日期: 2022/06/01 来源: https://gitee.com/weharmony/kernel_liteos_a_note
los_vm_phys.c
浏览该文件的文档.
1/*!
2 * @file los_vm_phys.c
3 * @brief 物理内存管理 - 段页式管理
4 * @link physical http://weharmonyos.com/openharmony/zh-cn/device-dev/kernel/kernel-small-basic-memory-physical.html @endlink
5 @verbatim
6基本概念
7 物理内存是计算机上最重要的资源之一,指的是实际的内存设备提供的、可以通过CPU总线直接进行寻址的内存空间,
8 其主要作用是为操作系统及程序提供临时存储空间。LiteOS-A内核管理物理内存是通过分页实现的,除了内核堆占用的一部分内存外,
9 其余可用内存均以4KiB为单位划分成页帧,内存分配和内存回收便是以页帧为单位进行操作。内核采用伙伴算法管理空闲页面,
10 可以降低一定的内存碎片率,提高内存分配和释放的效率,但是一个很小的块往往也会阻塞一个大块的合并,导致不能分配较大的内存块。
11运行机制
12 LiteOS-A内核的物理内存使用分布视图,主要由内核镜像、内核堆及物理页组成。内核堆部分见堆内存管理一节。
13 -----------------------------------------------------
14
15 kernel.bin | heap | page frames
16 (内核镜像) | (内核堆) | (物理页框)
17 -----------------------------------------------------
18 伙伴算法把所有空闲页帧分成9个内存块组,每组中内存块包含2的幂次方个页帧,例如:第0组的内存块包含2的0次方个页帧,
19 即1个页帧;第8组的内存块包含2的8次方个页帧,即256个页帧。相同大小的内存块挂在同一个链表上进行管理。
20
21申请内存
22 系统申请20KiB内存,按一页帧4K算,即5个页帧时,9个内存块组中索引为2的链表挂着一块大小为8个页帧的内存块满足要求,分配出20KiB内存后还剩余12KiB内存,
23 即3个页帧,将3个页帧分成2的幂次方之和,即0跟1,尝试查找伙伴进行合并。2个页帧的内存块没有伙伴则直接插到索引为1的链表上,
24 继续查找1个页帧的内存块是否有伙伴,索引为0的链表上此时有1个,如果两个内存块地址连续则进行合并,并将内存块挂到索引为0的链表上,否则不做处理。
25释放内存
26 系统释放12KiB内存,即3个页帧,将3个页帧分成2的幂次方之和,即2跟1,尝试查找伙伴进行合并,索引为1的链表上有1个内存块,
27 若地址连续则合并,并将合并后的内存块挂到索引为2的链表上,索引为0的链表上此时也有1个,如果地址连续则进行合并,
28 并将合并后的内存块挂到索引为1的链表上,此时继续判断是否有伙伴,重复上述操作。
29 @endverbatim
30 * @image html https://gitee.com/weharmonyos/resources/raw/master/17/malloc_phy.png
31 * @image html https://gitee.com/weharmonyos/resources/raw/master/17/free_phy.png
32 * @version
33 * @author weharmonyos.com | 鸿蒙研究站 | 每天死磕一点点
34 * @date 2021-11-25
35 */
36/*
37 * Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
38 * Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved.
39 *
40 * Redistribution and use in source and binary forms, with or without modification,
41 * are permitted provided that the following conditions are met:
42 *
43 * 1. Redistributions of source code must retain the above copyright notice, this list of
44 * conditions and the following disclaimer.
45 *
46 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
47 * of conditions and the following disclaimer in the documentation and/or other materials
48 * provided with the distribution.
49 *
50 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
51 * to endorse or promote products derived from this software without specific prior written
52 * permission.
53 *
54 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
55 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
56 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
57 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
58 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
59 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
60 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
61 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
62 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
63 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
64 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
65 */
66
67#include "los_vm_phys.h"
68#include "los_vm_boot.h"
69#include "los_vm_common.h"
70#include "los_vm_map.h"
71#include "los_vm_dump.h"
72#include "los_process_pri.h"
73
74
75#ifdef LOSCFG_KERNEL_VM
76
77#define ONE_PAGE 1
78
79/* Physical memory area array | 物理内存区数组 */
80STATIC struct VmPhysArea g_physArea[] = {///< 这里只有一个区域,即只生成一个段
81 {
82 .start = SYS_MEM_BASE, //整个物理内存基地址,#define SYS_MEM_BASE DDR_MEM_ADDR , 0x80000000
83 .size = SYS_MEM_SIZE_DEFAULT,//整个物理内存总大小 0x07f00000
84 },
85};
86
87struct VmPhysSeg g_vmPhysSeg[VM_PHYS_SEG_MAX]; ///< 最大32段
88INT32 g_vmPhysSegNum = 0; ///< 段数
89/// 获取段数组,全局变量,变量放在 .bbs 区
91{
92 return g_vmPhysSeg;
93}
94/// 初始化Lru置换链表
95STATIC VOID OsVmPhysLruInit(struct VmPhysSeg *seg)
96{
97 INT32 i;
98 UINT32 intSave;
99 LOS_SpinInit(&seg->lruLock);//初始化自旋锁,自旋锁用于CPU多核同步
100
101 LOS_SpinLockSave(&seg->lruLock, &intSave);
102 for (i = 0; i < VM_NR_LRU_LISTS; i++) { //五个双循环链表
103 seg->lruSize[i] = 0; //记录链表节点数
104 LOS_ListInit(&seg->lruList[i]); //初始化LRU链表
105 }
106 LOS_SpinUnlockRestore(&seg->lruLock, intSave);
107}
108/// 创建物理段,由区划分转成段管理
110{
111 struct VmPhysSeg *seg = NULL;
112
113 if (g_vmPhysSegNum >= VM_PHYS_SEG_MAX) {
114 return -1;
115 }
116
117 seg = &g_vmPhysSeg[g_vmPhysSegNum++];//拿到一段数据
118 for (; (seg > g_vmPhysSeg) && ((seg - 1)->start > (start + size)); seg--) {//定位到合适的段
119 *seg = *(seg - 1);
120 }
121 seg->start = start;
122 seg->size = size;
123
124 return 0;
125}
126/// 添加物理段
128{
129 INT32 i, ret;
130
131 LOS_ASSERT(g_vmPhysSegNum < VM_PHYS_SEG_MAX);
132
133 for (i = 0; i < (sizeof(g_physArea) / sizeof(g_physArea[0])); i++) {//遍历g_physArea数组
134 ret = OsVmPhysSegCreate(g_physArea[i].start, g_physArea[i].size);//由区划分转成段管理
135 if (ret != 0) {
136 VM_ERR("create phys seg failed");
137 }
138 }
139}
140/// 段区域大小调整
142{
143 /*
144 * The first physics memory segment is used for kernel image and kernel heap,
145 * so just need to adjust the first one here.
146 */
147 g_physArea[0].start += size;
148 g_physArea[0].size -= size;
149}
150
151/// 获得物理内存的总页数
153{
154 UINT32 nPages = 0;
155 INT32 i;
156
157 for (i = 0; i < (sizeof(g_physArea) / sizeof(g_physArea[0])); i++) {
158 nPages += g_physArea[i].size >> PAGE_SHIFT;//右移12位,相当于除以4K, 计算出总页数
159 }
160
161 return nPages;//返回所有物理内存总页数
162}
163/// 初始化空闲链表,分配物理页框使用伙伴算法
164STATIC INLINE VOID OsVmPhysFreeListInit(struct VmPhysSeg *seg)
165{
166 int i;
167 UINT32 intSave;
168 struct VmFreeList *list = NULL;
169
170 LOS_SpinInit(&seg->freeListLock);//初始化用于分配的自旋锁
171
172 LOS_SpinLockSave(&seg->freeListLock, &intSave);
173 for (i = 0; i < VM_LIST_ORDER_MAX; i++) {//遍历伙伴算法空闲块组链表
174 list = &seg->freeList[i]; //一个个来
175 LOS_ListInit(&list->node); //LosVmPage.node将挂到list->node上
176 list->listCnt = 0; //链表上的数量默认0
177 }
178 LOS_SpinUnlockRestore(&seg->freeListLock, intSave);
179}
180/// 物理段初始化
181VOID OsVmPhysInit(VOID)
182{
183 struct VmPhysSeg *seg = NULL;
184 UINT32 nPages = 0;
185 int i;
186
187 for (i = 0; i < g_vmPhysSegNum; i++) {
188 seg = &g_vmPhysSeg[i];
189 seg->pageBase = &g_vmPageArray[nPages];//记录本段首页物理页框地址
190 nPages += seg->size >> PAGE_SHIFT;//偏移12位,按4K一页,算出本段总页数
191 OsVmPhysFreeListInit(seg); //初始化空闲链表,分配页框使用伙伴算法
192 OsVmPhysLruInit(seg); //初始化LRU置换链表
193 }
194}
195/// 将页框挂入空闲链表,分配物理页框从空闲链表里拿
197{
198 struct VmPhysSeg *seg = NULL;
199 struct VmFreeList *list = NULL;
200
201 if (page->segID >= VM_PHYS_SEG_MAX) {
202 LOS_Panic("The page segment id(%d) is invalid\n", page->segID);
203 }
204
205 page->order = order;
206 seg = &g_vmPhysSeg[page->segID];
207
208 list = &seg->freeList[order];
209 LOS_ListTailInsert(&list->node, &page->node);
210 list->listCnt++;
211}
212///将物理页框从空闲链表上摘除,见于物理页框被分配的情况
214{
215 struct VmPhysSeg *seg = NULL;
216 struct VmFreeList *list = NULL;
217
218 if ((page->segID >= VM_PHYS_SEG_MAX) || (page->order >= VM_LIST_ORDER_MAX)) {//等于VM_LIST_ORDER_MAX也不行,说明伙伴算法最大支持 2^8的分配
219 LOS_Panic("The page segment id(%u) or order(%u) is invalid\n", page->segID, page->order);
220 }
221
222 seg = &g_vmPhysSeg[page->segID]; //找到物理页框对应的段
223 list = &seg->freeList[page->order]; //根据伙伴算法组序号找到空闲链表
224 list->listCnt--; //链表节点总数减一
225 LOS_ListDelete(&page->node); //将自己从链表上摘除
226 page->order = VM_LIST_ORDER_MAX; //告诉系统物理页框已不在空闲链表上, 用于OsVmPhysPagesSpiltUnsafe的断言
227}
228
229
230/**
231 * @brief 本函数很像卖猪肉的,拿一大块肉剁,先把多余的放回到小块肉堆里去.
232 * @param page
233 * @param oldOrder 原本要买 2^2肉
234 * @param newOrder 却找到个 2^8肉块
235 * @return STATIC
236 */
237STATIC VOID OsVmPhysPagesSpiltUnsafe(LosVmPage *page, UINT8 oldOrder, UINT8 newOrder)
238{
239 UINT32 order;
240 LosVmPage *buddyPage = NULL;
241
242 for (order = newOrder; order > oldOrder;) {//把肉剁碎的过程,把多余的肉块切成2^7,2^6...标准块,
243 order--;//越切越小,逐一挂到对应的空闲链表上
244 buddyPage = &page[VM_ORDER_TO_PAGES(order)];//@note_good 先把多余的肉割出来,这句代码很赞!因为LosVmPage本身是在一个大数组上,page[nPages]可直接定位
245 LOS_ASSERT(buddyPage->order == VM_LIST_ORDER_MAX);//没挂到伙伴算法对应组块空闲链表上的物理页框的order必须是VM_LIST_ORDER_MAX
246 OsVmPhysFreeListAddUnsafe(buddyPage, order);//将劈开的节点挂到对应序号的链表上,buddyPage->order = order
247 }
248}
249///通过物理地址获取所属参数段的物理页框
251{
252 struct VmPhysSeg *seg = NULL;
253 paddr_t offset;
254
255 if (segID >= VM_PHYS_SEG_MAX) {
256 LOS_Panic("The page segment id(%d) is invalid\n", segID);
257 }
258 seg = &g_vmPhysSeg[segID];
259 if ((pa < seg->start) || (pa >= (seg->start + seg->size))) {
260 return NULL;
261 }
262
263 offset = pa - seg->start;//得到物理地址的偏移量
264 return (seg->pageBase + (offset >> PAGE_SHIFT));//得到对应的物理页框
265}
266
268{
269 INT32 segID;
270 LosVmPage *vmPage = NULL;
271
272 for (segID = 0; segID < g_vmPhysSegNum; segID++) {
273 vmPage = OsVmPhysToPage(paddr, segID);
274 if (vmPage != NULL) {
275 return vmPage;
276 }
277 }
278 return NULL;
279}
280/*!
281 * @brief 通过page获取内核空间的虚拟地址 参考OsArchMmuInit
282 \n #define SYS_MEM_BASE DDR_MEM_ADDR /* physical memory base 物理地址的起始地址 * /
283 \n 本函数非常重要,通过一个物理地址找到内核虚拟地址
284 \n 内核静态映射:提升虚实转化效率,段映射减少页表项
285 * @param page
286 * @return VOID*
287 */
289{
290 VADDR_T vaddr;
291 vaddr = KERNEL_ASPACE_BASE + page->physAddr - SYS_MEM_BASE;//表示申请的物理地址在物理空间的偏移量等于映射的虚拟地址在内核空间的偏移量
292 return (VOID *)(UINTPTR)vaddr;//不需要存储映射关系,这简直就是神来之笔,拍案叫绝。@note_good 详见 鸿蒙内核源码分析(页表管理篇)
293}
294///通过虚拟地址找映射的物理页框
296{
297 struct VmPhysSeg *seg = NULL;
298 PADDR_T pa = LOS_PaddrQuery(ptr);//通过空间的虚拟地址查询物理地址
299 UINT32 segID;
300
301 for (segID = 0; segID < g_vmPhysSegNum; segID++) {//遍历所有段
302 seg = &g_vmPhysSeg[segID];
303 if ((pa >= seg->start) && (pa < (seg->start + seg->size))) {//找到物理地址所在的段
304 return seg->pageBase + ((pa - seg->start) >> PAGE_SHIFT);//段基地址+页偏移索引 得到虚拟地址经映射所在物理页框
305 }
306 }
307
308 return NULL;
309}
310/// 回收一定范围内的页框
311STATIC INLINE VOID OsVmRecycleExtraPages(LosVmPage *page, size_t startPage, size_t endPage)
312{
313 if (startPage >= endPage) {
314 return;
315 }
316
317 OsVmPhysPagesFreeContiguous(page, endPage - startPage);
318}
319/// 大块的物理内存分配
320STATIC LosVmPage *OsVmPhysLargeAlloc(struct VmPhysSeg *seg, size_t nPages)
321{
322 struct VmFreeList *list = NULL;
323 LosVmPage *page = NULL;
324 LosVmPage *tmp = NULL;
325 PADDR_T paStart;
326 PADDR_T paEnd;
327 size_t size = nPages << PAGE_SHIFT;
328
329 list = &seg->freeList[VM_LIST_ORDER_MAX - 1];//先找伙伴算法中内存块最大的开撸
330 LOS_DL_LIST_FOR_EACH_ENTRY(page, &list->node, LosVmPage, node) {//遍历链表
331 paStart = page->physAddr;
332 paEnd = paStart + size;
333 if (paEnd > (seg->start + seg->size)) {//匹配物理地址范围
334 continue;
335 }
336
337 for (;;) {
338 paStart += PAGE_SIZE << (VM_LIST_ORDER_MAX - 1);
339 if ((paStart >= paEnd) || (paStart < seg->start) ||
340 (paStart >= (seg->start + seg->size))) {
341 break;
342 }
343 tmp = &seg->pageBase[(paStart - seg->start) >> PAGE_SHIFT];
344 if (tmp->order != (VM_LIST_ORDER_MAX - 1)) {
345 break;
346 }
347 }
348 if (paStart >= paEnd) {
349 return page;
350 }
351 }
352
353 return NULL;
354}
355/// 申请物理页并挂在对应的链表上
356STATIC LosVmPage *OsVmPhysPagesAlloc(struct VmPhysSeg *seg, size_t nPages)
357{
358 struct VmFreeList *list = NULL;
359 LosVmPage *page = NULL;
360 LosVmPage *tmp = NULL;
361 UINT32 order;
362 UINT32 newOrder;
363
364 order = OsVmPagesToOrder(nPages);
365 if (order < VM_LIST_ORDER_MAX) {//按正常的伙伴算法分配
366 for (newOrder = order; newOrder < VM_LIST_ORDER_MAX; newOrder++) {//从小往大了撸
367 list = &seg->freeList[newOrder];
368 if (LOS_ListEmpty(&list->node)) {//这条链路上没有可分配的物理页框
369 continue;//继续往大的找
370 }
371 page = LOS_DL_LIST_ENTRY(LOS_DL_LIST_FIRST(&list->node), LosVmPage, node);//找到了直接返回第一个节点
372 goto DONE;
373 }
374 } else {
375 newOrder = VM_LIST_ORDER_MAX - 1;
376 page = OsVmPhysLargeAlloc(seg, nPages);
377 if (page != NULL) {
378 goto DONE;
379 }
380 }
381 return NULL;
382DONE:
383
384 for (tmp = page; tmp < &page[nPages]; tmp = &tmp[1 << newOrder]) {
386 }
387 OsVmPhysPagesSpiltUnsafe(page, order, newOrder);
388 OsVmRecycleExtraPages(&page[nPages], nPages, ROUNDUP(nPages, (1 << min(order, newOrder))));
389
390 return page;
391}
392/// 释放物理页框,所谓释放物理页就是把页挂到空闲链表中
394{
395 paddr_t pa;
396 LosVmPage *buddyPage = NULL;
397
398 if ((page == NULL) || (order >= VM_LIST_ORDER_MAX)) {
399 return;
400 }
401
402 if (order < VM_LIST_ORDER_MAX - 1) {//order[0,7]
403 pa = VM_PAGE_TO_PHYS(page);//获取物理地址
404 do {//按位异或
405 pa ^= VM_ORDER_TO_PHYS(order);//@note_good 注意这里是高位和低位的 ^= ,也就是说跳到order块组物理地址处,此处处理甚妙!
406 buddyPage = OsVmPhysToPage(pa, page->segID);//通过物理地址拿到页框
407 if ((buddyPage == NULL) || (buddyPage->order != order)) {//页框所在组块必须要对应
408 break;
409 }
410 OsVmPhysFreeListDelUnsafe(buddyPage);//注意buddypage是连续的物理页框 例如order=2时,2^2=4页就是一个块组 |_|_|_|_|
411 order++;
412 pa &= ~(VM_ORDER_TO_PHYS(order) - 1);
413 page = OsVmPhysToPage(pa, page->segID);
414 } while (order < VM_LIST_ORDER_MAX - 1);
415 }
416
417 OsVmPhysFreeListAddUnsafe(page, order);//伙伴算法 空闲节点增加
418}
419///连续的释放物理页框, 如果8页连在一块是一起释放的
420VOID OsVmPhysPagesFreeContiguous(LosVmPage *page, size_t nPages)
421{
422 paddr_t pa;
423 UINT32 order;
424 size_t n;
425
426 while (TRUE) {//死循环
427 pa = VM_PAGE_TO_PHYS(page);//获取页面物理地址
428 order = VM_PHYS_TO_ORDER(pa);//通过物理地址找到伙伴算法的级别
429 n = VM_ORDER_TO_PAGES(order);//通过级别找到物理页块 (1<<order),意思是如果order=3,就可以释放8个页块
430 if (n > nPages) {//nPages只剩下小于2^order时,退出循环
431 break;
432 }
433 OsVmPhysPagesFree(page, order);//释放伙伴算法对应块组
434 nPages -= n;//总页数减少
435 page += n;//释放的页数增多
436 }
437 //举例剩下 7个页框时,依次用 2^2 2^1 2^0 方式释放
438 while (nPages > 0) {
439 order = LOS_HighBitGet(nPages);//从高到低块组释放
440 n = VM_ORDER_TO_PAGES(order);//2^order次方
441 OsVmPhysPagesFree(page, order);//释放块组
442 nPages -= n;
443 page += n;//相当于page[n]
444 }
445}
446
447/*!
448 * @brief OsVmPhysPagesGet 获取一定数量的页框 LosVmPage实体是放在全局大数组中的,
449 * LosVmPage->nPages 标记了分配页数
450 * @param nPages
451 * @return
452 *
453 * @see
454 */
455STATIC LosVmPage *OsVmPhysPagesGet(size_t nPages)
456{
457 UINT32 intSave;
458 struct VmPhysSeg *seg = NULL;
459 LosVmPage *page = NULL;
460 UINT32 segID;
461
462 for (segID = 0; segID < g_vmPhysSegNum; segID++) {
463 seg = &g_vmPhysSeg[segID];
464 LOS_SpinLockSave(&seg->freeListLock, &intSave);
465 page = OsVmPhysPagesAlloc(seg, nPages);//分配指定页数的物理页,nPages需小于伙伴算法一次能分配的最大页数
466 if (page != NULL) {//分配成功
467 /* */
468 LOS_AtomicSet(&page->refCounts, 0);//设置引用次数为0
469 page->nPages = nPages;//页数
470 LOS_SpinUnlockRestore(&seg->freeListLock, intSave);
471 return page;
472 }
473 LOS_SpinUnlockRestore(&seg->freeListLock, intSave);
474 }
475 return NULL;
476}
477///分配连续的物理页
479{
480 LosVmPage *page = NULL;
481
482 if (nPages == 0) {
483 return NULL;
484 }
485 //鸿蒙 nPages 不能大于 2^8 次方,即256个页,1M内存,仅限于内核态,用户态不限制分配大小.
486 page = OsVmPhysPagesGet(nPages);//通过伙伴算法获取物理上连续的页
487 if (page == NULL) {
488 return NULL;
489 }
490
491 return OsVmPageToVaddr(page);//通过物理页找虚拟地址
492}
493/// 释放指定页数地址连续的物理内存
494VOID LOS_PhysPagesFreeContiguous(VOID *ptr, size_t nPages)
495{
496 UINT32 intSave;
497 struct VmPhysSeg *seg = NULL;
498 LosVmPage *page = NULL;
499
500 if (ptr == NULL) {
501 return;
502 }
503
504 page = OsVmVaddrToPage(ptr);//通过虚拟地址找到页框
505 if (page == NULL) {
506 VM_ERR("vm page of ptr(%#x) is null", ptr);
507 return;
508 }
509 page->nPages = 0;//被分配的页数置为0,表示不被分配
510
511 seg = &g_vmPhysSeg[page->segID];
512 LOS_SpinLockSave(&seg->freeListLock, &intSave);
513
514 OsVmPhysPagesFreeContiguous(page, nPages);//具体释放实现
515
516 LOS_SpinUnlockRestore(&seg->freeListLock, intSave);
517}
518
520{
521 if (kvaddr == 0) {
522 return 0;
523 }
524 return (kvaddr - KERNEL_ASPACE_BASE + SYS_MEM_BASE);
525}
526/// 通过物理地址获取内核虚拟地址
528{
529 struct VmPhysSeg *seg = NULL;
530 UINT32 segID;
531
532 if (paddr == 0) {
533 return NULL;
534 }
535
536 for (segID = 0; segID < g_vmPhysSegNum; segID++) {
537 seg = &g_vmPhysSeg[segID];
538 if ((paddr >= seg->start) && (paddr < (seg->start + seg->size))) {
539 return (VADDR_T *)(UINTPTR)(paddr - SYS_MEM_BASE + KERNEL_ASPACE_BASE);
540 }
541 }
542 //内核
543 return (VADDR_T *)(UINTPTR)(paddr - SYS_MEM_BASE + KERNEL_ASPACE_BASE);//
544}
545///释放一个物理页框
547{
548 UINT32 intSave;
549 struct VmPhysSeg *seg = NULL;
550
551 if (page == NULL) {
552 return;
553 }
554
555 if (LOS_AtomicDecRet(&page->refCounts) <= 0) {//减少引用数后不能小于0
556 seg = &g_vmPhysSeg[page->segID];
557 LOS_SpinLockSave(&seg->freeListLock, &intSave);
558
559 OsVmPhysPagesFreeContiguous(page, ONE_PAGE);//释放一页
560 LOS_AtomicSet(&page->refCounts, 0);//只要物理内存被释放了,引用数就必须得重置为 0
561
562 LOS_SpinUnlockRestore(&seg->freeListLock, intSave);
563 }
564}
565/// 申请一个物理页
567{
568 return OsVmPhysPagesGet(ONE_PAGE);//分配一页物理页
569}
570
571/*!
572 * @brief LOS_PhysPagesAlloc 分配nPages页个物理页框,并将页框挂入list
573 \n 返回已分配的页面大小,不负责一定能分配到nPages的页框
574 *
575 * @param list
576 * @param nPages
577 * @return
578 *
579 * @see
580 */
581size_t LOS_PhysPagesAlloc(size_t nPages, LOS_DL_LIST *list)
582{
583 LosVmPage *page = NULL;
584 size_t count = 0;
585
586 if ((list == NULL) || (nPages == 0)) {
587 return 0;
588 }
589
590 while (nPages--) {
591 page = OsVmPhysPagesGet(ONE_PAGE);//一页一页分配,由伙伴算法分配
592 if (page == NULL) {
593 break;
594 }
595 LOS_ListTailInsert(list, &page->node);//从参数链表list尾部挂入新页面结点
596 count++;
597 }
598
599 return count;
600}
601///拷贝共享页面
602VOID OsPhysSharePageCopy(PADDR_T oldPaddr, PADDR_T *newPaddr, LosVmPage *newPage)
603{
604 UINT32 intSave;
605 LosVmPage *oldPage = NULL;
606 VOID *newMem = NULL;
607 VOID *oldMem = NULL;
608 LosVmPhysSeg *seg = NULL;
609
610 if ((newPage == NULL) || (newPaddr == NULL)) {
611 VM_ERR("new Page invalid");
612 return;
613 }
614
615 oldPage = LOS_VmPageGet(oldPaddr);//由物理地址得到页框
616 if (oldPage == NULL) {
617 VM_ERR("invalid oldPaddr %p", oldPaddr);
618 return;
619 }
620
621 seg = &g_vmPhysSeg[oldPage->segID];//拿到物理段
622 LOS_SpinLockSave(&seg->freeListLock, &intSave);
623 if (LOS_AtomicRead(&oldPage->refCounts) == 1) {//页面引用次数仅一次,说明只有一个进程在操作
624 *newPaddr = oldPaddr;//新老指向同一块物理地址
625 } else {//是个共享内存
626 newMem = LOS_PaddrToKVaddr(*newPaddr); //新页虚拟地址
627 oldMem = LOS_PaddrToKVaddr(oldPaddr); //老页虚拟地址
628 if ((newMem == NULL) || (oldMem == NULL)) {
629 LOS_SpinUnlockRestore(&seg->freeListLock, intSave);
630 return;
631 }//请记住,在保护模式下,物理地址只能用于计算,操作(包括拷贝)需要虚拟地址!
632 if (memcpy_s(newMem, PAGE_SIZE, oldMem, PAGE_SIZE) != EOK) {//老页内容复制给新页,需操作虚拟地址,拷贝一页数据
633 VM_ERR("memcpy_s failed");
634 }
635
636 LOS_AtomicInc(&newPage->refCounts);//新页引用次数以原子方式自动减量
637 LOS_AtomicDec(&oldPage->refCounts);//老页引用次数以原子方式自动减量
638 }
639 LOS_SpinUnlockRestore(&seg->freeListLock, intSave);
640 return;
641}
642///获取物理页框所在段
644{
645 if ((page == NULL) || (page->segID >= VM_PHYS_SEG_MAX)) {
646 return NULL;
647 }
648
649 return (OsGVmPhysSegGet() + page->segID);//等用于OsGVmPhysSegGet()[page->segID]
650}
651///获取参数nPages对应的块组,例如 7 -> 2^3 返回 3
653{
654 UINT32 order;
655
656 for (order = 0; VM_ORDER_TO_PAGES(order) < nPages; order++);
657
658 return order;
659}
660///释放双链表中的所有节点内存,本质是回归到伙伴orderlist中
662{
663 UINT32 intSave;
664 LosVmPage *page = NULL;
665 LosVmPage *nPage = NULL;
666 LosVmPhysSeg *seg = NULL;
667 size_t count = 0;
668
669 if (list == NULL) {
670 return 0;
671 }
672
673 LOS_DL_LIST_FOR_EACH_ENTRY_SAFE(page, nPage, list, LosVmPage, node) {//宏循环
674 LOS_ListDelete(&page->node);//先把自己摘出去
675 if (LOS_AtomicDecRet(&page->refCounts) <= 0) {//无引用
676 seg = &g_vmPhysSeg[page->segID];//获取物理段
677 LOS_SpinLockSave(&seg->freeListLock, &intSave);//锁住freeList
678 OsVmPhysPagesFreeContiguous(page, ONE_PAGE);//连续释放,注意这里的ONE_PAGE其实有误导,让人以为是释放4K,其实是指连续的物理页框,如果3页连在一块是一起释放的.
679 LOS_AtomicSet(&page->refCounts, 0);//引用重置为0
680 LOS_SpinUnlockRestore(&seg->freeListLock, intSave);//恢复锁
681 }
682 count++;//继续取下一个node
683 }
684
685 return count;
686}
687#else
689{
690 if ((paddr < DDR_MEM_ADDR) || (paddr >= ((PADDR_T)DDR_MEM_ADDR + DDR_MEM_SIZE))) {
691 return NULL;
692 }
693
694 return (VADDR_T *)DMA_TO_VMM_ADDR(paddr);
695}
696#endif
697
STATIC INLINE VOID LOS_AtomicInc(Atomic *v)
Atomic addSelf.
Definition: los_atomic.h:253
STATIC INLINE INT32 LOS_AtomicRead(const Atomic *v)
Atomic read. | 读取32bit原子数据
Definition: los_atomic.h:123
STATIC INLINE VOID LOS_AtomicSet(Atomic *v, INT32 setVal)
Atomic setting.
Definition: los_atomic.h:147
STATIC INLINE VOID LOS_AtomicDec(Atomic *v)
Atomic auto-decrement. | 对32bit原子数据做减1
Definition: los_atomic.h:323
STATIC INLINE INT32 LOS_AtomicDecRet(Atomic *v)
Atomic auto-decrement. | 对内存数据减1并返回运算结果
Definition: los_atomic.h:357
UINT16 LOS_HighBitGet(UINT32 bitmap)
获取参数位图中最高位为1的索引位 例如: 00110110 返回 5
Definition: los_bitmap.c:88
NORETURN VOID LOS_Panic(const CHAR *fmt,...)
Kernel panic function.
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 BOOL LOS_ListEmpty(LOS_DL_LIST *list)
Identify whether a specified doubly linked list is empty. | 判断链表是否为空
Definition: los_list.h:321
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
VOID LOS_SpinInit(SPIN_LOCK_S *lock)
Definition: los_spinlock.c:37
unsigned long PADDR_T
Definition: los_typedef.h:207
signed int INT32
Definition: los_typedef.h:60
unsigned long paddr_t
Definition: los_typedef.h:209
unsigned long VADDR_T
Definition: los_typedef.h:208
unsigned char UINT8
Definition: los_typedef.h:55
unsigned long UINTPTR
Definition: los_typedef.h:68
unsigned int UINT32
Definition: los_typedef.h:57
PADDR_T LOS_PaddrQuery(VOID *vaddr)
通过虚拟地址查询映射的物理地址
Definition: los_vm_map.c:550
LosVmPage * g_vmPageArray
物理页框(page frame)池,在g_vmPageArray中:不可能存在两个物理地址一样的物理页框,
Definition: los_vm_page.c:41
LosVmPage * LOS_VmPageGet(PADDR_T paddr)
通过物理地址获取页框
Definition: los_vm_page.c:120
STATIC INT32 OsVmPhysSegCreate(paddr_t start, size_t size)
创建物理段,由区划分转成段管理
Definition: los_vm_phys.c:109
VOID * LOS_PhysPagesAllocContiguous(size_t nPages)
分配连续的物理页
Definition: los_vm_phys.c:478
STATIC struct VmPhysArea g_physArea[]
Definition: los_vm_phys.c:80
STATIC LosVmPage * OsVmPhysLargeAlloc(struct VmPhysSeg *seg, size_t nPages)
大块的物理内存分配
Definition: los_vm_phys.c:320
VOID OsPhysSharePageCopy(PADDR_T oldPaddr, PADDR_T *newPaddr, LosVmPage *newPage)
拷贝共享页面
Definition: los_vm_phys.c:602
INT32 g_vmPhysSegNum
段总数
Definition: los_vm_phys.c:88
STATIC LosVmPage * OsVmPhysPagesAlloc(struct VmPhysSeg *seg, size_t nPages)
申请物理页并挂在对应的链表上
Definition: los_vm_phys.c:356
LosVmPage * OsVmPaddrToPage(paddr_t paddr)
Definition: los_vm_phys.c:267
VOID LOS_PhysPageFree(LosVmPage *page)
释放一个物理页框
Definition: los_vm_phys.c:546
VOID OsVmPhysPagesFreeContiguous(LosVmPage *page, size_t nPages)
连续的释放物理页框, 如果8页连在一块是一起释放的
Definition: los_vm_phys.c:420
STATIC VOID OsVmPhysFreeListAddUnsafe(LosVmPage *page, UINT8 order)
将页框挂入空闲链表,分配物理页框从空闲链表里拿
Definition: los_vm_phys.c:196
STATIC VOID OsVmPhysPagesSpiltUnsafe(LosVmPage *page, UINT8 oldOrder, UINT8 newOrder)
本函数很像卖猪肉的,拿一大块肉剁,先把多余的放回到小块肉堆里去.
Definition: los_vm_phys.c:237
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
struct VmPhysSeg g_vmPhysSeg[VM_PHYS_SEG_MAX]
最大32段
Definition: los_vm_phys.c:87
STATIC INLINE VOID OsVmRecycleExtraPages(LosVmPage *page, size_t startPage, size_t endPage)
回收一定范围内的页框
Definition: los_vm_phys.c:311
PADDR_T OsKVaddrToPaddr(VADDR_T kvaddr)
Definition: los_vm_phys.c:519
VOID OsVmPhysPagesFree(LosVmPage *page, UINT8 order)
释放物理页框,所谓释放物理页就是把页挂到空闲链表中
Definition: los_vm_phys.c:393
struct VmPhysSeg * OsVmPhysSegGet(LosVmPage *page)
获取物理页框所在段
Definition: los_vm_phys.c:643
STATIC LosVmPage * OsVmPhysPagesGet(size_t nPages)
OsVmPhysPagesGet 获取一定数量的页框 LosVmPage实体是放在全局大数组中的, LosVmPage->nPages 标记了分配页数
Definition: los_vm_phys.c:455
LosVmPage * OsVmPhysToPage(paddr_t pa, UINT8 segID)
通过物理地址获取所属参数段的物理页框
Definition: los_vm_phys.c:250
LosVmPage * LOS_PhysPageAlloc(VOID)
申请一个物理页
Definition: los_vm_phys.c:566
UINT32 OsVmPhysPageNumGet(VOID)
获得物理内存的总页数
Definition: los_vm_phys.c:152
UINT32 OsVmPagesToOrder(size_t nPages)
获取参数nPages对应的块组,例如 7 -> 2^3 返回 3
Definition: los_vm_phys.c:652
VOID OsVmPhysSegAdd(VOID)
添加物理段
Definition: los_vm_phys.c:127
VOID OsVmPhysAreaSizeAdjust(size_t size)
段区域大小调整
Definition: los_vm_phys.c:141
STATIC INLINE VOID OsVmPhysFreeListInit(struct VmPhysSeg *seg)
初始化空闲链表,分配物理页框使用伙伴算法
Definition: los_vm_phys.c:164
size_t LOS_PhysPagesFree(LOS_DL_LIST *list)
释放双链表中的所有节点内存,本质是回归到伙伴orderlist中
Definition: los_vm_phys.c:661
LosVmPhysSeg * OsGVmPhysSegGet()
获取段数组,全局变量,变量放在 .bbs 区
Definition: los_vm_phys.c:90
LosVmPage * OsVmVaddrToPage(VOID *ptr)
通过虚拟地址找映射的物理页框
Definition: los_vm_phys.c:295
VOID OsVmPhysInit(VOID)
物理段初始化
Definition: los_vm_phys.c:181
VOID * OsVmPageToVaddr(LosVmPage *page)
通过page获取内核空间的虚拟地址 参考OsArchMmuInit #define SYS_MEM_BASE DDR_MEM_ADDR /* physical memory base 物理地址的起始...
Definition: los_vm_phys.c:288
STATIC VOID OsVmPhysLruInit(struct VmPhysSeg *seg)
初始化Lru置换链表
Definition: los_vm_phys.c:95
VADDR_T * LOS_PaddrToKVaddr(PADDR_T paddr)
通过物理地址获取内核虚拟地址
Definition: los_vm_phys.c:527
STATIC VOID OsVmPhysFreeListDelUnsafe(LosVmPage *page)
将物理页框从空闲链表上摘除,见于物理页框被分配的情况
Definition: los_vm_phys.c:213
@ VM_NR_LRU_LISTS
Definition: los_vm_phys.h:80
LOS_DL_LIST node
双循环链表用于挂空闲物理内框节点,通过 VmPage->node 挂上来
Definition: los_vm_phys.h:67
UINT32 listCnt
空闲物理页总数
Definition: los_vm_phys.h:68
物理页框描述符 虚拟内存体现的是程序对内存资源的需求,而物理内存是对该请求的供应。 伙伴算法的思想是:把内存中连续的空闲页框空间看成是空闲页框块,并按照它们的大小(连续页框的数目)分组
Definition: los_vm_page.h:53
Atomic refCounts
Definition: los_vm_page.h:57
LOS_DL_LIST node
Definition: los_vm_page.h:54
UINT8 order
Definition: los_vm_page.h:59
UINT16 nPages
Definition: los_vm_page.h:61
UINT8 segID
Definition: los_vm_page.h:60
PADDR_T physAddr
Definition: los_vm_page.h:56
物理区描述,仅用于方案商配置范围使用
Definition: los_vm_phys.h:98
size_t size
物理内存总大小
Definition: los_vm_phys.h:100
PADDR_T start
物理内存区基地址
Definition: los_vm_phys.h:99
物理段描述符
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 freeListLock
Definition: los_vm_phys.h:89
LosVmPage * pageBase
Definition: los_vm_phys.h:88
struct VmFreeList freeList[VM_LIST_ORDER_MAX]
Definition: los_vm_phys.h:90
size_t size
Definition: los_vm_phys.h:87
PADDR_T start
Definition: los_vm_phys.h:86