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

物理内存管理 - 段页式管理 http://weharmonyos.com/openharmony/zh-cn/device-dev/kernel/kernel-small-basic-memory-physical.html 更多...

浏览源代码.

函数

LosVmPhysSegOsGVmPhysSegGet ()
 获取段数组,全局变量,变量放在 .bbs 区 更多...
 
STATIC VOID OsVmPhysLruInit (struct VmPhysSeg *seg)
 初始化Lru置换链表 更多...
 
STATIC INT32 OsVmPhysSegCreate (paddr_t start, size_t size)
 创建物理段,由区划分转成段管理 更多...
 
VOID OsVmPhysSegAdd (VOID)
 添加物理段 更多...
 
VOID OsVmPhysAreaSizeAdjust (size_t size)
 段区域大小调整 更多...
 
UINT32 OsVmPhysPageNumGet (VOID)
 获得物理内存的总页数 更多...
 
STATIC INLINE VOID OsVmPhysFreeListInit (struct VmPhysSeg *seg)
 初始化空闲链表,分配物理页框使用伙伴算法 更多...
 
VOID OsVmPhysInit (VOID)
 物理段初始化 更多...
 
STATIC VOID OsVmPhysFreeListAddUnsafe (LosVmPage *page, UINT8 order)
 将页框挂入空闲链表,分配物理页框从空闲链表里拿 更多...
 
STATIC VOID OsVmPhysFreeListDelUnsafe (LosVmPage *page)
 将物理页框从空闲链表上摘除,见于物理页框被分配的情况 更多...
 
STATIC VOID OsVmPhysPagesSpiltUnsafe (LosVmPage *page, UINT8 oldOrder, UINT8 newOrder)
 本函数很像卖猪肉的,拿一大块肉剁,先把多余的放回到小块肉堆里去. 更多...
 
LosVmPageOsVmPhysToPage (paddr_t pa, UINT8 segID)
 通过物理地址获取所属参数段的物理页框 更多...
 
LosVmPageOsVmPaddrToPage (paddr_t paddr)
 
VOID * OsVmPageToVaddr (LosVmPage *page)
 通过page获取内核空间的虚拟地址 参考OsArchMmuInit
#define SYS_MEM_BASE DDR_MEM_ADDR /* physical memory base 物理地址的起始地址 * /
本函数非常重要,通过一个物理地址找到内核虚拟地址
内核静态映射:提升虚实转化效率,段映射减少页表项 更多...
 
LosVmPageOsVmVaddrToPage (VOID *ptr)
 通过虚拟地址找映射的物理页框 更多...
 
STATIC INLINE VOID OsVmRecycleExtraPages (LosVmPage *page, size_t startPage, size_t endPage)
 回收一定范围内的页框 更多...
 
STATIC LosVmPageOsVmPhysLargeAlloc (struct VmPhysSeg *seg, size_t nPages)
 大块的物理内存分配 更多...
 
STATIC LosVmPageOsVmPhysPagesAlloc (struct VmPhysSeg *seg, size_t nPages)
 申请物理页并挂在对应的链表上 更多...
 
VOID OsVmPhysPagesFree (LosVmPage *page, UINT8 order)
 释放物理页框,所谓释放物理页就是把页挂到空闲链表中 更多...
 
VOID OsVmPhysPagesFreeContiguous (LosVmPage *page, size_t nPages)
 连续的释放物理页框, 如果8页连在一块是一起释放的 更多...
 
STATIC LosVmPageOsVmPhysPagesGet (size_t nPages)
 OsVmPhysPagesGet 获取一定数量的页框 LosVmPage实体是放在全局大数组中的, LosVmPage->nPages 标记了分配页数 更多...
 
VOID * LOS_PhysPagesAllocContiguous (size_t nPages)
 分配连续的物理页 更多...
 
VOID LOS_PhysPagesFreeContiguous (VOID *ptr, size_t nPages)
 释放指定页数地址连续的物理内存 更多...
 
PADDR_T OsKVaddrToPaddr (VADDR_T kvaddr)
 
VADDR_TLOS_PaddrToKVaddr (PADDR_T paddr)
 通过物理地址获取内核虚拟地址 更多...
 
VOID LOS_PhysPageFree (LosVmPage *page)
 释放一个物理页框 更多...
 
LosVmPageLOS_PhysPageAlloc (VOID)
 申请一个物理页 更多...
 
size_t LOS_PhysPagesAlloc (size_t nPages, LOS_DL_LIST *list)
 LOS_PhysPagesAlloc 分配nPages页个物理页框,并将页框挂入list
返回已分配的页面大小,不负责一定能分配到nPages的页框
更多...
 
VOID OsPhysSharePageCopy (PADDR_T oldPaddr, PADDR_T *newPaddr, LosVmPage *newPage)
 拷贝共享页面 更多...
 
struct VmPhysSegOsVmPhysSegGet (LosVmPage *page)
 获取物理页框所在段 更多...
 
UINT32 OsVmPagesToOrder (size_t nPages)
 获取参数nPages对应的块组,例如 7 -> 2^3 返回 3 更多...
 
size_t LOS_PhysPagesFree (LOS_DL_LIST *list)
 释放双链表中的所有节点内存,本质是回归到伙伴orderlist中 更多...
 

变量

STATIC struct VmPhysArea g_physArea []
 
struct VmPhysSeg g_vmPhysSeg [VM_PHYS_SEG_MAX]
 最大32段 更多...
 
INT32 g_vmPhysSegNum = 0
 段总数 更多...
 

详细描述

物理内存管理 - 段页式管理 http://weharmonyos.com/openharmony/zh-cn/device-dev/kernel/kernel-small-basic-memory-physical.html

基本概念
   物理内存是计算机上最重要的资源之一,指的是实际的内存设备提供的、可以通过CPU总线直接进行寻址的内存空间,
   其主要作用是为操作系统及程序提供临时存储空间。LiteOS-A内核管理物理内存是通过分页实现的,除了内核堆占用的一部分内存外,
   其余可用内存均以4KiB为单位划分成页帧,内存分配和内存回收便是以页帧为单位进行操作。内核采用伙伴算法管理空闲页面,
   可以降低一定的内存碎片率,提高内存分配和释放的效率,但是一个很小的块往往也会阻塞一个大块的合并,导致不能分配较大的内存块。
运行机制
   LiteOS-A内核的物理内存使用分布视图,主要由内核镜像、内核堆及物理页组成。内核堆部分见堆内存管理一节。
   -----------------------------------------------------

    kernel.bin      | heap      | page frames
    (内核镜像)          | (内核堆)     | (物理页框)
   -----------------------------------------------------
   伙伴算法把所有空闲页帧分成9个内存块组,每组中内存块包含2的幂次方个页帧,例如:第0组的内存块包含2的0次方个页帧,
   即1个页帧;第8组的内存块包含2的8次方个页帧,即256个页帧。相同大小的内存块挂在同一个链表上进行管理。
   
申请内存
    系统申请20KiB内存,按一页帧4K算,即5个页帧时,9个内存块组中索引为2的链表挂着一块大小为8个页帧的内存块满足要求,分配出20KiB内存后还剩余12KiB内存,
    即3个页帧,将3个页帧分成2的幂次方之和,即0跟1,尝试查找伙伴进行合并。2个页帧的内存块没有伙伴则直接插到索引为1的链表上,
    继续查找1个页帧的内存块是否有伙伴,索引为0的链表上此时有1个,如果两个内存块地址连续则进行合并,并将内存块挂到索引为0的链表上,否则不做处理。
释放内存
    系统释放12KiB内存,即3个页帧,将3个页帧分成2的幂次方之和,即2跟1,尝试查找伙伴进行合并,索引为1的链表上有1个内存块,
    若地址连续则合并,并将合并后的内存块挂到索引为2的链表上,索引为0的链表上此时也有1个,如果地址连续则进行合并,
    并将合并后的内存块挂到索引为1的链表上,此时继续判断是否有伙伴,重复上述操作。 
版本
作者
weharmonyos.com | 鸿蒙研究站 | 每天死磕一点点
日期
2021-11-25

在文件 los_vm_phys.c 中定义.

函数说明

◆ LOS_PaddrToKVaddr()

VADDR_T * LOS_PaddrToKVaddr ( PADDR_T  paddr)

通过物理地址获取内核虚拟地址

在文件 los_vm_phys.c527 行定义.

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}
unsigned long VADDR_T
Definition: los_typedef.h:208
unsigned long UINTPTR
Definition: los_typedef.h:68
unsigned int UINT32
Definition: los_typedef.h:57
INT32 g_vmPhysSegNum
段总数
Definition: los_vm_phys.c:88
struct VmPhysSeg g_vmPhysSeg[VM_PHYS_SEG_MAX]
最大32段
Definition: los_vm_phys.c:87
物理段描述符
Definition: los_vm_phys.h:85
size_t size
Definition: los_vm_phys.h:87
PADDR_T start
Definition: los_vm_phys.h:86
这是这个函数的调用关系图:

◆ LOS_PhysPageAlloc()

LosVmPage * LOS_PhysPageAlloc ( VOID  )

申请一个物理页

在文件 los_vm_phys.c566 行定义.

567{
568 return OsVmPhysPagesGet(ONE_PAGE);//分配一页物理页
569}
STATIC LosVmPage * OsVmPhysPagesGet(size_t nPages)
OsVmPhysPagesGet 获取一定数量的页框 LosVmPage实体是放在全局大数组中的, LosVmPage->nPages 标记了分配页数
Definition: los_vm_phys.c:455
函数调用图:
这是这个函数的调用关系图:

◆ LOS_PhysPageFree()

VOID LOS_PhysPageFree ( LosVmPage page)

释放一个物理页框

在文件 los_vm_phys.c546 行定义.

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}
STATIC INLINE VOID LOS_AtomicSet(Atomic *v, INT32 setVal)
Atomic setting.
Definition: los_atomic.h:147
STATIC INLINE INT32 LOS_AtomicDecRet(Atomic *v)
Atomic auto-decrement. | 对内存数据减1并返回运算结果
Definition: los_atomic.h:357
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 OsVmPhysPagesFreeContiguous(LosVmPage *page, size_t nPages)
连续的释放物理页框, 如果8页连在一块是一起释放的
Definition: los_vm_phys.c:420
Atomic refCounts
Definition: los_vm_page.h:57
UINT8 segID
Definition: los_vm_page.h:60
SPIN_LOCK_S freeListLock
Definition: los_vm_phys.h:89
函数调用图:
这是这个函数的调用关系图:

◆ LOS_PhysPagesAlloc()

size_t LOS_PhysPagesAlloc ( size_t  nPages,
LOS_DL_LIST list 
)

LOS_PhysPagesAlloc 分配nPages页个物理页框,并将页框挂入list
返回已分配的页面大小,不负责一定能分配到nPages的页框

参数
list
nPages
返回
参见

在文件 los_vm_phys.c581 行定义.

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}
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
物理页框描述符 虚拟内存体现的是程序对内存资源的需求,而物理内存是对该请求的供应。 伙伴算法的思想是:把内存中连续的空闲页框空间看成是空闲页框块,并按照它们的大小(连续页框的数目)分组
Definition: los_vm_page.h:53
LOS_DL_LIST node
Definition: los_vm_page.h:54
函数调用图:
这是这个函数的调用关系图:

◆ LOS_PhysPagesAllocContiguous()

VOID * LOS_PhysPagesAllocContiguous ( size_t  nPages)

分配连续的物理页

在文件 los_vm_phys.c478 行定义.

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}
VOID * OsVmPageToVaddr(LosVmPage *page)
通过page获取内核空间的虚拟地址 参考OsArchMmuInit #define SYS_MEM_BASE DDR_MEM_ADDR /* physical memory base 物理地址的起始...
Definition: los_vm_phys.c:288
函数调用图:
这是这个函数的调用关系图:

◆ LOS_PhysPagesFree()

size_t LOS_PhysPagesFree ( LOS_DL_LIST list)

释放双链表中的所有节点内存,本质是回归到伙伴orderlist中

在文件 los_vm_phys.c661 行定义.

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}
LITE_OS_SEC_ALW_INLINE STATIC INLINE VOID LOS_ListDelete(LOS_DL_LIST *node)
Definition: los_list.h:292
函数调用图:
这是这个函数的调用关系图:

◆ LOS_PhysPagesFreeContiguous()

VOID LOS_PhysPagesFreeContiguous ( VOID *  ptr,
size_t  nPages 
)

释放指定页数地址连续的物理内存

在文件 los_vm_phys.c494 行定义.

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}
LosVmPage * OsVmVaddrToPage(VOID *ptr)
通过虚拟地址找映射的物理页框
Definition: los_vm_phys.c:295
UINT16 nPages
Definition: los_vm_page.h:61
函数调用图:
这是这个函数的调用关系图:

◆ OsGVmPhysSegGet()

LosVmPhysSeg * OsGVmPhysSegGet ( )

获取段数组,全局变量,变量放在 .bbs 区

在文件 los_vm_phys.c90 行定义.

91{
92 return g_vmPhysSeg;
93}
这是这个函数的调用关系图:

◆ OsKVaddrToPaddr()

PADDR_T OsKVaddrToPaddr ( VADDR_T  kvaddr)

在文件 los_vm_phys.c519 行定义.

520{
521 if (kvaddr == 0) {
522 return 0;
523 }
524 return (kvaddr - KERNEL_ASPACE_BASE + SYS_MEM_BASE);
525}
这是这个函数的调用关系图:

◆ OsPhysSharePageCopy()

VOID OsPhysSharePageCopy ( PADDR_T  oldPaddr,
PADDR_T newPaddr,
LosVmPage newPage 
)

拷贝共享页面

在文件 los_vm_phys.c602 行定义.

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}
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_AtomicDec(Atomic *v)
Atomic auto-decrement. | 对32bit原子数据做减1
Definition: los_atomic.h:323
LosVmPage * LOS_VmPageGet(PADDR_T paddr)
通过物理地址获取页框
Definition: los_vm_page.c:120
VADDR_T * LOS_PaddrToKVaddr(PADDR_T paddr)
通过物理地址获取内核虚拟地址
Definition: los_vm_phys.c:527
函数调用图:
这是这个函数的调用关系图:

◆ OsVmPaddrToPage()

LosVmPage * OsVmPaddrToPage ( paddr_t  paddr)

在文件 los_vm_phys.c267 行定义.

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}
signed int INT32
Definition: los_typedef.h:60
LosVmPage * OsVmPhysToPage(paddr_t pa, UINT8 segID)
通过物理地址获取所属参数段的物理页框
Definition: los_vm_phys.c:250
函数调用图:
这是这个函数的调用关系图:

◆ OsVmPagesToOrder()

UINT32 OsVmPagesToOrder ( size_t  nPages)

获取参数nPages对应的块组,例如 7 -> 2^3 返回 3

在文件 los_vm_phys.c652 行定义.

653{
654 UINT32 order;
655
656 for (order = 0; VM_ORDER_TO_PAGES(order) < nPages; order++);
657
658 return order;
659}
这是这个函数的调用关系图:

◆ OsVmPageToVaddr()

VOID * OsVmPageToVaddr ( LosVmPage page)

通过page获取内核空间的虚拟地址 参考OsArchMmuInit
#define SYS_MEM_BASE DDR_MEM_ADDR /* physical memory base 物理地址的起始地址 * /
本函数非常重要,通过一个物理地址找到内核虚拟地址
内核静态映射:提升虚实转化效率,段映射减少页表项

参数
page
返回
VOID*

在文件 los_vm_phys.c288 行定义.

289{
290 VADDR_T vaddr;
291 vaddr = KERNEL_ASPACE_BASE + page->physAddr - SYS_MEM_BASE;//表示申请的物理地址在物理空间的偏移量等于映射的虚拟地址在内核空间的偏移量
292 return (VOID *)(UINTPTR)vaddr;//不需要存储映射关系,这简直就是神来之笔,拍案叫绝。@note_good 详见 鸿蒙内核源码分析(页表管理篇)
293}
PADDR_T physAddr
Definition: los_vm_page.h:56
这是这个函数的调用关系图:

◆ OsVmPhysAreaSizeAdjust()

VOID OsVmPhysAreaSizeAdjust ( size_t  size)

段区域大小调整

在文件 los_vm_phys.c141 行定义.

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}
STATIC struct VmPhysArea g_physArea[]
Definition: los_vm_phys.c:80
size_t size
物理内存总大小
Definition: los_vm_phys.h:100
PADDR_T start
物理内存区基地址
Definition: los_vm_phys.h:99
这是这个函数的调用关系图:

◆ OsVmPhysFreeListAddUnsafe()

STATIC VOID OsVmPhysFreeListAddUnsafe ( LosVmPage page,
UINT8  order 
)

将页框挂入空闲链表,分配物理页框从空闲链表里拿

在文件 los_vm_phys.c196 行定义.

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}
NORETURN VOID LOS_Panic(const CHAR *fmt,...)
Kernel panic function.
LOS_DL_LIST node
双循环链表用于挂空闲物理内框节点,通过 VmPage->node 挂上来
Definition: los_vm_phys.h:67
UINT32 listCnt
空闲物理页总数
Definition: los_vm_phys.h:68
UINT8 order
Definition: los_vm_page.h:59
struct VmFreeList freeList[VM_LIST_ORDER_MAX]
Definition: los_vm_phys.h:90
函数调用图:
这是这个函数的调用关系图:

◆ OsVmPhysFreeListDelUnsafe()

STATIC VOID OsVmPhysFreeListDelUnsafe ( LosVmPage page)

将物理页框从空闲链表上摘除,见于物理页框被分配的情况

在文件 los_vm_phys.c213 行定义.

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}
函数调用图:
这是这个函数的调用关系图:

◆ OsVmPhysFreeListInit()

STATIC INLINE VOID OsVmPhysFreeListInit ( struct VmPhysSeg seg)

初始化空闲链表,分配物理页框使用伙伴算法

在文件 los_vm_phys.c164 行定义.

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}
LITE_OS_SEC_ALW_INLINE STATIC INLINE VOID LOS_ListInit(LOS_DL_LIST *list)
Definition: los_list.h:104
VOID LOS_SpinInit(SPIN_LOCK_S *lock)
Definition: los_spinlock.c:37
函数调用图:
这是这个函数的调用关系图:

◆ OsVmPhysInit()

VOID OsVmPhysInit ( VOID  )

物理段初始化

在文件 los_vm_phys.c181 行定义.

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}
LosVmPage * g_vmPageArray
物理页框(page frame)池,在g_vmPageArray中:不可能存在两个物理地址一样的物理页框,
Definition: los_vm_page.c:41
STATIC INLINE VOID OsVmPhysFreeListInit(struct VmPhysSeg *seg)
初始化空闲链表,分配物理页框使用伙伴算法
Definition: los_vm_phys.c:164
STATIC VOID OsVmPhysLruInit(struct VmPhysSeg *seg)
初始化Lru置换链表
Definition: los_vm_phys.c:95
LosVmPage * pageBase
Definition: los_vm_phys.h:88
函数调用图:
这是这个函数的调用关系图:

◆ OsVmPhysLargeAlloc()

STATIC LosVmPage * OsVmPhysLargeAlloc ( struct VmPhysSeg seg,
size_t  nPages 
)

大块的物理内存分配

在文件 los_vm_phys.c320 行定义.

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}
unsigned long PADDR_T
Definition: los_typedef.h:207
这是这个函数的调用关系图:

◆ OsVmPhysLruInit()

STATIC VOID OsVmPhysLruInit ( struct VmPhysSeg seg)

初始化Lru置换链表

在文件 los_vm_phys.c95 行定义.

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}
@ VM_NR_LRU_LISTS
Definition: los_vm_phys.h:80
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
函数调用图:
这是这个函数的调用关系图:

◆ OsVmPhysPageNumGet()

UINT32 OsVmPhysPageNumGet ( VOID  )

获得物理内存的总页数

在文件 los_vm_phys.c152 行定义.

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}
这是这个函数的调用关系图:

◆ OsVmPhysPagesAlloc()

STATIC LosVmPage * OsVmPhysPagesAlloc ( struct VmPhysSeg seg,
size_t  nPages 
)

申请物理页并挂在对应的链表上

在文件 los_vm_phys.c356 行定义.

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}
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
STATIC LosVmPage * OsVmPhysLargeAlloc(struct VmPhysSeg *seg, size_t nPages)
大块的物理内存分配
Definition: los_vm_phys.c:320
STATIC VOID OsVmPhysPagesSpiltUnsafe(LosVmPage *page, UINT8 oldOrder, UINT8 newOrder)
本函数很像卖猪肉的,拿一大块肉剁,先把多余的放回到小块肉堆里去.
Definition: los_vm_phys.c:237
STATIC INLINE VOID OsVmRecycleExtraPages(LosVmPage *page, size_t startPage, size_t endPage)
回收一定范围内的页框
Definition: los_vm_phys.c:311
UINT32 OsVmPagesToOrder(size_t nPages)
获取参数nPages对应的块组,例如 7 -> 2^3 返回 3
Definition: los_vm_phys.c:652
STATIC VOID OsVmPhysFreeListDelUnsafe(LosVmPage *page)
将物理页框从空闲链表上摘除,见于物理页框被分配的情况
Definition: los_vm_phys.c:213
函数调用图:
这是这个函数的调用关系图:

◆ OsVmPhysPagesFree()

VOID OsVmPhysPagesFree ( LosVmPage page,
UINT8  order 
)

释放物理页框,所谓释放物理页就是把页挂到空闲链表中

在文件 los_vm_phys.c393 行定义.

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}
unsigned long paddr_t
Definition: los_typedef.h:209
STATIC VOID OsVmPhysFreeListAddUnsafe(LosVmPage *page, UINT8 order)
将页框挂入空闲链表,分配物理页框从空闲链表里拿
Definition: los_vm_phys.c:196
函数调用图:
这是这个函数的调用关系图:

◆ OsVmPhysPagesFreeContiguous()

VOID OsVmPhysPagesFreeContiguous ( LosVmPage page,
size_t  nPages 
)

连续的释放物理页框, 如果8页连在一块是一起释放的

在文件 los_vm_phys.c420 行定义.

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}
UINT16 LOS_HighBitGet(UINT32 bitmap)
获取参数位图中最高位为1的索引位 例如: 00110110 返回 5
Definition: los_bitmap.c:88
VOID OsVmPhysPagesFree(LosVmPage *page, UINT8 order)
释放物理页框,所谓释放物理页就是把页挂到空闲链表中
Definition: los_vm_phys.c:393
函数调用图:
这是这个函数的调用关系图:

◆ OsVmPhysPagesGet()

STATIC LosVmPage * OsVmPhysPagesGet ( size_t  nPages)

OsVmPhysPagesGet 获取一定数量的页框 LosVmPage实体是放在全局大数组中的, LosVmPage->nPages 标记了分配页数

参数
nPages
返回
参见

在文件 los_vm_phys.c455 行定义.

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}
STATIC LosVmPage * OsVmPhysPagesAlloc(struct VmPhysSeg *seg, size_t nPages)
申请物理页并挂在对应的链表上
Definition: los_vm_phys.c:356
函数调用图:
这是这个函数的调用关系图:

◆ OsVmPhysPagesSpiltUnsafe()

STATIC VOID OsVmPhysPagesSpiltUnsafe ( LosVmPage page,
UINT8  oldOrder,
UINT8  newOrder 
)

本函数很像卖猪肉的,拿一大块肉剁,先把多余的放回到小块肉堆里去.

参数
page
oldOrder原本要买 2^2肉
newOrder却找到个 2^8肉块
返回
STATIC

在文件 los_vm_phys.c237 行定义.

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}
函数调用图:
这是这个函数的调用关系图:

◆ OsVmPhysSegAdd()

VOID OsVmPhysSegAdd ( VOID  )

添加物理段

在文件 los_vm_phys.c127 行定义.

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}
STATIC INT32 OsVmPhysSegCreate(paddr_t start, size_t size)
创建物理段,由区划分转成段管理
Definition: los_vm_phys.c:109
函数调用图:
这是这个函数的调用关系图:

◆ OsVmPhysSegCreate()

STATIC INT32 OsVmPhysSegCreate ( paddr_t  start,
size_t  size 
)

创建物理段,由区划分转成段管理

在文件 los_vm_phys.c109 行定义.

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}
这是这个函数的调用关系图:

◆ OsVmPhysSegGet()

struct VmPhysSeg * OsVmPhysSegGet ( LosVmPage page)

获取物理页框所在段

在文件 los_vm_phys.c643 行定义.

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}
LosVmPhysSeg * OsGVmPhysSegGet()
获取段数组,全局变量,变量放在 .bbs 区
Definition: los_vm_phys.c:90
函数调用图:
这是这个函数的调用关系图:

◆ OsVmPhysToPage()

LosVmPage * OsVmPhysToPage ( paddr_t  pa,
UINT8  segID 
)

通过物理地址获取所属参数段的物理页框

在文件 los_vm_phys.c250 行定义.

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}
函数调用图:
这是这个函数的调用关系图:

◆ OsVmRecycleExtraPages()

STATIC INLINE VOID OsVmRecycleExtraPages ( LosVmPage page,
size_t  startPage,
size_t  endPage 
)

回收一定范围内的页框

在文件 los_vm_phys.c311 行定义.

312{
313 if (startPage >= endPage) {
314 return;
315 }
316
317 OsVmPhysPagesFreeContiguous(page, endPage - startPage);
318}
函数调用图:
这是这个函数的调用关系图:

◆ OsVmVaddrToPage()

LosVmPage * OsVmVaddrToPage ( VOID *  ptr)

通过虚拟地址找映射的物理页框

在文件 los_vm_phys.c295 行定义.

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}
PADDR_T LOS_PaddrQuery(VOID *vaddr)
通过虚拟地址查询映射的物理地址
Definition: los_vm_map.c:550
函数调用图:
这是这个函数的调用关系图:

变量说明

◆ g_physArea

STATIC struct VmPhysArea g_physArea[]
初始值:
= {
{
.start = SYS_MEM_BASE,
.size = SYS_MEM_SIZE_DEFAULT,
},
}

在文件 los_vm_phys.c80 行定义.

◆ g_vmPhysSeg

struct VmPhysSeg g_vmPhysSeg[VM_PHYS_SEG_MAX]

最大32段

物理内存采用段页式管理,先切段后伙伴算法页

在文件 los_vm_phys.c87 行定义.

◆ g_vmPhysSegNum

INT32 g_vmPhysSegNum = 0

段总数

段数

在文件 los_vm_phys.c88 行定义.