Compare commits

..

6 Commits

Binary file not shown.

Binary file not shown.

@ -2315,7 +2315,6 @@ LITE_OS_SEC_TEXT INT32 OsClone(UINT32 flags, UINTPTR sp, UINT32 size)
return OsCopyProcess(cloneFlag & flags, NULL, sp, size);
}
//著名的 fork 函数 记得前往 https://gitee.com/weharmony/kernel_liteos_a_note fork一下 :)
LITE_OS_SEC_TEXT INT32 LOS_Fork(UINT32 flags, const CHAR *name, const TSK_ENTRY_FUNC entry, UINT32 stackSize)
{
UINT32 cloneFlag = CLONE_PARENT | CLONE_THREAD | CLONE_VFORK | CLONE_FILES;

@ -44,7 +44,7 @@ STATIC VOID IdleTimeSliceUpdate(SchedRunqueue *rq, LosTaskCB *taskCB, UINT64 cur
STATIC INT32 IdleParamCompare(const SchedPolicy *sp1, const SchedPolicy *sp2);
STATIC VOID IdlePriorityInheritance(LosTaskCB *owner, const SchedParam *param);
STATIC VOID IdlePriorityRestore(LosTaskCB *owner, const LOS_DL_LIST *list, const SchedParam *param);
//空闲调度
const STATIC SchedOps g_idleOps = {
.dequeue = IdleDequeue,
.enqueue = IdleEnqueue,

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022-2023 Huawei Device Co., Ltd. All rights reserved.
* Copyright (c) 2022-2022 Huawei Device Co., Ltd. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
@ -43,6 +43,8 @@
#define OS_SCHED_READY_MAX 30
#define OS_TIME_SLICE_MIN (INT32)((50 * OS_SYS_NS_PER_US) / OS_NS_PER_CYCLE) /* 50us */
//基于优先数调度算法 Highest-Priority-First (HPF)
STATIC HPFRunqueue g_schedHPF;
STATIC VOID HPFDequeue(SchedRunqueue *rq, LosTaskCB *taskCB);
@ -63,7 +65,7 @@ STATIC VOID HPFTimeSliceUpdate(SchedRunqueue *rq, LosTaskCB *taskCB, UINT64 curr
STATIC INT32 HPFParamCompare(const SchedPolicy *sp1, const SchedPolicy *sp2);
STATIC VOID HPFPriorityInheritance(LosTaskCB *owner, const SchedParam *param);
STATIC VOID HPFPriorityRestore(LosTaskCB *owner, const LOS_DL_LIST *list, const SchedParam *param);
//优先级调度算法操作
const STATIC SchedOps g_priorityOps = {
.dequeue = HPFDequeue,
.enqueue = HPFEnqueue,
@ -243,7 +245,7 @@ STATIC INLINE VOID PriQueInsert(HPFRunqueue *rq, LosTaskCB *taskCB)
taskCB->taskStatus &= ~OS_TASK_STATUS_BLOCKED;
taskCB->taskStatus |= OS_TASK_STATUS_READY;
}
//入就绪队列
STATIC VOID HPFEnqueue(SchedRunqueue *rq, LosTaskCB *taskCB)
{
#ifdef LOSCFG_SCHED_HPF_DEBUG
@ -253,14 +255,14 @@ STATIC VOID HPFEnqueue(SchedRunqueue *rq, LosTaskCB *taskCB)
#endif
PriQueInsert(rq->hpfRunqueue, taskCB);
}
//出就绪队列
STATIC VOID HPFDequeue(SchedRunqueue *rq, LosTaskCB *taskCB)
{
SchedHPF *sched = (SchedHPF *)&taskCB->sp;
if (taskCB->taskStatus & OS_TASK_STATUS_READY) {
if (taskCB->taskStatus & OS_TASK_STATUS_READY) {//是否有就绪状态
PriQueDelete(rq->hpfRunqueue, sched->basePrio, &taskCB->pendList, sched->priority);
taskCB->taskStatus &= ~OS_TASK_STATUS_READY;
taskCB->taskStatus &= ~OS_TASK_STATUS_READY;//更新成非就绪状态
}
}
@ -475,7 +477,7 @@ STATIC VOID HPFPriorityInheritance(LosTaskCB *owner, const SchedParam *param)
LOS_BitmapSet(&sp->priBitmap, sp->priority);
sp->priority = param->priority;
}
/// 恢复任务优先级
STATIC VOID HPFPriorityRestore(LosTaskCB *owner, const LOS_DL_LIST *list, const SchedParam *param)
{
UINT16 priority;
@ -498,8 +500,8 @@ STATIC VOID HPFPriorityRestore(LosTaskCB *owner, const LOS_DL_LIST *list, const
}
if ((list != NULL) && !LOS_ListEmpty((LOS_DL_LIST *)list)) {
priority = LOS_HighBitGet(sp->priBitmap);
LOS_DL_LIST_FOR_EACH_ENTRY(pendedTask, list, LosTaskCB, pendList) {
priority = LOS_HighBitGet(sp->priBitmap);//获取在历史调度中最高优先级
LOS_DL_LIST_FOR_EACH_ENTRY(pendedTask, list, LosTaskCB, pendList) {//遍历链表
SchedHPF *pendSp = (SchedHPF *)&pendedTask->sp;
if ((pendedTask->ops == owner->ops) && (priority != pendSp->priority)) {
LOS_BitmapClr(&sp->priBitmap, pendSp->priority);
@ -537,7 +539,7 @@ VOID HPFProcessDefaultSchedParamGet(SchedParam *param)
{
param->basePrio = OS_USER_PROCESS_PRIORITY_HIGHEST;
}
//HPF 调度策略初始化
VOID HPFSchedPolicyInit(SchedRunqueue *rq)
{
if (ArchCurrCpuid() > 0) {

@ -1,6 +1,6 @@
/*
* Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
* Copyright (c) 2020-2022 Huawei Device Co., Ltd. All rights reserved.
* Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
@ -30,7 +30,7 @@
*/
#include "los_sortlink_pri.h"
/// 排序链表初始化
VOID OsSortLinkInit(SortLinkAttribute *sortLinkHeader)
{
LOS_ListInit(&sortLinkHeader->sortLink);
@ -38,38 +38,47 @@ VOID OsSortLinkInit(SortLinkAttribute *sortLinkHeader)
sortLinkHeader->nodeNum = 0;
}
/*!
* @brief OsAddNode2SortLink ,
*
* @param sortLinkHeader
* @param sortList
* @return
*
* @see
*/
STATIC INLINE VOID AddNode2SortLink(SortLinkAttribute *sortLinkHeader, SortLinkList *sortList)
{
LOS_DL_LIST *head = (LOS_DL_LIST *)&sortLinkHeader->sortLink;
LOS_DL_LIST *head = (LOS_DL_LIST *)&sortLinkHeader->sortLink; //获取双向链表
if (LOS_ListEmpty(head)) {
LOS_ListHeadInsert(head, &sortList->sortLinkNode);
sortLinkHeader->nodeNum++;
if (LOS_ListEmpty(head)) { //空链表,直接插入
LOS_ListHeadInsert(head, &sortList->sortLinkNode);//插入结点
sortLinkHeader->nodeNum++;//CPU的工作量增加了
return;
}
//链表不为空时,插入分三种情况, responseTime 大于,等于,小于的处理
SortLinkList *listSorted = LOS_DL_LIST_ENTRY(head->pstNext, SortLinkList, sortLinkNode);
if (listSorted->responseTime > sortList->responseTime) {
LOS_ListAdd(head, &sortList->sortLinkNode);
sortLinkHeader->nodeNum++;
return;
} else if (listSorted->responseTime == sortList->responseTime) {
LOS_ListAdd(head->pstNext, &sortList->sortLinkNode);
if (listSorted->responseTime > sortList->responseTime) {//如果要插入的节点 responseTime 最小
LOS_ListAdd(head, &sortList->sortLinkNode);//能跑进来说明是最小的,直接插入到第一的位置
sortLinkHeader->nodeNum++;//CPU的工作量增加了
return;//直接返回了
} else if (listSorted->responseTime == sortList->responseTime) {//相等的情况
LOS_ListAdd(head->pstNext, &sortList->sortLinkNode);//插到第二的位置
sortLinkHeader->nodeNum++;
return;
}
LOS_DL_LIST *prevNode = head->pstPrev;
do {
listSorted = LOS_DL_LIST_ENTRY(prevNode, SortLinkList, sortLinkNode);
if (listSorted->responseTime <= sortList->responseTime) {
//处理大于链表中第一个responseTime的情况,需要遍历链表
LOS_DL_LIST *prevNode = head->pstPrev;//注意这里用的前一个结点,也就是说前一个结点中的responseTime 是最大的
do { // @note_good 这里写的有点妙,也是双向链表的魅力所在
listSorted = LOS_DL_LIST_ENTRY(prevNode, SortLinkList, sortLinkNode);//一个个遍历,先比大的再比小的
if (listSorted->responseTime <= sortList->responseTime) {//如果时间比你小,就插到后面
LOS_ListAdd(prevNode, &sortList->sortLinkNode);
sortLinkHeader->nodeNum++;
break;
}
prevNode = prevNode->pstPrev;
} while (1);
prevNode = prevNode->pstPrev;//再拿上一个更小的responseTime进行比较
} while (1);//死循环
}
VOID OsAdd2SortLink(SortLinkAttribute *head, SortLinkList *node, UINT64 responseTime, UINT16 idleCpu)

@ -159,26 +159,26 @@ UINT32 OsKProcessPmUsage(LosVmSpace *kSpace, UINT32 *actualPm)
/* Kernel resident memory, include default heap memory */
memUsed = SYS_MEM_SIZE_DEFAULT - (totalCount << PAGE_SHIFT);
spaceList = LOS_GetVmSpaceList();
spaceList = LOS_GetVmSpaceList();//获取虚拟空间链表,上面挂了所有虚拟空间
LosMux *vmSpaceListMux = OsGVmSpaceMuxGet();
(VOID)LOS_MuxAcquire(vmSpaceListMux);
LOS_DL_LIST_FOR_EACH_ENTRY(space, spaceList, LosVmSpace, node) {
if (space == LOS_GetKVmSpace()) {
LOS_DL_LIST_FOR_EACH_ENTRY(space, spaceList, LosVmSpace, node) {//遍历链表
if (space == LOS_GetKVmSpace()) {//内核空间不统计
continue;
}
UProcessUsed += OsUProcessPmUsage(space, NULL, NULL);
}
(VOID)LOS_MuxRelease(vmSpaceListMux);
/* Kernel dynamic memory, include extended heap memory */
/* Kernel dynamic memory, include extended heap memory */ //内核动态内存,包括扩展堆内存
memUsed += ((usedCount << PAGE_SHIFT) - UProcessUsed);
/* Remaining heap memory */
/* Remaining heap memory */ //剩余堆内存
memUsed -= freeMem;
*actualPm = memUsed;
return memUsed;
}
///shell task 物理内存的使用情况
UINT32 OsShellCmdProcessPmUsage(LosVmSpace *space, UINT32 *sharePm, UINT32 *actualPm)
{
if (space == NULL) {
@ -194,7 +194,7 @@ UINT32 OsShellCmdProcessPmUsage(LosVmSpace *space, UINT32 *sharePm, UINT32 *actu
}
return OsUProcessPmUsage(space, sharePm, actualPm);
}
///虚拟空间物理内存的使用情况参数同时带走共享物理内存sharePm和actualPm单位是字节
UINT32 OsUProcessPmUsage(LosVmSpace *space, UINT32 *sharePm, UINT32 *actualPm)
{
LosVmMapRegion *region = NULL;
@ -230,10 +230,10 @@ UINT32 OsUProcessPmUsage(LosVmSpace *space, UINT32 *sharePm, UINT32 *actualPm)
continue;
}
shareRef = LOS_AtomicRead(&page->refCounts);
shareRef = LOS_AtomicRead(&page->refCounts);//ref 大于1 说明page被其他空间也引用了
if (shareRef > 1) {
if (sharePm != NULL) {
*sharePm += PAGE_SIZE;
*sharePm += PAGE_SIZE;//一页4K字节
}
pmSize += PAGE_SIZE / shareRef;
} else {
@ -250,7 +250,9 @@ UINT32 OsUProcessPmUsage(LosVmSpace *space, UINT32 *sharePm, UINT32 *actualPm)
return pmSize;
}
/// @brief 通过虚拟空间获取进程实体
/// @param space
/// @return
LosProcessCB *OsGetPIDByAspace(const LosVmSpace *space)
{
UINT32 pid;
@ -258,13 +260,13 @@ LosProcessCB *OsGetPIDByAspace(const LosVmSpace *space)
LosProcessCB *processCB = NULL;
SCHEDULER_LOCK(intSave);
for (pid = 0; pid < g_processMaxNum; ++pid) {
for (pid = 0; pid < g_processMaxNum; ++pid) {//循环进程池,进程池本质是个数组
processCB = g_processCBArray + pid;
if (OsProcessIsUnused(processCB)) {
if (OsProcessIsUnused(processCB)) {//进程还没被分配使用
continue;
}
if (processCB->vmSpace == space) {
if (processCB->vmSpace == space) {//找到了
SCHEDULER_UNLOCK(intSave);
return processCB;
}
@ -272,7 +274,11 @@ LosProcessCB *OsGetPIDByAspace(const LosVmSpace *space)
SCHEDULER_UNLOCK(intSave);
return NULL;
}
/// @brief 统计虚拟空间中某个线性区的页数
/// @param space
/// @param region
/// @param pssPages
/// @return
UINT32 OsCountRegionPages(LosVmSpace *space, LosVmMapRegion *region, UINT32 *pssPages)
{
UINT32 regionPages = 0;
@ -306,7 +312,7 @@ UINT32 OsCountRegionPages(LosVmSpace *space, LosVmMapRegion *region, UINT32 *pss
return regionPages;
}
///统计虚拟空间的总页数
UINT32 OsCountAspacePages(LosVmSpace *space)
{
UINT32 spacePages = 0;
@ -390,27 +396,30 @@ VOID OsDumpRegion2(LosVmSpace *space, LosVmMapRegion *region)
region->range.size, flagsStr, regionPages, pssPages);
(VOID)LOS_MemFree(m_aucSysMem0, flagsStr);
}
///dump 指定虚拟空间的信息
VOID OsDumpAspace(LosVmSpace *space)
{
LosVmMapRegion *region = NULL;
LosRbNode *pstRbNode = NULL;
LosRbNode *pstRbNodeNext = NULL;
UINT32 spacePages;
LosProcessCB *pcb = OsGetPIDByAspace(space);
LosProcessCB *pcb = OsGetPIDByAspace(space);//通过虚拟空间找到进程实体
if (pcb == NULL) {
return;
}
spacePages = OsCountAspacePages(space);
//进程ID | 进程虚拟内存控制块地址信息 | 虚拟内存起始地址 | 虚拟内存大小 | 已使用的物理页数量
spacePages = OsCountAspacePages(space);//获取空间的页数
PRINTK("\r\n PID aspace name base size pages \n");
PRINTK(" ---- ------ ---- ---- ----- ----\n");
PRINTK(" %-4d %#010x %-10.10s %#010x %#010x %d\n", pcb->processID, space, pcb->processName,
space->base, space->size, spacePages);
//虚拟区间控制块地址信息 | 虚拟区间类型 | 虚拟区间起始地址 | 虚拟区间大小 | 虚拟区间mmu映射属性 | 已使用的物理页数量(包括共享内存部分 | 已使用的物理页数量
PRINTK("\r\n\t region name base size mmu_flags pages pg/ref\n");
PRINTK("\t ------ ---- ---- ---- --------- ----- -----\n");
RB_SCAN_SAFE(&space->regionRbTree, pstRbNode, pstRbNodeNext)
RB_SCAN_SAFE(&space->regionRbTree, pstRbNode, pstRbNodeNext)//按region 轮询统计
region = (LosVmMapRegion *)pstRbNode;
if (region != NULL) {
OsDumpRegion2(space, region);
@ -421,14 +430,14 @@ VOID OsDumpAspace(LosVmSpace *space)
RB_SCAN_SAFE_END(&space->regionRbTree, pstRbNode, pstRbNodeNext)
return;
}
///查看所有进程使用虚拟内存的情况
VOID OsDumpAllAspace(VOID)
{
LosVmSpace *space = NULL;
LOS_DL_LIST *aspaceList = LOS_GetVmSpaceList();
LOS_DL_LIST_FOR_EACH_ENTRY(space, aspaceList, LosVmSpace, node) {
LOS_DL_LIST *aspaceList = LOS_GetVmSpaceList();//获取所有空间链表
LOS_DL_LIST_FOR_EACH_ENTRY(space, aspaceList, LosVmSpace, node) {//循环取出进程虚拟
(VOID)LOS_MuxAcquire(&space->regionMux);
OsDumpAspace(space);
OsDumpAspace(space);//dump 空间
(VOID)LOS_MuxRelease(&space->regionMux);
}
return;
@ -447,11 +456,11 @@ STATUS_T OsRegionOverlapCheck(LosVmSpace *space, LosVmMapRegion *region)
(VOID)LOS_MuxRelease(&space->regionMux);
return ret;
}
///dump 页表项
VOID OsDumpPte(VADDR_T vaddr)
{
UINT32 l1Index = vaddr >> MMU_DESCRIPTOR_L1_SMALL_SHIFT;
LosVmSpace *space = LOS_SpaceGet(vaddr);
LosVmSpace *space = LOS_SpaceGet(vaddr);//通过虚拟地址获取空间,内核分三个空间 内核进程空间,内核堆空间,用户进程空间
UINT32 ttEntry;
LosVmPage *page = NULL;
PTE_T *l2Table = NULL;
@ -461,27 +470,27 @@ VOID OsDumpPte(VADDR_T vaddr)
return;
}
ttEntry = space->archMmu.virtTtb[l1Index];
ttEntry = space->archMmu.virtTtb[l1Index];//找到 L1 页面项
if (ttEntry) {
l2Table = LOS_PaddrToKVaddr(MMU_DESCRIPTOR_L1_PAGE_TABLE_ADDR(ttEntry));
l2Index = (vaddr % MMU_DESCRIPTOR_L1_SMALL_SIZE) >> PAGE_SHIFT;
l2Table = LOS_PaddrToKVaddr(MMU_DESCRIPTOR_L1_PAGE_TABLE_ADDR(ttEntry));//找到L1页面项对应的 L2表
l2Index = (vaddr % MMU_DESCRIPTOR_L1_SMALL_SIZE) >> PAGE_SHIFT;//找到L2页面项
if (l2Table == NULL) {
goto ERR;
}
page = LOS_VmPageGet(l2Table[l2Index] & ~(PAGE_SIZE - 1));
page = LOS_VmPageGet(l2Table[l2Index] & ~(PAGE_SIZE - 1));//获取物理页框
if (page == NULL) {
goto ERR;
}
PRINTK("vaddr %p, l1Index %d, ttEntry %p, l2Table %p, l2Index %d, pfn %p count %d\n",
vaddr, l1Index, ttEntry, l2Table, l2Index, l2Table[l2Index], LOS_AtomicRead(&page->refCounts));
} else {
vaddr, l1Index, ttEntry, l2Table, l2Index, l2Table[l2Index], LOS_AtomicRead(&page->refCounts));//打印L1 L2 页表项
} else {//不在L1表
PRINTK("vaddr %p, l1Index %d, ttEntry %p\n", vaddr, l1Index, ttEntry);
}
return;
ERR:
PRINTK("%s, error vaddr: %#x, l2Table: %#x, l2Index: %#x\n", __FUNCTION__, vaddr, l2Table, l2Index);
}
///获取段剩余页框数
UINT32 OsVmPhySegPagesGet(LosVmPhysSeg *seg)
{
UINT32 intSave;
@ -489,14 +498,25 @@ UINT32 OsVmPhySegPagesGet(LosVmPhysSeg *seg)
UINT32 segFreePages = 0;
LOS_SpinLockSave(&seg->freeListLock, &intSave);
for (flindex = 0; flindex < VM_LIST_ORDER_MAX; flindex++) {
segFreePages += ((1 << flindex) * seg->freeList[flindex].listCnt);
for (flindex = 0; flindex < VM_LIST_ORDER_MAX; flindex++) {//遍历块组
segFreePages += ((1 << flindex) * seg->freeList[flindex].listCnt);//1 << flindex等于页数, * 节点数 得到组块的总页数.
}
LOS_SpinUnlockRestore(&seg->freeListLock, intSave);
return segFreePages;
}
return segFreePages;//返回剩余未分配的总物理页框
}
///dump 物理内存
/***********************************************************
* phys_seg:
* base:
* size:
* free_pages:
* active anon: pagecache
* inactive anon: pagecache
* active file: pagecache
* inactive file: pagecache
* pmm pages totalused使free
************************************************************/
VOID OsVmPhysDump(VOID)
{
LosVmPhysSeg *seg = NULL;
@ -508,7 +528,7 @@ VOID OsVmPhysDump(VOID)
UINT32 flindex;
UINT32 listCount[VM_LIST_ORDER_MAX] = {0};
for (segIndex = 0; segIndex < g_vmPhysSegNum; segIndex++) {
for (segIndex = 0; segIndex < g_vmPhysSegNum; segIndex++) {//循环取段
seg = &g_vmPhysSeg[segIndex];
if (seg->size > 0) {
segFreePages = OsVmPhySegPagesGet(seg);
@ -538,7 +558,7 @@ VOID OsVmPhysDump(VOID)
PRINTK("\n\rpmm pages: total = %u, used = %u, free = %u\n",
totalPages, (totalPages - totalFreePages), totalFreePages);
}
///获取物理内存的使用信息,两个参数接走数据
VOID OsVmPhysUsedInfoGet(UINT32 *usedCount, UINT32 *totalCount)
{
UINT32 index;
@ -551,12 +571,12 @@ VOID OsVmPhysUsedInfoGet(UINT32 *usedCount, UINT32 *totalCount)
*usedCount = 0;
*totalCount = 0;
for (index = 0; index < g_vmPhysSegNum; index++) {
for (index = 0; index < g_vmPhysSegNum; index++) {//循环取段
physSeg = &g_vmPhysSeg[index];
if (physSeg->size > 0) {
*totalCount += physSeg->size >> PAGE_SHIFT;
segFreePages = OsVmPhySegPagesGet(physSeg);
*usedCount += (*totalCount - segFreePages);
*totalCount += physSeg->size >> PAGE_SHIFT;//叠加段的总页数
segFreePages = OsVmPhySegPagesGet(physSeg);//获取段的剩余页数
*usedCount += (*totalCount - segFreePages);//叠加段的使用页数
}
}
}

@ -54,7 +54,7 @@
extern char __exc_table_start[];
extern char __exc_table_end[];
//线性正确性检查
STATIC STATUS_T OsVmRegionPermissionCheck(LosVmMapRegion *region, UINT32 flags)
{
if ((region->regionFlags & VM_MAP_REGION_FLAG_PERM_READ) != VM_MAP_REGION_FLAG_PERM_READ) {
@ -62,14 +62,14 @@ STATIC STATUS_T OsVmRegionPermissionCheck(LosVmMapRegion *region, UINT32 flags)
return LOS_NOK;
}
if ((flags & VM_MAP_PF_FLAG_WRITE) == VM_MAP_PF_FLAG_WRITE) {
if ((flags & VM_MAP_PF_FLAG_WRITE) == VM_MAP_PF_FLAG_WRITE) {//写入许可
if ((region->regionFlags & VM_MAP_REGION_FLAG_PERM_WRITE) != VM_MAP_REGION_FLAG_PERM_WRITE) {
VM_ERR("write permission check failed operation flags %x, region flags %x", flags, region->regionFlags);
return LOS_NOK;
}
}
if ((flags & VM_MAP_PF_FLAG_INSTRUCTION) == VM_MAP_PF_FLAG_INSTRUCTION) {
if ((flags & VM_MAP_PF_FLAG_INSTRUCTION) == VM_MAP_PF_FLAG_INSTRUCTION) {//指令
if ((region->regionFlags & VM_MAP_REGION_FLAG_PERM_EXECUTE) != VM_MAP_REGION_FLAG_PERM_EXECUTE) {
VM_ERR("exec permission check failed operation flags %x, region flags %x", flags, region->regionFlags);
return LOS_NOK;
@ -97,7 +97,8 @@ STATIC VOID OsFaultTryFixup(ExcContext *frame, VADDR_T excVaddr, STATUS_T *statu
}
#ifdef LOSCFG_FS_VFS
STATIC STATUS_T OsDoReadFault(LosVmMapRegion *region, LosVmPgFault *vmPgFault)
//读页时发生缺页的处理
STATIC STATUS_T OsDoReadFault(LosVmMapRegion *region, LosVmPgFault *vmPgFault)//读缺页
{
status_t ret;
PADDR_T paddr;
@ -105,26 +106,26 @@ STATIC STATUS_T OsDoReadFault(LosVmMapRegion *region, LosVmPgFault *vmPgFault)
VADDR_T vaddr = (VADDR_T)vmPgFault->vaddr;
LosVmSpace *space = region->space;
ret = LOS_ArchMmuQuery(&space->archMmu, vaddr, NULL, NULL);
if (ret == LOS_OK) {
return LOS_OK;
ret = LOS_ArchMmuQuery(&space->archMmu, vaddr, NULL, NULL);//查询是否缺页
if (ret == LOS_OK) {//注意这里时LOS_OK却返回,都OK了说明查到了物理地址,有页了。
return LOS_OK;//查到了就说明不缺页的,缺页就是因为虚拟地址没有映射到物理地址嘛
}
if (region->unTypeData.rf.vmFOps == NULL || region->unTypeData.rf.vmFOps->fault == NULL) {
if (region->unTypeData.rf.vmFOps == NULL || region->unTypeData.rf.vmFOps->fault == NULL) {//线性区必须有实现了缺页接口
VM_ERR("region args invalid, file path: %s", region->unTypeData.rf.vnode->filePath);
return LOS_ERRNO_VM_INVALID_ARGS;
}
(VOID)LOS_MuxAcquire(&region->unTypeData.rf.vnode->mapping.mux_lock);
ret = region->unTypeData.rf.vmFOps->fault(region, vmPgFault);
ret = region->unTypeData.rf.vmFOps->fault(region, vmPgFault);// 函数指针执行的是g_commVmOps.OsVmmFileFault
if (ret == LOS_OK) {
paddr = LOS_PaddrQuery(vmPgFault->pageKVaddr);
page = LOS_VmPageGet(paddr);
paddr = LOS_PaddrQuery(vmPgFault->pageKVaddr);//查询物理地址
page = LOS_VmPageGet(paddr);//获取page
if (page != NULL) { /* just incase of page null */
LOS_AtomicInc(&page->refCounts);
LOS_AtomicInc(&page->refCounts);//ref 自增
OsCleanPageLocked(page);
}
ret = LOS_ArchMmuMap(&space->archMmu, vaddr, paddr, 1,
region->regionFlags & (~VM_MAP_REGION_FLAG_PERM_WRITE));
region->regionFlags & (~VM_MAP_REGION_FLAG_PERM_WRITE));//重新映射为非可写
if (ret < 0) {
VM_ERR("LOS_ArchMmuMap failed");
OsDelMapInfo(region, vmPgFault, false);
@ -140,7 +141,7 @@ STATIC STATUS_T OsDoReadFault(LosVmMapRegion *region, LosVmPgFault *vmPgFault)
return LOS_ERRNO_VM_NO_MEMORY;
}
/* unmap a page when cow happened only */
/* unmap a page when cow happened only *///仅当写时拷贝发生时取消页面映射
STATIC LosVmPage *OsCowUnmapOrg(LosArchMmu *archMmu, LosVmMapRegion *region, LosVmPgFault *vmf)
{
UINT32 intSave;
@ -168,7 +169,7 @@ STATIC LosVmPage *OsCowUnmapOrg(LosArchMmu *archMmu, LosVmMapRegion *region, Los
return oldPage;
}
#endif
//在私有线性区写入文件时发生缺页的处理
status_t OsDoCowFault(LosVmMapRegion *region, LosVmPgFault *vmPgFault)
{
STATUS_T ret;
@ -186,23 +187,23 @@ status_t OsDoCowFault(LosVmMapRegion *region, LosVmPgFault *vmPgFault)
}
space = region->space;
ret = LOS_ArchMmuQuery(&space->archMmu, (VADDR_T)vmPgFault->vaddr, &oldPaddr, NULL);
ret = LOS_ArchMmuQuery(&space->archMmu, (VADDR_T)vmPgFault->vaddr, &oldPaddr, NULL);//查询出老物理地址
if (ret == LOS_OK) {
oldPage = OsCowUnmapOrg(&space->archMmu, region, vmPgFault);
oldPage = OsCowUnmapOrg(&space->archMmu, region, vmPgFault);//取消页面映射
}
newPage = LOS_PhysPageAlloc();
newPage = LOS_PhysPageAlloc();//分配一个新页面
if (newPage == NULL) {
VM_ERR("LOS_PhysPageAlloc failed");
ret = LOS_ERRNO_VM_NO_MEMORY;
goto ERR_OUT;
}
newPaddr = VM_PAGE_TO_PHYS(newPage);
kvaddr = OsVmPageToVaddr(newPage);
newPaddr = VM_PAGE_TO_PHYS(newPage);//拿到新的物理地址
kvaddr = OsVmPageToVaddr(newPage);//拿到新的虚拟地址
(VOID)LOS_MuxAcquire(&region->unTypeData.rf.vnode->mapping.mux_lock);
ret = region->unTypeData.rf.vmFOps->fault(region, vmPgFault);
ret = region->unTypeData.rf.vmFOps->fault(region, vmPgFault);// 函数指针 g_commVmOps.OsVmmFileFault
if (ret != LOS_OK) {
VM_ERR("call region->vm_ops->fault fail");
(VOID)LOS_MuxRelease(&region->unTypeData.rf.vnode->mapping.mux_lock);
@ -214,20 +215,20 @@ status_t OsDoCowFault(LosVmMapRegion *region, LosVmPgFault *vmPgFault)
* we can take it as a normal file cow map. 2.this page has done file cow map,
* we can take it as a anonymous cow map.
*/
if ((oldPaddr == 0) || (LOS_PaddrToKVaddr(oldPaddr) == vmPgFault->pageKVaddr)) {
(VOID)memcpy_s(kvaddr, PAGE_SIZE, vmPgFault->pageKVaddr, PAGE_SIZE);
LOS_AtomicInc(&newPage->refCounts);
OsCleanPageLocked(LOS_VmPageGet(LOS_PaddrQuery(vmPgFault->pageKVaddr)));
if ((oldPaddr == 0) || (LOS_PaddrToKVaddr(oldPaddr) == vmPgFault->pageKVaddr)) {//没有映射或者 已在pagecache有映射
(VOID)memcpy_s(kvaddr, PAGE_SIZE, vmPgFault->pageKVaddr, PAGE_SIZE);//直接copy到新页
LOS_AtomicInc(&newPage->refCounts);//引用ref++
OsCleanPageLocked(LOS_VmPageGet(LOS_PaddrQuery(vmPgFault->pageKVaddr)));//解锁
} else {
OsPhysSharePageCopy(oldPaddr, &newPaddr, newPage);
OsPhysSharePageCopy(oldPaddr, &newPaddr, newPage);//调用之前 oldPaddr肯定不等于newPaddr
/* use old page free the new one */
if (newPaddr == oldPaddr) {
LOS_PhysPageFree(newPage);
if (newPaddr == oldPaddr) {//注意这里newPaddr可能已经被改变了,参数传入的是 &newPaddr
LOS_PhysPageFree(newPage);//释放新页,别浪费的内存,内核使用内存是一分钱当十块用.
newPage = NULL;
}
}
ret = LOS_ArchMmuMap(&space->archMmu, (VADDR_T)vmPgFault->vaddr, newPaddr, 1, region->regionFlags);
ret = LOS_ArchMmuMap(&space->archMmu, (VADDR_T)vmPgFault->vaddr, newPaddr, 1, region->regionFlags);//把新物理地址映射给缺页的虚拟地址,这样就不会缺页啦
if (ret < 0) {
VM_ERR("LOS_ArchMmuMap failed");
ret = LOS_ERRNO_VM_NO_MEMORY;
@ -252,7 +253,7 @@ ERR_OUT:
return ret;
}
///在共享线性区写文件操作发生缺页的情况处理,因为线性区是共享的
status_t OsDoSharedFault(LosVmMapRegion *region, LosVmPgFault *vmPgFault)
{
STATUS_T ret;
@ -268,10 +269,10 @@ status_t OsDoSharedFault(LosVmMapRegion *region, LosVmPgFault *vmPgFault)
return LOS_ERRNO_VM_INVALID_ARGS;
}
ret = LOS_ArchMmuQuery(&space->archMmu, vmPgFault->vaddr, &paddr, NULL);
ret = LOS_ArchMmuQuery(&space->archMmu, vmPgFault->vaddr, &paddr, NULL);//查询物理地址
if (ret == LOS_OK) {
LOS_ArchMmuUnmap(&space->archMmu, vmPgFault->vaddr, 1);
ret = LOS_ArchMmuMap(&space->archMmu, vaddr, paddr, 1, region->regionFlags);
LOS_ArchMmuUnmap(&space->archMmu, vmPgFault->vaddr, 1);//先取消映射
ret = LOS_ArchMmuMap(&space->archMmu, vaddr, paddr, 1, region->regionFlags);//再重新映射,为啥这么干,是因为regionFlags变了,
if (ret < 0) {
VM_ERR("LOS_ArchMmuMap failed. ret=%d", ret);
return LOS_ERRNO_VM_NO_MEMORY;
@ -279,16 +280,16 @@ status_t OsDoSharedFault(LosVmMapRegion *region, LosVmPgFault *vmPgFault)
LOS_SpinLockSave(&region->unTypeData.rf.vnode->mapping.list_lock, &intSave);
fpage = OsFindGetEntry(&region->unTypeData.rf.vnode->mapping, vmPgFault->pgoff);
if (fpage) {
OsMarkPageDirty(fpage, region, 0, 0);
if (fpage) {//在页高速缓存(page cache)中找到了
OsMarkPageDirty(fpage, region, 0, 0);//标记为脏页
}
LOS_SpinUnlockRestore(&region->unTypeData.rf.vnode->mapping.list_lock, intSave);
return LOS_OK;
}
//以下是没有映射到物理地址的处理
(VOID)LOS_MuxAcquire(&region->unTypeData.rf.vnode->mapping.mux_lock);
ret = region->unTypeData.rf.vmFOps->fault(region, vmPgFault);
ret = region->unTypeData.rf.vmFOps->fault(region, vmPgFault);//函数指针执行的是g_commVmOps.OsVmmFileFault
if (ret == LOS_OK) {
paddr = LOS_PaddrQuery(vmPgFault->pageKVaddr);
page = LOS_VmPageGet(paddr);
@ -319,23 +320,36 @@ status_t OsDoSharedFault(LosVmMapRegion *region, LosVmPgFault *vmPgFault)
* For COW fault, pagecache is copied to private anonyous pages and the changes on this page
* won't write through to the underlying file. For SHARED fault, pagecache is mapping with
* region->arch_mmu_flags and the changes on this page will write through to the underlying file
*/
*/ //操作文件时产生缺页中断
STATIC STATUS_T OsDoFileFault(LosVmMapRegion *region, LosVmPgFault *vmPgFault, UINT32 flags)
{
STATUS_T ret;
if (flags & VM_MAP_PF_FLAG_WRITE) {
if (region->regionFlags & VM_MAP_REGION_FLAG_SHARED) {
ret = OsDoSharedFault(region, vmPgFault);
} else {
ret = OsDoCowFault(region, vmPgFault);
if (flags & VM_MAP_PF_FLAG_WRITE) {//写页的时候产生缺页
if (region->regionFlags & VM_MAP_REGION_FLAG_SHARED) {//共享线性区
ret = OsDoSharedFault(region, vmPgFault);//写操作时的共享缺页,最复杂,此页上的更改将写入磁盘文件
} else {//非共享线性区
ret = OsDoCowFault(region, vmPgFault);//(写时拷贝技术)写操作时的私有缺页,pagecache被复制到私有的任意一个页面上并在此页面上进行更改,不会直接写入磁盘文件
}
} else {
ret = OsDoReadFault(region, vmPgFault);
} else {//读页的时候产生缺页
ret = OsDoReadFault(region, vmPgFault);//页面读取操作很简单只需共享页面缓存节省内存并进行读权限映射region->arch_mmu_flags&~arch_mmu_FLAG_PERM_WRITE
}
return ret;
}
/***************************************************************
:
:
***************************************************************/
/**
* @brief
* @param vaddr
* @param flags
* @param frame
* @return STATUS_T
*/
STATUS_T OsVmPageFaultHandler(VADDR_T vaddr, UINT32 flags, ExcContext *frame)
{
LosVmSpace *space = LOS_SpaceGet(vaddr);
@ -354,9 +368,9 @@ STATUS_T OsVmPageFaultHandler(VADDR_T vaddr, UINT32 flags, ExcContext *frame)
return status;
}
if (((flags & VM_MAP_PF_FLAG_USER) != 0) && (!LOS_IsUserAddress(vaddr))) {
if (((flags & VM_MAP_PF_FLAG_USER) != 0) && (!LOS_IsUserAddress(vaddr))) {//地址保护,用户空间不允许跨界访问
VM_ERR("user space not allowed to access invalid address: %#x", vaddr);
return LOS_ERRNO_VM_ACCESS_DENIED;
return LOS_ERRNO_VM_ACCESS_DENIED;//拒绝访问
}
#ifdef LOSCFG_KERNEL_PLIMITS
@ -366,7 +380,7 @@ STATUS_T OsVmPageFaultHandler(VADDR_T vaddr, UINT32 flags, ExcContext *frame)
#endif
(VOID)LOS_MuxAcquire(&space->regionMux);
region = LOS_RegionFind(space, vaddr);
region = LOS_RegionFind(space, vaddr);//通过虚拟地址找到所在线性区
if (region == NULL) {
VM_ERR("region not exists, vaddr: %#x", vaddr);
status = LOS_ERRNO_VM_NOT_FOUND;
@ -375,11 +389,11 @@ STATUS_T OsVmPageFaultHandler(VADDR_T vaddr, UINT32 flags, ExcContext *frame)
status = OsVmRegionPermissionCheck(region, flags);
if (status != LOS_OK) {
status = LOS_ERRNO_VM_ACCESS_DENIED;
status = LOS_ERRNO_VM_ACCESS_DENIED;//拒绝访问
goto CHECK_FAILED;
}
if (OomCheckProcess()) {
if (OomCheckProcess()) {//低内存检查
/*
* under low memory, when user process request memory allocation
* it will fail, and result is LOS_NOK and current user process
@ -389,18 +403,18 @@ STATUS_T OsVmPageFaultHandler(VADDR_T vaddr, UINT32 flags, ExcContext *frame)
goto CHECK_FAILED;
}
vaddr = ROUNDDOWN(vaddr, PAGE_SIZE);
vaddr = ROUNDDOWN(vaddr, PAGE_SIZE);//为啥要向下圆整,因为这一页要重新使用,需找到页面基地址
#ifdef LOSCFG_FS_VFS
if (LOS_IsRegionFileValid(region)) {
if (LOS_IsRegionFileValid(region)) {//是否为文件线性区
if (region->unTypeData.rf.vnode == NULL) {
goto CHECK_FAILED;
}
vmPgFault.vaddr = vaddr;
vmPgFault.pgoff = ((vaddr - region->range.base) >> PAGE_SHIFT) + region->pgOff;
vmPgFault.vaddr = vaddr;//虚拟地址
vmPgFault.pgoff = ((vaddr - region->range.base) >> PAGE_SHIFT) + region->pgOff;//计算出文件读取位置
vmPgFault.flags = flags;
vmPgFault.pageKVaddr = NULL;
vmPgFault.pageKVaddr = NULL;//缺失页初始化没有物理地址
status = OsDoFileFault(region, &vmPgFault, flags);
status = OsDoFileFault(region, &vmPgFault, flags);//缺页处理
if (status) {
VM_ERR("vm fault error, status=%d", status);
goto CHECK_FAILED;
@ -408,27 +422,27 @@ STATUS_T OsVmPageFaultHandler(VADDR_T vaddr, UINT32 flags, ExcContext *frame)
goto DONE;
}
#endif
newPage = LOS_PhysPageAlloc();
//请求调页:推迟到不能再推迟为止
newPage = LOS_PhysPageAlloc();//分配一个新的物理页
if (newPage == NULL) {
status = LOS_ERRNO_VM_NO_MEMORY;
goto CHECK_FAILED;
}
newPaddr = VM_PAGE_TO_PHYS(newPage);
(VOID)memset_s(OsVmPageToVaddr(newPage), PAGE_SIZE, 0, PAGE_SIZE);
status = LOS_ArchMmuQuery(&space->archMmu, vaddr, &oldPaddr, NULL);
newPaddr = VM_PAGE_TO_PHYS(newPage);//获取物理地址
(VOID)memset_s(OsVmPageToVaddr(newPage), PAGE_SIZE, 0, PAGE_SIZE);//获取虚拟地址 清0
status = LOS_ArchMmuQuery(&space->archMmu, vaddr, &oldPaddr, NULL);//通过虚拟地址查询老物理地址
if (status >= 0) {
LOS_ArchMmuUnmap(&space->archMmu, vaddr, 1);
OsPhysSharePageCopy(oldPaddr, &newPaddr, newPage);
LOS_ArchMmuUnmap(&space->archMmu, vaddr, 1);//解除映射关系
OsPhysSharePageCopy(oldPaddr, &newPaddr, newPage);//将oldPaddr的数据拷贝到newPage
/* use old page free the new one */
if (newPaddr == oldPaddr) {
LOS_PhysPageFree(newPage);
if (newPaddr == oldPaddr) {//新老物理地址一致
LOS_PhysPageFree(newPage);//继续使用旧页释放新页
newPage = NULL;
}
/* map all of the pages */
status = LOS_ArchMmuMap(&space->archMmu, vaddr, newPaddr, 1, region->regionFlags);
status = LOS_ArchMmuMap(&space->archMmu, vaddr, newPaddr, 1, region->regionFlags);//重新映射新物理地址
if (status < 0) {
VM_ERR("failed to map replacement page, status:%d", status);
status = LOS_ERRNO_VM_MAP_FAILED;
@ -439,8 +453,8 @@ STATUS_T OsVmPageFaultHandler(VADDR_T vaddr, UINT32 flags, ExcContext *frame)
goto DONE;
} else {
/* map all of the pages */
LOS_AtomicInc(&newPage->refCounts);
status = LOS_ArchMmuMap(&space->archMmu, vaddr, newPaddr, 1, region->regionFlags);
LOS_AtomicInc(&newPage->refCounts);//引用数自增
status = LOS_ArchMmuMap(&space->archMmu, vaddr, newPaddr, 1, region->regionFlags);//映射新物理地址,如此下次就不会缺页了
if (status < 0) {
VM_ERR("failed to map page, status:%d", status);
status = LOS_ERRNO_VM_MAP_FAILED;

@ -66,98 +66,114 @@ VOID ResetPageCacheHitInfo(int *try, int *hit)
#define TRACE_TRY_CACHE()
#define TRACE_HIT_CACHE()
#endif
#ifdef LOSCFG_KERNEL_VM
/**
* @brief
@verbatim
(page cache)
LosFilePage,seek,,
pgoff,cache.
@endverbatim
* @param page
* @param mapping
* @param pgoff
* @return STATIC
*/
STATIC VOID OsPageCacheAdd(LosFilePage *page, struct page_mapping *mapping, VM_OFFSET_T pgoff)
{
LosFilePage *fpage = NULL;
LOS_DL_LIST_FOR_EACH_ENTRY(fpage, &mapping->page_list, LosFilePage, node) {
if (fpage->pgoff > pgoff) {
LOS_ListTailInsert(&fpage->node, &page->node);
LOS_DL_LIST_FOR_EACH_ENTRY(fpage, &mapping->page_list, LosFilePage, node) {//遍历page_list链表
if (fpage->pgoff > pgoff) {//插入的条件,这样插入保证了按pgoff 从小到大排序
LOS_ListTailInsert(&fpage->node, &page->node);//等于挂到fpage节点的前面了
goto done_add;
}
}
LOS_ListTailInsert(&mapping->page_list, &page->node);
LOS_ListTailInsert(&mapping->page_list, &page->node);//将页挂到文件映射的链表上,相当于挂到了最后
done_add:
mapping->nrpages++;
mapping->nrpages++; //文件在缓存中多了一个 文件页
}
///将页面加到活动文件页LRU链表上
VOID OsAddToPageacheLru(LosFilePage *page, struct page_mapping *mapping, VM_OFFSET_T pgoff)
{
OsPageCacheAdd(page, mapping, pgoff);
OsLruCacheAdd(page, VM_LRU_ACTIVE_FILE);
}
///从页高速缓存上删除页
VOID OsPageCacheDel(LosFilePage *fpage)
{
/* delete from file cache list */
LOS_ListDelete(&fpage->node);
fpage->mapping->nrpages--;
LOS_ListDelete(&fpage->node);//将自己从链表上摘除
fpage->mapping->nrpages--;//文件映射的页总数减少
/* unmap and remove map info */
if (OsIsPageMapped(fpage)) {
if (OsIsPageMapped(fpage)) {//是否映射过
OsUnmapAllLocked(fpage);
}
LOS_PhysPageFree(fpage->vmPage);
LOS_PhysPageFree(fpage->vmPage);//释放物理内存
LOS_MemFree(m_aucSysMem0, fpage);
LOS_MemFree(m_aucSysMem0, fpage);//释放文件页结构体内存
}
/**************************************************************************************************
, 访LosFilePage,使
LosFilePage.
1. 2.
**************************************************************************************************/
VOID OsAddMapInfo(LosFilePage *page, LosArchMmu *archMmu, VADDR_T vaddr)
{
LosMapInfo *info = NULL;
info = (LosMapInfo *)LOS_MemAlloc(m_aucSysMem0, sizeof(LosMapInfo));
info = (LosMapInfo *)LOS_MemAlloc(m_aucSysMem0, sizeof(LosMapInfo));//分配一个映射信息
if (info == NULL) {
VM_ERR("OsAddMapInfo alloc memory failed!");
return;
}
info->page = page;
info->archMmu = archMmu;
info->vaddr = vaddr;
info->page = page; //文件页
info->archMmu = archMmu;//进程MMU,完成虚实地址转换
info->vaddr = vaddr; //虚拟地址
LOS_ListAdd(&page->i_mmap, &info->node);
page->n_maps++;
LOS_ListAdd(&page->i_mmap, &info->node);//将 LosMapInfo 节点挂入链表
page->n_maps++;//映射总数++
}
///通过虚拟地址获取文件页映射信息,archMmu每个进程都有属于自己的mmu
LosMapInfo *OsGetMapInfo(const LosFilePage *page, const LosArchMmu *archMmu, VADDR_T vaddr)
{
LosMapInfo *info = NULL;
const LOS_DL_LIST *immap = &page->i_mmap;
const LOS_DL_LIST *immap = &page->i_mmap;//一个文件页被多个进程映射
LOS_DL_LIST_FOR_EACH_ENTRY(info, immap, LosMapInfo, node) {
if ((info->archMmu == archMmu) && (info->vaddr == vaddr) && (info->page == page)) {
LOS_DL_LIST_FOR_EACH_ENTRY(info, immap, LosMapInfo, node) {//遍历每个节点
if ((info->archMmu == archMmu) && (info->vaddr == vaddr) && (info->page == page)) {//全等时返回
return info;
}
}
return NULL;
}
///删除页高速缓存和LRU,对应 OsAddToPageacheLru
VOID OsDeletePageCacheLru(LosFilePage *page)
{
/* delete from lru list */
OsLruCacheDel(page);
/* delete from cache list and free pmm if needed */
OsPageCacheDel(page);
/* delete form lru list */
OsLruCacheDel(page); //将页面从lru列表中删除
/* delete from cache lits and free pmm if need */
OsPageCacheDel(page); //从page缓存中删除
}
//解除文件页和进程的映射关系
STATIC VOID OsPageCacheUnmap(LosFilePage *fpage, LosArchMmu *archMmu, VADDR_T vaddr)
{
UINT32 intSave;
LosMapInfo *info = NULL;
LOS_SpinLockSave(&fpage->physSeg->lruLock, &intSave);
info = OsGetMapInfo(fpage, archMmu, vaddr);
info = OsGetMapInfo(fpage, archMmu, vaddr);//获取文件页在进程的映射信息
if (info == NULL) {
VM_ERR("OsPageCacheUnmap get map info failed!");
} else {
OsUnmapPageLocked(fpage, info);
OsUnmapPageLocked(fpage, info);//解除进程和文件页映射关系
}
if (!(OsIsPageMapped(fpage) && ((fpage->flags & VM_MAP_REGION_FLAG_PERM_EXECUTE) ||
OsIsPageDirty(fpage->vmPage)))) {
@ -166,7 +182,7 @@ STATIC VOID OsPageCacheUnmap(LosFilePage *fpage, LosArchMmu *archMmu, VADDR_T va
LOS_SpinUnlockRestore(&fpage->physSeg->lruLock, intSave);
}
///删除文件
VOID OsVmmFileRemove(LosVmMapRegion *region, LosArchMmu *archMmu, VM_OFFSET_T pgoff)
{
UINT32 intSave;
@ -179,31 +195,31 @@ VOID OsVmmFileRemove(LosVmMapRegion *region, LosArchMmu *archMmu, VM_OFFSET_T pg
LosVmPage *mapPage = NULL;
if (!LOS_IsRegionFileValid(region) || (region->unTypeData.rf.vnode == NULL)) {
return;
return;//判断是否为文件映射是否已map
}
vnode = region->unTypeData.rf.vnode;
mapping = &vnode->mapping;
vaddr = region->range.base + ((UINT32)(pgoff - region->pgOff) << PAGE_SHIFT);
vaddr = region->range.base + ((UINT32)(pgoff - region->pgOff) << PAGE_SHIFT);//得到虚拟地址
status_t status = LOS_ArchMmuQuery(archMmu, vaddr, &paddr, NULL);
status_t status = LOS_ArchMmuQuery(archMmu, vaddr, &paddr, NULL);//获取物理地址
if (status != LOS_OK) {
return;
}
mapPage = LOS_VmPageGet(paddr);
mapPage = LOS_VmPageGet(paddr);//获取物理页框
/* is page is in cache list */
LOS_SpinLockSave(&mapping->list_lock, &intSave);
fpage = OsFindGetEntry(mapping, pgoff);
fpage = OsFindGetEntry(mapping, pgoff);//获取fpage
/* no cache or have cache but not map(cow), free it direct */
if ((fpage == NULL) || (fpage->vmPage != mapPage)) {
LOS_PhysPageFree(mapPage);
LOS_ArchMmuUnmap(archMmu, vaddr, 1);
if ((fpage == NULL) || (fpage->vmPage != mapPage)) {//没有缓存或有缓存但没有映射cow直接释放它
LOS_PhysPageFree(mapPage);//释放物理页框
LOS_ArchMmuUnmap(archMmu, vaddr, 1);//取消虚拟地址的映射
/* this is a page cache map! */
} else {
OsPageCacheUnmap(fpage, archMmu, vaddr);
if (OsIsPageDirty(fpage->vmPage)) {
tmpPage = OsDumpDirtyPage(fpage);
OsPageCacheUnmap(fpage, archMmu, vaddr);////取消缓存中的映射
if (OsIsPageDirty(fpage->vmPage)) {//脏页处理
tmpPage = OsDumpDirtyPage(fpage);//dump 脏页
}
}
LOS_SpinUnlockRestore(&mapping->list_lock, intSave);
@ -213,15 +229,15 @@ VOID OsVmmFileRemove(LosVmMapRegion *region, LosArchMmu *archMmu, VM_OFFSET_T pg
}
return;
}
///标记page为脏页 进程修改了高速缓存里的数据时,该页就被内核标记为脏页
VOID OsMarkPageDirty(LosFilePage *fpage, const LosVmMapRegion *region, INT32 off, INT32 len)
{
if (region != NULL) {
OsSetPageDirty(fpage->vmPage);
fpage->dirtyOff = off;
fpage->dirtyEnd = len;
OsSetPageDirty(fpage->vmPage);//设置为脏页
fpage->dirtyOff = off;//脏页偏移位置
fpage->dirtyEnd = len;//脏页结束位置
} else {
OsSetPageDirty(fpage->vmPage);
OsSetPageDirty(fpage->vmPage);//设置为脏页
if ((off + len) > fpage->dirtyEnd) {
fpage->dirtyEnd = off + len;
}
@ -258,22 +274,22 @@ STATIC UINT32 GetDirtySize(LosFilePage *fpage, struct Vnode *vnode)
return PAGE_SIZE;
}
///冲洗脏页,回写磁盘
STATIC INT32 OsFlushDirtyPage(LosFilePage *fpage)
{
UINT32 ret;
size_t len;
char *buff = NULL;
struct Vnode *vnode = fpage->mapping->host;
struct Vnode *vnode = fpage->mapping->host;/* owner of this mapping */ //此映射属于哪个文件,注意<file,page_mapping>是1:1的关系.
if (vnode == NULL) {
VM_ERR("page cache vnode error");
return LOS_NOK;
}
len = fpage->dirtyEnd - fpage->dirtyOff;
len = fpage->dirtyEnd - fpage->dirtyOff;//计算出脏数据长度
len = (len == 0) ? GetDirtySize(fpage, vnode) : len;
if (len == 0) {
OsCleanPageDirty(fpage->vmPage);
if (len == 0) {//没有脏数据
OsCleanPageDirty(fpage->vmPage);//页面取消脏标签
return LOS_OK;
}
@ -290,7 +306,7 @@ STATIC INT32 OsFlushDirtyPage(LosFilePage *fpage)
return ret;
}
///备份脏页,老脏页撕掉脏页标签
LosFilePage *OsDumpDirtyPage(LosFilePage *oldFPage)
{
LosFilePage *newFPage = NULL;
@ -302,11 +318,11 @@ LosFilePage *OsDumpDirtyPage(LosFilePage *oldFPage)
}
OsCleanPageDirty(oldFPage->vmPage);
(VOID)memcpy_s(newFPage, sizeof(LosFilePage), oldFPage, sizeof(LosFilePage));
(VOID)memcpy_s(newFPage, sizeof(LosFilePage), oldFPage, sizeof(LosFilePage));//直接内存拷贝
return newFPage;
}
///冲洗脏页数据,将脏页数据回写磁盘
VOID OsDoFlushDirtyPage(LosFilePage *fpage)
{
if (fpage == NULL) {
@ -328,7 +344,7 @@ STATIC VOID OsReleaseFpage(struct page_mapping *mapping, LosFilePage *fpage)
LOS_SpinUnlockRestore(lruLock, lruSave);
LOS_SpinUnlockRestore(&mapping->list_lock, intSave);
}
///删除映射信息
VOID OsDelMapInfo(LosVmMapRegion *region, LosVmPgFault *vmf, BOOL cleanDirty)
{
UINT32 intSave;
@ -349,9 +365,9 @@ VOID OsDelMapInfo(LosVmMapRegion *region, LosVmPgFault *vmf, BOOL cleanDirty)
}
if (cleanDirty) {
OsCleanPageDirty(fpage->vmPage);
OsCleanPageDirty(fpage->vmPage);//恢复干净页
}
info = OsGetMapInfo(fpage, &region->space->archMmu, (vaddr_t)vmf->vaddr);
info = OsGetMapInfo(fpage, &region->space->archMmu, (vaddr_t)vmf->vaddr);//通过虚拟地址获取映射信息
if (info != NULL) {
fpage->n_maps--;
LOS_ListDelete(&info->node);
@ -362,7 +378,10 @@ VOID OsDelMapInfo(LosVmMapRegion *region, LosVmPgFault *vmf, BOOL cleanDirty)
}
LOS_SpinUnlockRestore(&mapping->list_lock, intSave);
}
/*!
,
OsDoReadFault(...),OsDoCowFault(...),OsDoSharedFault(...)
*/
INT32 OsVmmFileFault(LosVmMapRegion *region, LosVmPgFault *vmf)
{
INT32 ret;
@ -374,7 +393,7 @@ INT32 OsVmmFileFault(LosVmMapRegion *region, LosVmPgFault *vmf)
struct page_mapping *mapping = NULL;
LosFilePage *fpage = NULL;
if (!LOS_IsRegionFileValid(region) || (region->unTypeData.rf.vnode == NULL) || (vmf == NULL)) {
if (!LOS_IsRegionFileValid(region) || (region->unTypeData.rf.vnode == NULL) || (vmf == NULL)) {//文件是否映射到了内存
VM_ERR("Input param is NULL");
return LOS_NOK;
}
@ -383,26 +402,26 @@ INT32 OsVmmFileFault(LosVmMapRegion *region, LosVmPgFault *vmf)
/* get or create a new cache node */
LOS_SpinLockSave(&mapping->list_lock, &intSave);
fpage = OsFindGetEntry(mapping, vmf->pgoff);
fpage = OsFindGetEntry(mapping, vmf->pgoff);//获取文件页
TRACE_TRY_CACHE();
if (fpage != NULL) {
if (fpage != NULL) {//找到了,说明该页已经在页高速缓存中
TRACE_HIT_CACHE();
OsPageRefIncLocked(fpage);
} else {
fpage = OsPageCacheAlloc(mapping, vmf->pgoff);
} else {//真的缺页了,页高速缓存中没找到
fpage = OsPageCacheAlloc(mapping, vmf->pgoff);//分配一个文件页将数据初始化好包括vmpage(物理页框)
if (fpage == NULL) {
LOS_SpinUnlockRestore(&mapping->list_lock, intSave);
VM_ERR("Failed to alloc a page frame");
return LOS_NOK;
}
newCache = true;
newCache = true;//分配了新文件页
}
OsSetPageLocked(fpage->vmPage);
OsSetPageLocked(fpage->vmPage);//对vmpage上锁
LOS_SpinUnlockRestore(&mapping->list_lock, intSave);
kvaddr = OsVmPageToVaddr(fpage->vmPage);
kvaddr = OsVmPageToVaddr(fpage->vmPage);//获取该页框在内核空间的虚拟地址,因为 page cache本身就是在内核空间,
/* read file to new page cache */
if (newCache) {
if (newCache) {//新cache
ret = vnode->vop->ReadPage(vnode, kvaddr, fpage->pgoff << PAGE_SHIFT);
if (ret == 0) {
VM_ERR("Failed to read from file!");
@ -410,32 +429,32 @@ INT32 OsVmmFileFault(LosVmMapRegion *region, LosVmPgFault *vmf)
return LOS_NOK;
}
LOS_SpinLockSave(&mapping->list_lock, &intSave);
OsAddToPageacheLru(fpage, mapping, vmf->pgoff);
OsAddToPageacheLru(fpage, mapping, vmf->pgoff);//将fpage挂入pageCache 和 LruCache
LOS_SpinUnlockRestore(&mapping->list_lock, intSave);
}
LOS_SpinLockSave(&mapping->list_lock, &intSave);
/* cow fault case no need to save mapinfo */
if (!((vmf->flags & VM_MAP_PF_FLAG_WRITE) && !(region->regionFlags & VM_MAP_REGION_FLAG_SHARED))) {
OsAddMapInfo(fpage, &region->space->archMmu, (vaddr_t)vmf->vaddr);
OsAddMapInfo(fpage, &region->space->archMmu, (vaddr_t)vmf->vaddr);//添加<虚拟地址,文件页>的映射关系,如此进程以后就能通过虚拟地址操作文件页了.
fpage->flags = region->regionFlags;
}
/* share page fault, mark the page dirty */
if ((vmf->flags & VM_MAP_PF_FLAG_WRITE) && (region->regionFlags & VM_MAP_REGION_FLAG_SHARED)) {
OsMarkPageDirty(fpage, region, 0, 0);
if ((vmf->flags & VM_MAP_PF_FLAG_WRITE) && (region->regionFlags & VM_MAP_REGION_FLAG_SHARED)) {//有过写操作或者为共享线性区
OsMarkPageDirty(fpage, region, 0, 0);//标记为脏页,要回写磁盘,内核会在适当的时候回写磁盘
}
vmf->pageKVaddr = kvaddr;
vmf->pageKVaddr = kvaddr;//缺陷页记录文件页的虚拟地址
LOS_SpinUnlockRestore(&mapping->list_lock, intSave);
return LOS_OK;
}
///文件缓存冲洗,把所有fpage冲洗一边把脏页洗到dirtyList中,配合OsFileCacheRemove理解
VOID OsFileCacheFlush(struct page_mapping *mapping)
{
UINT32 intSave;
UINT32 lruLock;
LOS_DL_LIST_HEAD(dirtyList);
LOS_DL_LIST_HEAD(dirtyList);//LOS_DL_LIST list = { &(list), &(list) };
LosFilePage *ftemp = NULL;
LosFilePage *fpage = NULL;
@ -443,70 +462,77 @@ VOID OsFileCacheFlush(struct page_mapping *mapping)
return;
}
LOS_SpinLockSave(&mapping->list_lock, &intSave);
LOS_DL_LIST_FOR_EACH_ENTRY(fpage, &mapping->page_list, LosFilePage, node) {
LOS_DL_LIST_FOR_EACH_ENTRY(fpage, &mapping->page_list, LosFilePage, node) {//循环从page_list中取node给fpage
LOS_SpinLockSave(&fpage->physSeg->lruLock, &lruLock);
if (OsIsPageDirty(fpage->vmPage)) {
ftemp = OsDumpDirtyPage(fpage);
if (OsIsPageDirty(fpage->vmPage)) {//是否为脏页
ftemp = OsDumpDirtyPage(fpage);//这里挺妙的copy出一份新页老页变成了非脏页继续用
if (ftemp != NULL) {
LOS_ListTailInsert(&dirtyList, &ftemp->node);
LOS_ListTailInsert(&dirtyList, &ftemp->node);//将新页插入脏页List,等待回写磁盘
}
}
LOS_SpinUnlockRestore(&fpage->physSeg->lruLock, lruLock);
}
LOS_SpinUnlockRestore(&mapping->list_lock, intSave);
LOS_DL_LIST_FOR_EACH_ENTRY_SAFE(fpage, ftemp, &dirtyList, LosFilePage, node) {
OsDoFlushDirtyPage(fpage);
LOS_DL_LIST_FOR_EACH_ENTRY_SAFE(fpage, ftemp, &dirtyList, LosFilePage, node) {//仔细看这个宏,关键在 &(item)->member != (list);
OsDoFlushDirtyPage(fpage);//立马洗掉所以dirtyList可以不是全局变量
}
}
/******************************************************************************
,page cache
mapping
******************************************************************************/
VOID OsFileCacheRemove(struct page_mapping *mapping)
{
UINT32 intSave;
UINT32 lruSave;
SPIN_LOCK_S *lruLock = NULL;
LOS_DL_LIST_HEAD(dirtyList);
LOS_DL_LIST_HEAD(dirtyList);//定义一个叫dirtyList的双循环链表并初始化,用于挂脏页
LosFilePage *ftemp = NULL;
LosFilePage *fpage = NULL;
LosFilePage *fnext = NULL;
LOS_SpinLockSave(&mapping->list_lock, &intSave);
LOS_DL_LIST_FOR_EACH_ENTRY_SAFE(fpage, fnext, &mapping->page_list, LosFilePage, node) {
LOS_SpinLockSave(&mapping->list_lock, &intSave);//多进程操作,必须上锁.
LOS_DL_LIST_FOR_EACH_ENTRY_SAFE(fpage, fnext, &mapping->page_list, LosFilePage, node) {//遍历文件在内存中产生的所有文件页(例如1,4,8页)不一定连续,取决于用户的读取顺序
lruLock = &fpage->physSeg->lruLock;
LOS_SpinLockSave(lruLock, &lruSave);
if (OsIsPageDirty(fpage->vmPage)) {
ftemp = OsDumpDirtyPage(fpage);
if (ftemp != NULL) {
LOS_ListTailInsert(&dirtyList, &ftemp->node);
LOS_SpinLockSave(lruLock, &lruSave);//@note_why 自旋锁有必要从这里开始上锁吗?
if (OsIsPageDirty(fpage->vmPage)) {//数据是脏页吗,脏页就是被修改过数据的页
ftemp = OsDumpDirtyPage(fpage);//做这个拷贝动作是为了fpage的统一下线,因为数据回写磁盘的速度是很慢的,如果直接在这里处理脏数据
if (ftemp != NULL) {//会导致函数持有mapping->list_lock自旋锁的时间太长了,影响其他CPU的处理效率
LOS_ListTailInsert(&dirtyList, &ftemp->node);//将临时脏页挂到记录脏页链表上
}
}
OsDeletePageCacheLru(fpage);
OsDeletePageCacheLru(fpage);//删除高速缓存和从置换链表中下线
LOS_SpinUnlockRestore(lruLock, lruSave);
}
LOS_SpinUnlockRestore(&mapping->list_lock, intSave);
LOS_SpinUnlockRestore(&mapping->list_lock, intSave);//恢复自旋锁,不能让别的CPU等太久
LOS_DL_LIST_FOR_EACH_ENTRY_SAFE(fpage, fnext, &dirtyList, LosFilePage, node) {
OsDoFlushDirtyPage(fpage);
LOS_DL_LIST_FOR_EACH_ENTRY_SAFE(fpage, fnext, &dirtyList, LosFilePage, node) {//到这里,再来慢慢的统一处理脏页数据
OsDoFlushDirtyPage(fpage);//遍历脏页链表,一页一页处理
}
}
LosVmFileOps g_commVmOps = {
///虚拟内存文件操作实现类
LosVmFileOps g_commVmOps = {//
.open = NULL,
.close = NULL,
.fault = OsVmmFileFault,
.remove = OsVmmFileRemove,
.fault = OsVmmFileFault, //缺页中断处理
.remove = OsVmmFileRemove,//删除页
};
//文件映射
INT32 OsVfsFileMmap(struct file *filep, LosVmMapRegion *region)
{
region->unTypeData.rf.vmFOps = &g_commVmOps;
region->unTypeData.rf.vmFOps = &g_commVmOps;//文件操作
region->unTypeData.rf.vnode = filep->f_vnode;
region->unTypeData.rf.f_oflags = filep->f_oflags;
return ENOERR;
}
/*!
,,
filep广,鸿,//////
*/
STATUS_T OsNamedMMap(struct file *filep, LosVmMapRegion *region)
{
struct Vnode *vnode = NULL;
@ -519,10 +545,10 @@ STATUS_T OsNamedMMap(struct file *filep, LosVmMapRegion *region)
vnode->useCount++;
VnodeDrop();
if (filep->ops != NULL && filep->ops->mmap != NULL) {
if (vnode->type == VNODE_TYPE_CHR || vnode->type == VNODE_TYPE_BLK) {
LOS_SetRegionTypeDev(region);
if (vnode->type == VNODE_TYPE_CHR || vnode->type == VNODE_TYPE_BLK) {//块设备或者字符设备 /dev/..
LOS_SetRegionTypeDev(region);//设置为设备类型
} else {
LOS_SetRegionTypeFile(region);
LOS_SetRegionTypeFile(region);//设置为文件类型
}
int ret = filep->ops->mmap(filep, region);
if (ret != LOS_OK) {
@ -538,17 +564,21 @@ STATUS_T OsNamedMMap(struct file *filep, LosVmMapRegion *region)
return LOS_OK;
}
/**************************************************************************************************
:mapping->page_list 1,3,4,6 ,5
**************************************************************************************************/
LosFilePage *OsFindGetEntry(struct page_mapping *mapping, VM_OFFSET_T pgoff)
{
LosFilePage *fpage = NULL;
LOS_DL_LIST_FOR_EACH_ENTRY(fpage, &mapping->page_list, LosFilePage, node) {
if (fpage->pgoff == pgoff) {
LOS_DL_LIST_FOR_EACH_ENTRY(fpage, &mapping->page_list, LosFilePage, node) {//遍历文件页
if (fpage->pgoff == pgoff) {//找到指定的页,
return fpage;
}
if (fpage->pgoff > pgoff) {
break;
if (fpage->pgoff > pgoff) {//大于之前还没有找到,说明不在链表中,往后的也不用找了,
break;//因为 mapping->page_list节点上的数据都是按 fpage->pgoff 从小到大的顺序排列的.
}
}
@ -556,6 +586,11 @@ LosFilePage *OsFindGetEntry(struct page_mapping *mapping, VM_OFFSET_T pgoff)
}
/* need mutex & change memory to dma zone. */
/*!
LosFilePage
Direct Memory Access访
"DMA控制器"CPU
*/
LosFilePage *OsPageCacheAlloc(struct page_mapping *mapping, VM_OFFSET_T pgoff)
{
VOID *kvaddr = NULL;
@ -563,39 +598,39 @@ LosFilePage *OsPageCacheAlloc(struct page_mapping *mapping, VM_OFFSET_T pgoff)
LosVmPage *vmPage = NULL;
LosFilePage *fpage = NULL;
vmPage = LOS_PhysPageAlloc();
vmPage = LOS_PhysPageAlloc(); //先分配一个物理页
if (vmPage == NULL) {
VM_ERR("alloc vm page failed");
return NULL;
}
physSeg = OsVmPhysSegGet(vmPage);
kvaddr = OsVmPageToVaddr(vmPage);
physSeg = OsVmPhysSegGet(vmPage);//通过页获取所在seg
kvaddr = OsVmPageToVaddr(vmPage);//获取内核空间的虚拟地址,具体点进去看函数说明,这里一定要理解透彻!
if ((physSeg == NULL) || (kvaddr == NULL)) {
LOS_PhysPageFree(vmPage);
LOS_PhysPageFree(vmPage); //异常情况要释放vmPage
VM_ERR("alloc vm page failed!");
return NULL;
}
fpage = (LosFilePage *)LOS_MemAlloc(m_aucSysMem0, sizeof(LosFilePage));
fpage = (LosFilePage *)LOS_MemAlloc(m_aucSysMem0, sizeof(LosFilePage));//从内存池中分配一个filePage
if (fpage == NULL) {
LOS_PhysPageFree(vmPage);
LOS_PhysPageFree(vmPage); //异常情况要释放vmPage
VM_ERR("Failed to allocate for page!");
return NULL;
}
(VOID)memset_s((VOID *)fpage, sizeof(LosFilePage), 0, sizeof(LosFilePage));
(VOID)memset_s((VOID *)fpage, sizeof(LosFilePage), 0, sizeof(LosFilePage));//调标准库函数 置0
LOS_ListInit(&fpage->i_mmap);
LOS_ListInit(&fpage->node);
LOS_ListInit(&fpage->lru);
fpage->n_maps = 0;
fpage->dirtyOff = PAGE_SIZE;
fpage->dirtyEnd = 0;
fpage->physSeg = physSeg;
fpage->vmPage = vmPage;
fpage->mapping = mapping;
fpage->pgoff = pgoff;
(VOID)memset_s(kvaddr, PAGE_SIZE, 0, PAGE_SIZE);
LOS_ListInit(&fpage->i_mmap); //初始化映射,链表上挂 MapInfo
LOS_ListInit(&fpage->node); //节点初始化
LOS_ListInit(&fpage->lru); //LRU初始化
fpage->n_maps = 0; //映射次数
fpage->dirtyOff = PAGE_SIZE; //默认页尾部,相当于没有脏数据
fpage->dirtyEnd = 0; //脏页结束位置
fpage->physSeg = physSeg; //页框所属段.其中包含了 LRU LIST ==
fpage->vmPage = vmPage; //物理页框
fpage->mapping = mapping; //记录所有文件页映射
fpage->pgoff = pgoff; //将文件切成一页页,页标
(VOID)memset_s(kvaddr, PAGE_SIZE, 0, PAGE_SIZE);//页内数据清0
return fpage;
}

@ -1,3 +1,42 @@
/*
访
访Direct Memory AccessDMA访
CPU
DMA使DMA
DMA
使
DMA使
DMA
使DMA
ISA DMA8DMA7
DMA1616DMA
DMA
"分散-收集"Scatter-gatherDMADMADMA
DRQDMADACKDMADMA
DMA线
DMADMA访
访
DMADMA
访访
Cache-coherent system
Non-coherent systemDMA
DMA
DMA
*/
/*
* Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
* Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved.
@ -36,7 +75,7 @@
#include "los_vm_map.h"
#include "los_memory.h"
///分配DMA空间
VOID *LOS_DmaMemAlloc(DMA_ADDR_T *dmaAddr, size_t size, size_t align, enum DmaMemType type)
{
VOID *kVaddr = NULL;
@ -53,24 +92,24 @@ VOID *LOS_DmaMemAlloc(DMA_ADDR_T *dmaAddr, size_t size, size_t align, enum DmaMe
#ifdef LOSCFG_KERNEL_VM
kVaddr = LOS_KernelMallocAlign(size, align);
#else
kVaddr = LOS_MemAllocAlign(OS_SYS_MEM_ADDR, size, align);
kVaddr = LOS_MemAllocAlign(OS_SYS_MEM_ADDR, size, align);//不走内存池方式, 直接申请物理页
#endif
if (kVaddr == NULL) {
VM_ERR("failed, size = %u, align = %u", size, align);
VM_ERR("failed, size = %u, align = %u", size, align);//从内存池中申请
return NULL;
}
if (dmaAddr != NULL) {
*dmaAddr = (DMA_ADDR_T)LOS_PaddrQuery(kVaddr);
*dmaAddr = (DMA_ADDR_T)LOS_PaddrQuery(kVaddr);//查询物理地址, DMA直接将数据灌到物理地址
}
if (type == DMA_NOCACHE) {
if (type == DMA_NOCACHE) {//无缓存模式 , 计算新的虚拟地址
kVaddr = (VOID *)VMM_TO_UNCACHED_ADDR((UINTPTR)kVaddr);
}
return kVaddr;
}
///释放DMA指针
VOID LOS_DmaMemFree(VOID *vaddr)
{
UINTPTR addr;
@ -79,13 +118,13 @@ VOID LOS_DmaMemFree(VOID *vaddr)
return;
}
addr = (UINTPTR)vaddr;
// 未缓存区
if ((addr >= UNCACHED_VMM_BASE) && (addr < UNCACHED_VMM_BASE + UNCACHED_VMM_SIZE)) {
addr = UNCACHED_TO_VMM_ADDR(addr);
addr = UNCACHED_TO_VMM_ADDR(addr);//转换成未缓存区地址
#ifdef LOSCFG_KERNEL_VM
LOS_KernelFree((VOID *)addr);
#else
LOS_MemFree(OS_SYS_MEM_ADDR, (VOID *)addr);
LOS_MemFree(OS_SYS_MEM_ADDR, (VOID *)addr);//内存池方式释放
#endif
} else if ((addr >= KERNEL_VMM_BASE) && (addr < KERNEL_VMM_BASE + KERNEL_VMM_SIZE)) {
#ifdef LOSCFG_KERNEL_VM

File diff suppressed because it is too large Load Diff

@ -40,26 +40,26 @@
#ifdef LOSCFG_KERNEL_VM
LosVmPage *g_vmPageArray = NULL;
size_t g_vmPageArraySize;
LosVmPage *g_vmPageArray = NULL;//物理页框数组
size_t g_vmPageArraySize;//物理页框大小
//页框初始化
STATIC VOID OsVmPageInit(LosVmPage *page, paddr_t pa, UINT8 segID)
{
LOS_ListInit(&page->node);
page->flags = FILE_PAGE_FREE;
LOS_AtomicSet(&page->refCounts, 0);
page->physAddr = pa;
page->segID = segID;
page->order = VM_LIST_ORDER_MAX;
LOS_ListInit(&page->node);//页节点初始化
page->flags = FILE_PAGE_FREE;//页标签,初始为空闲页
LOS_AtomicSet(&page->refCounts, 0);//引用次数0
page->physAddr = pa;//物理地址
page->segID = segID;//物理地址使用段管理段ID
page->order = VM_LIST_ORDER_MAX;//初始化值,不属于任何块组
page->nPages = 0;
#ifdef LOSCFG_PAGE_TABLE_FINE_LOCK
LOS_SpinInit(&page->lock);
#endif
}
///伙伴算法初始化
STATIC INLINE VOID OsVmPageOrderListInit(LosVmPage *page, size_t nPages)
{
OsVmPhysPagesFreeContiguous(page, nPages);
{//@note_why 此时所有页面 page->order = VM_LIST_ORDER_MAX,能挂入伙伴算法的链表吗?
OsVmPhysPagesFreeContiguous(page, nPages);//释放连续的物理页框
}
#define VMPAGEINIT(page, pa, segID) do { \
@ -68,6 +68,10 @@ STATIC INLINE VOID OsVmPageOrderListInit(LosVmPage *page, size_t nPages)
(pa) += PAGE_SIZE; \
} while (0)
/*!
,
1.g_vmPageArrayLosVmPage,4K.
*/
VOID OsVmPageStartup(VOID)
{
struct VmPhysSeg *seg = NULL;
@ -76,7 +80,7 @@ VOID OsVmPageStartup(VOID)
UINT32 nPage;
INT32 segID;
OsVmPhysAreaSizeAdjust(ROUNDUP((g_vmBootMemBase - KERNEL_ASPACE_BASE), PAGE_SIZE));
OsVmPhysAreaSizeAdjust(ROUNDUP((g_vmBootMemBase - KERNEL_ASPACE_BASE), PAGE_SIZE));//校正 g_physArea size
/*
* Pages getting from OsVmPhysPageNumGet() interface here contain the memory
@ -85,20 +89,20 @@ VOID OsVmPageStartup(VOID)
*/
UINT32 pageNum = OsVmPhysPageNumGet();
nPage = pageNum * PAGE_SIZE / (sizeof(LosVmPage) + PAGE_SIZE);
g_vmPageArraySize = nPage * sizeof(LosVmPage);
g_vmPageArray = (LosVmPage *)OsVmBootMemAlloc(g_vmPageArraySize);
g_vmPageArraySize = nPage * sizeof(LosVmPage);//页表总大小
g_vmPageArray = (LosVmPage *)OsVmBootMemAlloc(g_vmPageArraySize);//实模式下申请内存,此时还没有初始化MMU
OsVmPhysAreaSizeAdjust(ROUNDUP(g_vmPageArraySize, PAGE_SIZE));
OsVmPhysSegAdd();
OsVmPhysInit();
OsVmPhysSegAdd();// 完成对段的初始化
OsVmPhysInit();// 加入空闲链表和设置置换算法,LRU(最近最久未使用)算法
#ifdef LOSCFG_KERNEL_PLIMITS
OsMemLimitSetLimit(pageNum * PAGE_SIZE);
#endif
for (segID = 0; segID < g_vmPhysSegNum; segID++) {
for (segID = 0; segID < g_vmPhysSegNum; segID++) {//遍历物理段,将段切成一页一页
seg = &g_vmPhysSeg[segID];
nPage = seg->size >> PAGE_SHIFT;
nPage = seg->size >> PAGE_SHIFT;//本段总页数
UINT32 count = nPage >> 3; /* 3: 2 ^ 3, nPage / 8, cycle count */
UINT32 left = nPage & 0x7; /* 0x7: nPage % 8, left page */
@ -116,17 +120,17 @@ VOID OsVmPageStartup(VOID)
for (; left > 0; left--) {
VMPAGEINIT(page, pa, segID);
}
OsVmPageOrderListInit(seg->pageBase, nPage);
OsVmPageOrderListInit(seg->pageBase, nPage);//伙伴算法初始化,将所有页加入空闲链表供分配
}
}
///通过物理地址获取页框
LosVmPage *LOS_VmPageGet(PADDR_T paddr)
{
INT32 segID;
LosVmPage *page = NULL;
for (segID = 0; segID < g_vmPhysSegNum; segID++) {
page = OsVmPhysToPage(paddr, segID);
for (segID = 0; segID < g_vmPhysSegNum; segID++) {//物理内存采用段页管理
page = OsVmPhysToPage(paddr, segID);//通过物理地址和段ID找出物理页框
if (page != NULL) {
break;
}

@ -1,3 +1,29 @@
/*!
CPU线
LiteOS-A
4KiB便
LiteOS-A使
-----------------------------------------------------
kernel.bin | heap | page frames
() | () | ()
-----------------------------------------------------
92020
1828256
20KiB4K,592820KiB12KiB
3320121
1010
12KiB3322111
201
1
*/
/*
* Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
* Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved.
@ -41,36 +67,36 @@
#define ONE_PAGE 1
/* Physical memory area array */
STATIC struct VmPhysArea g_physArea[] = {
/* Physical memory area array | 物理内存区数组 */
STATIC struct VmPhysArea g_physArea[] = {///< 这里只有一个区域,即只生成一个段
{
.start = SYS_MEM_BASE,
.size = SYS_MEM_SIZE_DEFAULT,
.start = SYS_MEM_BASE, //整个物理内存基地址,#define SYS_MEM_BASE DDR_MEM_ADDR , 0x80000000
.size = SYS_MEM_SIZE_DEFAULT,//整个物理内存总大小 0x07f00000
},
};
struct VmPhysSeg g_vmPhysSeg[VM_PHYS_SEG_MAX];
INT32 g_vmPhysSegNum = 0;
struct VmPhysSeg g_vmPhysSeg[VM_PHYS_SEG_MAX]; ///< 最大32段
INT32 g_vmPhysSegNum = 0; ///< 段数
/// 获取段数组,全局变量,变量放在 .bbs 区
LosVmPhysSeg *OsGVmPhysSegGet(void)
{
return g_vmPhysSeg;
}
/// 初始化Lru置换链表
STATIC VOID OsVmPhysLruInit(struct VmPhysSeg *seg)
{
INT32 i;
UINT32 intSave;
LOS_SpinInit(&seg->lruLock);
LOS_SpinInit(&seg->lruLock);//初始化自旋锁,自旋锁用于CPU多核同步
LOS_SpinLockSave(&seg->lruLock, &intSave);
for (i = 0; i < VM_NR_LRU_LISTS; i++) {
seg->lruSize[i] = 0;
LOS_ListInit(&seg->lruList[i]);
for (i = 0; i < VM_NR_LRU_LISTS; i++) { //五个双循环链表
seg->lruSize[i] = 0; //记录链表节点数
LOS_ListInit(&seg->lruList[i]); //初始化LRU链表
}
LOS_SpinUnlockRestore(&seg->lruLock, intSave);
}
/// 创建物理段,由区划分转成段管理
STATIC INT32 OsVmPhysSegCreate(paddr_t start, size_t size)
{
struct VmPhysSeg *seg = NULL;
@ -79,8 +105,8 @@ STATIC INT32 OsVmPhysSegCreate(paddr_t start, size_t size)
return -1;
}
seg = &g_vmPhysSeg[g_vmPhysSegNum++];
for (; (seg > g_vmPhysSeg) && ((seg - 1)->start > (start + size)); seg--) {
seg = &g_vmPhysSeg[g_vmPhysSegNum++];//拿到一段数据
for (; (seg > g_vmPhysSeg) && ((seg - 1)->start > (start + size)); seg--) {//定位到合适的段
*seg = *(seg - 1);
}
seg->start = start;
@ -88,21 +114,21 @@ STATIC INT32 OsVmPhysSegCreate(paddr_t start, size_t size)
return 0;
}
/// 添加物理段
VOID OsVmPhysSegAdd(VOID)
{
INT32 i, ret;
LOS_ASSERT(g_vmPhysSegNum < VM_PHYS_SEG_MAX);
for (i = 0; i < (sizeof(g_physArea) / sizeof(g_physArea[0])); i++) {
ret = OsVmPhysSegCreate(g_physArea[i].start, g_physArea[i].size);
for (i = 0; i < (sizeof(g_physArea) / sizeof(g_physArea[0])); i++) {//遍历g_physArea数组
ret = OsVmPhysSegCreate(g_physArea[i].start, g_physArea[i].size);//由区划分转成段管理
if (ret != 0) {
VM_ERR("create phys seg failed");
}
}
}
/// 段区域大小调整
VOID OsVmPhysAreaSizeAdjust(size_t size)
{
/*
@ -113,35 +139,36 @@ VOID OsVmPhysAreaSizeAdjust(size_t size)
g_physArea[0].size -= size;
}
/// 获得物理内存的总页数
UINT32 OsVmPhysPageNumGet(VOID)
{
UINT32 nPages = 0;
INT32 i;
for (i = 0; i < (sizeof(g_physArea) / sizeof(g_physArea[0])); i++) {
nPages += g_physArea[i].size >> PAGE_SHIFT;
nPages += g_physArea[i].size >> PAGE_SHIFT;//右移12位相当于除以4K, 计算出总页数
}
return nPages;
return nPages;//返回所有物理内存总页数
}
/// 初始化空闲链表,分配物理页框使用伙伴算法
STATIC INLINE VOID OsVmPhysFreeListInit(struct VmPhysSeg *seg)
{
int i;
UINT32 intSave;
struct VmFreeList *list = NULL;
LOS_SpinInit(&seg->freeListLock);
LOS_SpinInit(&seg->freeListLock);//初始化用于分配的自旋锁
LOS_SpinLockSave(&seg->freeListLock, &intSave);
for (i = 0; i < VM_LIST_ORDER_MAX; i++) {
list = &seg->freeList[i];
LOS_ListInit(&list->node);
list->listCnt = 0;
for (i = 0; i < VM_LIST_ORDER_MAX; i++) {//遍历伙伴算法空闲块组链表
list = &seg->freeList[i]; //一个个来
LOS_ListInit(&list->node); //LosVmPage.node将挂到list->node上
list->listCnt = 0; //链表上的数量默认0
}
LOS_SpinUnlockRestore(&seg->freeListLock, intSave);
}
/// 物理段初始化
VOID OsVmPhysInit(VOID)
{
struct VmPhysSeg *seg = NULL;
@ -150,13 +177,13 @@ VOID OsVmPhysInit(VOID)
for (i = 0; i < g_vmPhysSegNum; i++) {
seg = &g_vmPhysSeg[i];
seg->pageBase = &g_vmPageArray[nPages];
nPages += seg->size >> PAGE_SHIFT;
OsVmPhysFreeListInit(seg);
OsVmPhysLruInit(seg);
seg->pageBase = &g_vmPageArray[nPages];//记录本段首页物理页框地址
nPages += seg->size >> PAGE_SHIFT;//偏移12位,按4K一页,算出本段总页数
OsVmPhysFreeListInit(seg); //初始化空闲链表,分配页框使用伙伴算法
OsVmPhysLruInit(seg); //初始化LRU置换链表
}
}
/// 将页框挂入空闲链表,分配物理页框从空闲链表里拿
STATIC VOID OsVmPhysFreeListAddUnsafe(LosVmPage *page, UINT8 order)
{
struct VmPhysSeg *seg = NULL;
@ -173,36 +200,44 @@ STATIC VOID OsVmPhysFreeListAddUnsafe(LosVmPage *page, UINT8 order)
LOS_ListTailInsert(&list->node, &page->node);
list->listCnt++;
}
///将物理页框从空闲链表上摘除,见于物理页框被分配的情况
STATIC VOID OsVmPhysFreeListDelUnsafe(LosVmPage *page)
{
struct VmPhysSeg *seg = NULL;
struct VmFreeList *list = NULL;
if ((page->segID >= VM_PHYS_SEG_MAX) || (page->order >= VM_LIST_ORDER_MAX)) {
if ((page->segID >= VM_PHYS_SEG_MAX) || (page->order >= VM_LIST_ORDER_MAX)) {//等于VM_LIST_ORDER_MAX也不行,说明伙伴算法最大支持 2^8的分配
LOS_Panic("The page segment id(%u) or order(%u) is invalid\n", page->segID, page->order);
}
seg = &g_vmPhysSeg[page->segID];
list = &seg->freeList[page->order];
list->listCnt--;
LOS_ListDelete(&page->node);
page->order = VM_LIST_ORDER_MAX;
seg = &g_vmPhysSeg[page->segID]; //找到物理页框对应的段
list = &seg->freeList[page->order]; //根据伙伴算法组序号找到空闲链表
list->listCnt--; //链表节点总数减一
LOS_ListDelete(&page->node); //将自己从链表上摘除
page->order = VM_LIST_ORDER_MAX; //告诉系统物理页框已不在空闲链表上, 用于OsVmPhysPagesSpiltUnsafe的断言
}
/**
* @brief ,,.
* @param page
* @param oldOrder 2^2
* @param newOrder 2^8
* @return STATIC
*/
STATIC VOID OsVmPhysPagesSpiltUnsafe(LosVmPage *page, UINT8 oldOrder, UINT8 newOrder)
{
UINT32 order;
LosVmPage *buddyPage = NULL;
for (order = newOrder; order > oldOrder;) {
order--;
buddyPage = &page[VM_ORDER_TO_PAGES(order)];
LOS_ASSERT(buddyPage->order == VM_LIST_ORDER_MAX);
OsVmPhysFreeListAddUnsafe(buddyPage, order);
for (order = newOrder; order > oldOrder;) {//把肉剁碎的过程,把多余的肉块切成2^7,2^6...标准块,
order--;//越切越小,逐一挂到对应的空闲链表上
buddyPage = &page[VM_ORDER_TO_PAGES(order)];//@note_good 先把多余的肉割出来,这句代码很赞!因为LosVmPage本身是在一个大数组上,page[nPages]可直接定位
LOS_ASSERT(buddyPage->order == VM_LIST_ORDER_MAX);//没挂到伙伴算法对应组块空闲链表上的物理页框的order必须是VM_LIST_ORDER_MAX
OsVmPhysFreeListAddUnsafe(buddyPage, order);//将劈开的节点挂到对应序号的链表上,buddyPage->order = order
}
}
///通过物理地址获取所属参数段的物理页框
LosVmPage *OsVmPhysToPage(paddr_t pa, UINT8 segID)
{
struct VmPhysSeg *seg = NULL;
@ -216,8 +251,8 @@ LosVmPage *OsVmPhysToPage(paddr_t pa, UINT8 segID)
return NULL;
}
offset = pa - seg->start;
return (seg->pageBase + (offset >> PAGE_SHIFT));
offset = pa - seg->start;//得到物理地址的偏移量
return (seg->pageBase + (offset >> PAGE_SHIFT));//得到对应的物理页框
}
LosVmPage *OsVmPaddrToPage(paddr_t paddr)
@ -233,31 +268,37 @@ LosVmPage *OsVmPaddrToPage(paddr_t paddr)
}
return NULL;
}
VOID *OsVmPageToVaddr(LosVmPage *page)
/*!
* @brief page OsArchMmuInit
\n #define SYS_MEM_BASE DDR_MEM_ADDR /* physical memory base 物理地址的起始地址 * /
\n
\n :,
* @param page
* @return VOID*
*/
VOID *OsVmPageToVaddr(LosVmPage *page)//
{
VADDR_T vaddr;
vaddr = KERNEL_ASPACE_BASE + page->physAddr - SYS_MEM_BASE;
return (VOID *)(UINTPTR)vaddr;
vaddr = KERNEL_ASPACE_BASE + page->physAddr - SYS_MEM_BASE;//表示申请的物理地址在物理空间的偏移量等于映射的虚拟地址在内核空间的偏移量
return (VOID *)(UINTPTR)vaddr;//不需要存储映射关系,这简直就是神来之笔,拍案叫绝。@note_good 详见 鸿蒙内核源码分析(页表管理篇)
}
///通过虚拟地址找映射的物理页框
LosVmPage *OsVmVaddrToPage(VOID *ptr)
{
struct VmPhysSeg *seg = NULL;
PADDR_T pa = LOS_PaddrQuery(ptr);
PADDR_T pa = LOS_PaddrQuery(ptr);//通过空间的虚拟地址查询物理地址
UINT32 segID;
for (segID = 0; segID < g_vmPhysSegNum; segID++) {
for (segID = 0; segID < g_vmPhysSegNum; segID++) {//遍历所有段
seg = &g_vmPhysSeg[segID];
if ((pa >= seg->start) && (pa < (seg->start + seg->size))) {
return seg->pageBase + ((pa - seg->start) >> PAGE_SHIFT);
if ((pa >= seg->start) && (pa < (seg->start + seg->size))) {//找到物理地址所在的段
return seg->pageBase + ((pa - seg->start) >> PAGE_SHIFT);//段基地址+页偏移索引 得到虚拟地址经映射所在物理页框
}
}
return NULL;
}
/// 回收一定范围内的页框
STATIC INLINE VOID OsVmRecycleExtraPages(LosVmPage *page, size_t startPage, size_t endPage)
{
if (startPage >= endPage) {
@ -266,7 +307,7 @@ STATIC INLINE VOID OsVmRecycleExtraPages(LosVmPage *page, size_t startPage, size
OsVmPhysPagesFreeContiguous(page, endPage - startPage);
}
/// 大块的物理内存分配
STATIC LosVmPage *OsVmPhysLargeAlloc(struct VmPhysSeg *seg, size_t nPages)
{
struct VmFreeList *list = NULL;
@ -276,11 +317,11 @@ STATIC LosVmPage *OsVmPhysLargeAlloc(struct VmPhysSeg *seg, size_t nPages)
PADDR_T paEnd;
size_t size = nPages << PAGE_SHIFT;
list = &seg->freeList[VM_LIST_ORDER_MAX - 1];
LOS_DL_LIST_FOR_EACH_ENTRY(page, &list->node, LosVmPage, node) {
list = &seg->freeList[VM_LIST_ORDER_MAX - 1];//先找伙伴算法中内存块最大的开撸
LOS_DL_LIST_FOR_EACH_ENTRY(page, &list->node, LosVmPage, node) {//遍历链表
paStart = page->physAddr;
paEnd = paStart + size;
if (paEnd > (seg->start + seg->size)) {
if (paEnd > (seg->start + seg->size)) {//匹配物理地址范围
continue;
}
@ -302,7 +343,7 @@ STATIC LosVmPage *OsVmPhysLargeAlloc(struct VmPhysSeg *seg, size_t nPages)
return NULL;
}
/// 申请物理页并挂在对应的链表上
STATIC LosVmPage *OsVmPhysPagesAlloc(struct VmPhysSeg *seg, size_t nPages)
{
struct VmFreeList *list = NULL;
@ -312,13 +353,13 @@ STATIC LosVmPage *OsVmPhysPagesAlloc(struct VmPhysSeg *seg, size_t nPages)
UINT32 newOrder;
order = OsVmPagesToOrder(nPages);
if (order < VM_LIST_ORDER_MAX) {
for (newOrder = order; newOrder < VM_LIST_ORDER_MAX; newOrder++) {
if (order < VM_LIST_ORDER_MAX) {//按正常的伙伴算法分配
for (newOrder = order; newOrder < VM_LIST_ORDER_MAX; newOrder++) {//从小往大了撸
list = &seg->freeList[newOrder];
if (LOS_ListEmpty(&list->node)) {
continue;
if (LOS_ListEmpty(&list->node)) {//这条链路上没有可分配的物理页框
continue;//继续往大的找
}
page = LOS_DL_LIST_ENTRY(LOS_DL_LIST_FIRST(&list->node), LosVmPage, node);
page = LOS_DL_LIST_ENTRY(LOS_DL_LIST_FIRST(&list->node), LosVmPage, node);//找到了直接返回第一个节点
goto DONE;
}
} else {
@ -339,7 +380,7 @@ DONE:
return page;
}
/// 释放物理页框,所谓释放物理页就是把页挂到空闲链表中
VOID OsVmPhysPagesFree(LosVmPage *page, UINT8 order)
{
paddr_t pa;
@ -349,51 +390,59 @@ VOID OsVmPhysPagesFree(LosVmPage *page, UINT8 order)
return;
}
if (order < VM_LIST_ORDER_MAX - 1) {
pa = VM_PAGE_TO_PHYS(page);
do {
pa ^= VM_ORDER_TO_PHYS(order);
buddyPage = OsVmPhysToPage(pa, page->segID);
if ((buddyPage == NULL) || (buddyPage->order != order)) {
if (order < VM_LIST_ORDER_MAX - 1) {//order[0,7]
pa = VM_PAGE_TO_PHYS(page);//获取物理地址
do {//按位异或
pa ^= VM_ORDER_TO_PHYS(order);//@note_good 注意这里是高位和低位的 ^= ,也就是说跳到order块组物理地址处,此处处理甚妙!
buddyPage = OsVmPhysToPage(pa, page->segID);//通过物理地址拿到页框
if ((buddyPage == NULL) || (buddyPage->order != order)) {//页框所在组块必须要对应
break;
}
OsVmPhysFreeListDelUnsafe(buddyPage);
OsVmPhysFreeListDelUnsafe(buddyPage);//注意buddypage是连续的物理页框 例如order=2时,2^2=4页就是一个块组 |_|_|_|_|
order++;
pa &= ~(VM_ORDER_TO_PHYS(order) - 1);
page = OsVmPhysToPage(pa, page->segID);
} while (order < VM_LIST_ORDER_MAX - 1);
}
OsVmPhysFreeListAddUnsafe(page, order);
OsVmPhysFreeListAddUnsafe(page, order);//伙伴算法 空闲节点增加
}
///连续的释放物理页框, 如果8页连在一块是一起释放的
VOID OsVmPhysPagesFreeContiguous(LosVmPage *page, size_t nPages)
{
paddr_t pa;
UINT32 order;
size_t n;
while (TRUE) {
pa = VM_PAGE_TO_PHYS(page);
order = VM_PHYS_TO_ORDER(pa);
n = VM_ORDER_TO_PAGES(order);
if (n > nPages) {
while (TRUE) {//死循环
pa = VM_PAGE_TO_PHYS(page);//获取页面物理地址
order = VM_PHYS_TO_ORDER(pa);//通过物理地址找到伙伴算法的级别
n = VM_ORDER_TO_PAGES(order);//通过级别找到物理页块 (1<<order),意思是如果order=3就可以释放8个页块
if (n > nPages) {//nPages只剩下小于2^order时退出循环
break;
}
OsVmPhysPagesFree(page, order);
nPages -= n;
page += n;
OsVmPhysPagesFree(page, order);//释放伙伴算法对应块组
nPages -= n;//总页数减少
page += n;//释放的页数增多
}
//举例剩下 7个页框时依次用 2^2 2^1 2^0 方式释放
while (nPages > 0) {
order = LOS_HighBitGet(nPages);
n = VM_ORDER_TO_PAGES(order);
OsVmPhysPagesFree(page, order);
order = LOS_HighBitGet(nPages);//从高到低块组释放
n = VM_ORDER_TO_PAGES(order);//2^order次方
OsVmPhysPagesFree(page, order);//释放块组
nPages -= n;
page += n;
page += n;//相当于page[n]
}
}
/*!
* @brief OsVmPhysPagesGet LosVmPage,
* LosVmPage->nPages
* @param nPages
* @return
*
* @see
*/
STATIC LosVmPage *OsVmPhysPagesGet(size_t nPages)
{
UINT32 intSave;
@ -404,11 +453,11 @@ STATIC LosVmPage *OsVmPhysPagesGet(size_t nPages)
for (segID = 0; segID < g_vmPhysSegNum; segID++) {
seg = &g_vmPhysSeg[segID];
LOS_SpinLockSave(&seg->freeListLock, &intSave);
page = OsVmPhysPagesAlloc(seg, nPages);
if (page != NULL) {
/* the first page of continuous physical addresses holds refCounts */
LOS_AtomicSet(&page->refCounts, 0);
page->nPages = nPages;
page = OsVmPhysPagesAlloc(seg, nPages);//分配指定页数的物理页,nPages需小于伙伴算法一次能分配的最大页数
if (page != NULL) {//分配成功
/* */
LOS_AtomicSet(&page->refCounts, 0);//设置引用次数为0
page->nPages = nPages;//页数
LOS_SpinUnlockRestore(&seg->freeListLock, intSave);
return page;
}
@ -416,7 +465,7 @@ STATIC LosVmPage *OsVmPhysPagesGet(size_t nPages)
}
return NULL;
}
///分配连续的物理页
VOID *LOS_PhysPagesAllocContiguous(size_t nPages)
{
LosVmPage *page = NULL;
@ -424,15 +473,15 @@ VOID *LOS_PhysPagesAllocContiguous(size_t nPages)
if (nPages == 0) {
return NULL;
}
page = OsVmPhysPagesGet(nPages);
//鸿蒙 nPages 不能大于 2^8 次方,即256个页,1M内存,仅限于内核态,用户态不限制分配大小.
page = OsVmPhysPagesGet(nPages);//通过伙伴算法获取物理上连续的页
if (page == NULL) {
return NULL;
}
return OsVmPageToVaddr(page);
return OsVmPageToVaddr(page);//通过物理页找虚拟地址
}
/// 释放指定页数地址连续的物理内存
VOID LOS_PhysPagesFreeContiguous(VOID *ptr, size_t nPages)
{
UINT32 intSave;
@ -443,17 +492,17 @@ VOID LOS_PhysPagesFreeContiguous(VOID *ptr, size_t nPages)
return;
}
page = OsVmVaddrToPage(ptr);
page = OsVmVaddrToPage(ptr);//通过虚拟地址找到页框
if (page == NULL) {
VM_ERR("vm page of ptr(%#x) is null", ptr);
return;
}
page->nPages = 0;
page->nPages = 0;//被分配的页数置为0,表示不被分配
seg = &g_vmPhysSeg[page->segID];
LOS_SpinLockSave(&seg->freeListLock, &intSave);
OsVmPhysPagesFreeContiguous(page, nPages);
OsVmPhysPagesFreeContiguous(page, nPages);//具体释放实现
LOS_SpinUnlockRestore(&seg->freeListLock, intSave);
#ifdef LOSCFG_KERNEL_PLIMITS
@ -468,7 +517,7 @@ PADDR_T OsKVaddrToPaddr(VADDR_T kvaddr)
}
return (kvaddr - KERNEL_ASPACE_BASE + SYS_MEM_BASE);
}
/// 通过物理地址获取内核虚拟地址
VADDR_T *LOS_PaddrToKVaddr(PADDR_T paddr)
{
struct VmPhysSeg *seg = NULL;
@ -484,10 +533,10 @@ VADDR_T *LOS_PaddrToKVaddr(PADDR_T paddr)
return (VADDR_T *)(UINTPTR)(paddr - SYS_MEM_BASE + KERNEL_ASPACE_BASE);
}
}
return (VADDR_T *)(UINTPTR)(paddr - SYS_MEM_BASE + KERNEL_ASPACE_BASE);
//内核
return (VADDR_T *)(UINTPTR)(paddr - SYS_MEM_BASE + KERNEL_ASPACE_BASE);//
}
///释放一个物理页框
VOID LOS_PhysPageFree(LosVmPage *page)
{
UINT32 intSave;
@ -497,12 +546,12 @@ VOID LOS_PhysPageFree(LosVmPage *page)
return;
}
if (LOS_AtomicDecRet(&page->refCounts) <= 0) {
if (LOS_AtomicDecRet(&page->refCounts) <= 0) {//减少引用数后不能小于0
seg = &g_vmPhysSeg[page->segID];
LOS_SpinLockSave(&seg->freeListLock, &intSave);
OsVmPhysPagesFreeContiguous(page, ONE_PAGE);
LOS_AtomicSet(&page->refCounts, 0);
OsVmPhysPagesFreeContiguous(page, ONE_PAGE);//释放一页
LOS_AtomicSet(&page->refCounts, 0);//只要物理内存被释放了,引用数就必须得重置为 0
LOS_SpinUnlockRestore(&seg->freeListLock, intSave);
}
@ -510,12 +559,22 @@ VOID LOS_PhysPageFree(LosVmPage *page)
OsMemLimitMemFree(PAGE_SIZE);
#endif
}
/// 申请一个物理页
LosVmPage *LOS_PhysPageAlloc(VOID)
{
return OsVmPhysPagesGet(ONE_PAGE);
return OsVmPhysPagesGet(ONE_PAGE);//分配一页物理页
}
/*!
* @brief LOS_PhysPagesAlloc nPages,list
\n ,nPages
*
* @param list
* @param nPages
* @return
*
* @see
*/
size_t LOS_PhysPagesAlloc(size_t nPages, LOS_DL_LIST *list)
{
LosVmPage *page = NULL;
@ -526,17 +585,17 @@ size_t LOS_PhysPagesAlloc(size_t nPages, LOS_DL_LIST *list)
}
while (nPages--) {
page = OsVmPhysPagesGet(ONE_PAGE);
page = OsVmPhysPagesGet(ONE_PAGE);//一页一页分配,由伙伴算法分配
if (page == NULL) {
break;
}
LOS_ListTailInsert(list, &page->node);
LOS_ListTailInsert(list, &page->node);//从参数链表list尾部挂入新页面结点
count++;
}
return count;
}
///拷贝共享页面
VOID OsPhysSharePageCopy(PADDR_T oldPaddr, PADDR_T *newPaddr, LosVmPage *newPage)
{
UINT32 intSave;
@ -550,43 +609,43 @@ VOID OsPhysSharePageCopy(PADDR_T oldPaddr, PADDR_T *newPaddr, LosVmPage *newPage
return;
}
oldPage = LOS_VmPageGet(oldPaddr);
oldPage = LOS_VmPageGet(oldPaddr);//由物理地址得到页框
if (oldPage == NULL) {
VM_ERR("invalid oldPaddr %p", oldPaddr);
return;
}
seg = &g_vmPhysSeg[oldPage->segID];
seg = &g_vmPhysSeg[oldPage->segID];//拿到物理段
LOS_SpinLockSave(&seg->freeListLock, &intSave);
if (LOS_AtomicRead(&oldPage->refCounts) == 1) {
*newPaddr = oldPaddr;
} else {
newMem = LOS_PaddrToKVaddr(*newPaddr);
oldMem = LOS_PaddrToKVaddr(oldPaddr);
if (LOS_AtomicRead(&oldPage->refCounts) == 1) {//页面引用次数仅一次,说明只有一个进程在操作
*newPaddr = oldPaddr;//新老指向同一块物理地址
} else {//是个共享内存
newMem = LOS_PaddrToKVaddr(*newPaddr); //新页虚拟地址
oldMem = LOS_PaddrToKVaddr(oldPaddr); //老页虚拟地址
if ((newMem == NULL) || (oldMem == NULL)) {
LOS_SpinUnlockRestore(&seg->freeListLock, intSave);
return;
}
if (memcpy_s(newMem, PAGE_SIZE, oldMem, PAGE_SIZE) != EOK) {
}//请记住,在保护模式下,物理地址只能用于计算,操作(包括拷贝)需要虚拟地址!
if (memcpy_s(newMem, PAGE_SIZE, oldMem, PAGE_SIZE) != EOK) {//老页内容复制给新页,需操作虚拟地址,拷贝一页数据
VM_ERR("memcpy_s failed");
}
LOS_AtomicInc(&newPage->refCounts);
LOS_AtomicDec(&oldPage->refCounts);
LOS_AtomicInc(&newPage->refCounts);//新页引用次数以原子方式自动减量
LOS_AtomicDec(&oldPage->refCounts);//老页引用次数以原子方式自动减量
}
LOS_SpinUnlockRestore(&seg->freeListLock, intSave);
return;
}
///获取物理页框所在段
struct VmPhysSeg *OsVmPhysSegGet(LosVmPage *page)
{
if ((page == NULL) || (page->segID >= VM_PHYS_SEG_MAX)) {
return NULL;
}
return (OsGVmPhysSegGet() + page->segID);
return (OsGVmPhysSegGet() + page->segID);//等用于OsGVmPhysSegGet()[page->segID]
}
///获取参数nPages对应的块组,例如 7 -> 2^3 返回 3
UINT32 OsVmPagesToOrder(size_t nPages)
{
UINT32 order;
@ -595,7 +654,7 @@ UINT32 OsVmPagesToOrder(size_t nPages)
return order;
}
///释放双链表中的所有节点内存,本质是回归到伙伴orderlist中
size_t LOS_PhysPagesFree(LOS_DL_LIST *list)
{
UINT32 intSave;
@ -608,16 +667,16 @@ size_t LOS_PhysPagesFree(LOS_DL_LIST *list)
return 0;
}
LOS_DL_LIST_FOR_EACH_ENTRY_SAFE(page, nPage, list, LosVmPage, node) {
LOS_ListDelete(&page->node);
if (LOS_AtomicDecRet(&page->refCounts) <= 0) {
seg = &g_vmPhysSeg[page->segID];
LOS_SpinLockSave(&seg->freeListLock, &intSave);
OsVmPhysPagesFreeContiguous(page, ONE_PAGE);
LOS_AtomicSet(&page->refCounts, 0);
LOS_SpinUnlockRestore(&seg->freeListLock, intSave);
LOS_DL_LIST_FOR_EACH_ENTRY_SAFE(page, nPage, list, LosVmPage, node) {//宏循环
LOS_ListDelete(&page->node);//先把自己摘出去
if (LOS_AtomicDecRet(&page->refCounts) <= 0) {//无引用
seg = &g_vmPhysSeg[page->segID];//获取物理段
LOS_SpinLockSave(&seg->freeListLock, &intSave);//锁住freeList
OsVmPhysPagesFreeContiguous(page, ONE_PAGE);//连续释放,注意这里的ONE_PAGE其实有误导,让人以为是释放4K,其实是指连续的物理页框,如果3页连在一块是一起释放的.
LOS_AtomicSet(&page->refCounts, 0);//引用重置为0
LOS_SpinUnlockRestore(&seg->freeListLock, intSave);//恢复锁
}
count++;
count++;//继续取下一个node
}
return count;
@ -632,4 +691,3 @@ VADDR_T *LOS_PaddrToKVaddr(PADDR_T paddr)
return (VADDR_T *)DMA_TO_VMM_ADDR(paddr);
}
#endif

@ -37,6 +37,10 @@
#ifdef LOSCFG_KERNEL_VM
/* unmap a lru page by map record info caller need lru lock */
/**************************************************************************************************
(mmu)
infoMMU
**************************************************************************************************/
VOID OsUnmapPageLocked(LosFilePage *page, LosMapInfo *info)
{
if (page == NULL || info == NULL) {
@ -47,88 +51,88 @@ VOID OsUnmapPageLocked(LosFilePage *page, LosMapInfo *info)
LOS_ListDelete(&info->node);
LOS_AtomicDec(&page->vmPage->refCounts);
LOS_ArchMmuUnmap(info->archMmu, info->vaddr, 1);
LOS_MemFree(m_aucSysMem0, info);
LOS_MemFree(m_aucSysMem0, info);//释放虚拟
}
///解除文件页在所有进程的映射
VOID OsUnmapAllLocked(LosFilePage *page)
{
LosMapInfo *info = NULL;
LosMapInfo *next = NULL;
LOS_DL_LIST *immap = &page->i_mmap;
LOS_DL_LIST_FOR_EACH_ENTRY_SAFE(info, next, immap, LosMapInfo, node) {
LOS_DL_LIST_FOR_EACH_ENTRY_SAFE(info, next, immap, LosMapInfo, node) {//遍历 immap->info 链表
OsUnmapPageLocked(page, info);
}
}
/* add a new lru node to lru list, lruType can be file or anon */
VOID OsLruCacheAdd(LosFilePage *fpage, enum OsLruList lruType)
VOID OsLruCacheAdd(LosFilePage *fpage, enum OsLruList lruType)//在lru列表中添加一个新的lru节点lruType可以是文件或匿名
{
UINT32 intSave;
LosVmPhysSeg *physSeg = fpage->physSeg;
LosVmPage *page = fpage->vmPage;
LosVmPhysSeg *physSeg = fpage->physSeg; //得到页面对应段
LosVmPage *page = fpage->vmPage; //得到物理页面
LOS_SpinLockSave(&physSeg->lruLock, &intSave);
OsSetPageActive(page);
OsCleanPageReferenced(page);
physSeg->lruSize[lruType]++;
LOS_ListTailInsert(&physSeg->lruList[lruType], &fpage->lru);
LOS_SpinLockSave(&physSeg->lruLock, &intSave);//自旋锁:最多只能被一个内核持有CPU内核 互斥锁
OsSetPageActive(page); //设置页面为活动页
OsCleanPageReferenced(page);//清除页面被引用位
physSeg->lruSize[lruType]++; //lruType页总size++
LOS_ListTailInsert(&physSeg->lruList[lruType], &fpage->lru);//加入lruType页双循环链表中
LOS_SpinUnlockRestore(&physSeg->lruLock, intSave);
LOS_SpinUnlockRestore(&physSeg->lruLock, intSave);//解锁
}
/* delete a lru node, caller need hold lru_lock */
VOID OsLruCacheDel(LosFilePage *fpage)
/* dellete a lru node, caller need hold lru_lock */
VOID OsLruCacheDel(LosFilePage *fpage)//删除lru节点调用者需要拿到lru锁
{
LosVmPhysSeg *physSeg = fpage->physSeg;
int type = OsIsPageActive(fpage->vmPage) ? VM_LRU_ACTIVE_FILE : VM_LRU_INACTIVE_FILE;
LosVmPhysSeg *physSeg = fpage->physSeg; //得到页面对应段
int type = OsIsPageActive(fpage->vmPage) ? VM_LRU_ACTIVE_FILE : VM_LRU_INACTIVE_FILE;//得到页面LRU类型
physSeg->lruSize[type]--;
LOS_ListDelete(&fpage->lru);
physSeg->lruSize[type]--; //type页总size--
LOS_ListDelete(&fpage->lru);//将自己从lru链表中摘出来
}
///非活动文件页低于活动文件页吗
BOOL OsInactiveListIsLow(LosVmPhysSeg *physSeg)
{
return (physSeg->lruSize[VM_LRU_ACTIVE_FILE] >
physSeg->lruSize[VM_LRU_INACTIVE_FILE]) ? TRUE : FALSE;
physSeg->lruSize[VM_LRU_INACTIVE_FILE]) ? TRUE : FALSE;//直接对比size效率杠杠的
}
/* move a page from inactive list to active list head */
STATIC INLINE VOID OsMoveToActiveList(LosFilePage *fpage)
STATIC INLINE VOID OsMoveToActiveList(LosFilePage *fpage)//将页面从非活动列表移动到活动列表
{
LosVmPhysSeg *physSeg = fpage->physSeg;
LosVmPhysSeg *physSeg = fpage->physSeg; //得到页面对应段
physSeg->lruSize[VM_LRU_ACTIVE_FILE]++;
physSeg->lruSize[VM_LRU_INACTIVE_FILE]--;
LOS_ListDelete(&fpage->lru);
LOS_ListTailInsert(&physSeg->lruList[VM_LRU_ACTIVE_FILE], &fpage->lru);
physSeg->lruSize[VM_LRU_ACTIVE_FILE]++; //活动页总size++
physSeg->lruSize[VM_LRU_INACTIVE_FILE]--; //不活动页总size--
LOS_ListDelete(&fpage->lru); //将自己从lru链表中摘出来
LOS_ListTailInsert(&physSeg->lruList[VM_LRU_ACTIVE_FILE], &fpage->lru);//加入活动页双循环链表中
}
/* move a page from active list to inactive list head */
STATIC INLINE VOID OsMoveToInactiveList(LosFilePage *fpage)
STATIC INLINE VOID OsMoveToInactiveList(LosFilePage *fpage)//将页面从活动列表移动到非活动列表
{
LosVmPhysSeg *physSeg = fpage->physSeg;
LosVmPhysSeg *physSeg = fpage->physSeg; //得到页面对应段
physSeg->lruSize[VM_LRU_ACTIVE_FILE]--;
physSeg->lruSize[VM_LRU_INACTIVE_FILE]++;
LOS_ListDelete(&fpage->lru);
LOS_ListTailInsert(&physSeg->lruList[VM_LRU_INACTIVE_FILE], &fpage->lru);
physSeg->lruSize[VM_LRU_ACTIVE_FILE]--; //活动页总size--
physSeg->lruSize[VM_LRU_INACTIVE_FILE]++; //不活动页总size++
LOS_ListDelete(&fpage->lru); //将自己从lru链表中摘出来
LOS_ListTailInsert(&physSeg->lruList[VM_LRU_INACTIVE_FILE], &fpage->lru);//加入不活动页双循环链表中
}
/* move a page to the most active pos in lru list(active head) */
/* move a page to the most active pos in lru list(active head) *///将页面移至lru列表中最活跃的位置
STATIC INLINE VOID OsMoveToActiveHead(LosFilePage *fpage)
{
LosVmPhysSeg *physSeg = fpage->physSeg;
LOS_ListDelete(&fpage->lru);
LOS_ListTailInsert(&physSeg->lruList[VM_LRU_ACTIVE_FILE], &fpage->lru);
LosVmPhysSeg *physSeg = fpage->physSeg; //得到页面对应段
LOS_ListDelete(&fpage->lru); //将自己从lru链表中摘出来
LOS_ListTailInsert(&physSeg->lruList[VM_LRU_ACTIVE_FILE], &fpage->lru);//加入活动页双循环链表中
}
/* move a page to the most active pos in lru list(inactive head) */
STATIC INLINE VOID OsMoveToInactiveHead(LosFilePage *fpage)
STATIC INLINE VOID OsMoveToInactiveHead(LosFilePage *fpage)//鸿蒙会从inactive链表的尾部开始进行回收,跟linux一样
{
LosVmPhysSeg *physSeg = fpage->physSeg;
LOS_ListDelete(&fpage->lru);
LOS_ListTailInsert(&physSeg->lruList[VM_LRU_INACTIVE_FILE], &fpage->lru);
LosVmPhysSeg *physSeg = fpage->physSeg; //得到页面对应段
LOS_ListDelete(&fpage->lru); //将自己从lru链表中摘出来
LOS_ListTailInsert(&physSeg->lruList[VM_LRU_INACTIVE_FILE], &fpage->lru);//加入不活动页双循环链表中
}
/* page referced add: (call by page cache get)
@ -138,7 +142,7 @@ ref:0, act:0 --> ref:1, act:0
ref:1, act:0 --> ref:0, act:1
ref:0, act:1 --> ref:1, act:1
*/
VOID OsPageRefIncLocked(LosFilePage *fpage)
VOID OsPageRefIncLocked(LosFilePage *fpage)// ref ,act 标签转换功能
{
BOOL isOrgActive;
UINT32 intSave;
@ -148,16 +152,16 @@ VOID OsPageRefIncLocked(LosFilePage *fpage)
return;
}
LOS_SpinLockSave(&fpage->physSeg->lruLock, &intSave);
LOS_SpinLockSave(&fpage->physSeg->lruLock, &intSave);//要处理lruList,先拿锁
page = fpage->vmPage;
isOrgActive = OsIsPageActive(page);
page = fpage->vmPage;//拿到物理页框
isOrgActive = OsIsPageActive(page);//页面是否在活动
if (OsIsPageReferenced(page) && !OsIsPageActive(page)) {
OsCleanPageReferenced(page);
OsSetPageActive(page);
if (OsIsPageReferenced(page) && !OsIsPageActive(page)) {//身兼 不活动和引用标签
OsCleanPageReferenced(page);//撕掉引用标签 ref:1, act:0 --> ref:0, act:1
OsSetPageActive(page); //贴上活动标签
} else if (!OsIsPageReferenced(page)) {
OsSetPageReferenced(page);
OsSetPageReferenced(page);//ref:0, act:0 --> ref:1, act:0
}
if (!isOrgActive && OsIsPageActive(page)) {
@ -175,14 +179,14 @@ VOID OsPageRefIncLocked(LosFilePage *fpage)
LOS_SpinUnlockRestore(&fpage->physSeg->lruLock, intSave);
}
/* page referced dec: (call by shrinker)
/* page referced dec: (call by thrinker)
----------inactive----------|----------active------------
[ref:0,act:0], [ref:1,act:0]|[ref:0,act:1], [ref:1,act:1]
ref:1, act:1 --> ref:0, act:1
ref:0, act:1 --> ref:1, act:0
ref:1, act:0 --> ref:0, act:0
*/
VOID OsPageRefDecNoLock(LosFilePage *fpage)
VOID OsPageRefDecNoLock(LosFilePage *fpage) // ref ,act 标签转换功能
{
BOOL isOrgActive;
LosVmPage *page = NULL;
@ -194,7 +198,7 @@ VOID OsPageRefDecNoLock(LosFilePage *fpage)
page = fpage->vmPage;
isOrgActive = OsIsPageActive(page);
if (!OsIsPageReferenced(page) && OsIsPageActive(page)) {
if (!OsIsPageReferenced(page) && OsIsPageActive(page)) {//[ref:0,act:1]的情况
OsCleanPageActive(page);
OsSetPageReferenced(page);
} else if (OsIsPageReferenced(page)) {
@ -205,39 +209,39 @@ VOID OsPageRefDecNoLock(LosFilePage *fpage)
OsMoveToInactiveList(fpage);
}
}
///缩小活动页链表
VOID OsShrinkActiveList(LosVmPhysSeg *physSeg, int nScan)
{
LosFilePage *fpage = NULL;
LosFilePage *fnext = NULL;
LOS_DL_LIST *activeFile = &physSeg->lruList[VM_LRU_ACTIVE_FILE];
LOS_DL_LIST_FOR_EACH_ENTRY_SAFE(fpage, fnext, activeFile, LosFilePage, lru) {
if (LOS_SpinTrylock(&fpage->mapping->list_lock) != LOS_OK) {
continue;
LOS_DL_LIST_FOR_EACH_ENTRY_SAFE(fpage, fnext, activeFile, LosFilePage, lru) {//一页一页处理
if (LOS_SpinTrylock(&fpage->mapping->list_lock) != LOS_OK) {//尝试获取文件页所在的page_mapping锁
continue;//接着处理下一文件页
}
/* happened when caller hold cache lock and try reclaim this page */
if (OsIsPageLocked(fpage->vmPage)) {
LOS_SpinUnlock(&fpage->mapping->list_lock);
continue;
/* happend when caller hold cache lock and try reclaim this page *///调用方持有缓存锁并尝试回收此页时发生
if (OsIsPageLocked(fpage->vmPage)) {//页面是否被锁
LOS_SpinUnlock(&fpage->mapping->list_lock);//失败时,一定要释放page_mapping锁.
continue;//接着处理下一文件页
}
if (OsIsPageMapped(fpage) && (fpage->flags & VM_MAP_REGION_FLAG_PERM_EXECUTE)) {
LOS_SpinUnlock(&fpage->mapping->list_lock);
continue;
if (OsIsPageMapped(fpage) && (fpage->flags & VM_MAP_REGION_FLAG_PERM_EXECUTE)) {//文件页是否被映射而且是个可执行文件 ?
LOS_SpinUnlock(&fpage->mapping->list_lock);//是时,一定要释放page_mapping锁.
continue;//接着处理下一文件页
}
//找了可以收缩的文件页
OsPageRefDecNoLock(fpage); //将页面移到未活动文件链表
OsPageRefDecNoLock(fpage);
LOS_SpinUnlock(&fpage->mapping->list_lock);
LOS_SpinUnlock(&fpage->mapping->list_lock); //释放page_mapping锁.
if (--nScan <= 0) {
break;
}
}
}
///缩小未活动页链表
int OsShrinkInactiveList(LosVmPhysSeg *physSeg, int nScan, LOS_DL_LIST *list)
{
UINT32 nrReclaimed = 0;
@ -248,36 +252,36 @@ int OsShrinkInactiveList(LosVmPhysSeg *physSeg, int nScan, LOS_DL_LIST *list)
LosFilePage *ftemp = NULL;
LOS_DL_LIST *inactive_file = &physSeg->lruList[VM_LRU_INACTIVE_FILE];
LOS_DL_LIST_FOR_EACH_ENTRY_SAFE(fpage, fnext, inactive_file, LosFilePage, lru) {
LOS_DL_LIST_FOR_EACH_ENTRY_SAFE(fpage, fnext, inactive_file, LosFilePage, lru) {//遍历链表一页一页处理
flock = &fpage->mapping->list_lock;
if (LOS_SpinTrylock(flock) != LOS_OK) {
continue;
if (LOS_SpinTrylock(flock) != LOS_OK) {//尝试获取文件页所在的page_mapping锁
continue;//接着处理下一文件页
}
page = fpage->vmPage;
if (OsIsPageLocked(page)) {
page = fpage->vmPage;//获取物理页框
if (OsIsPageLocked(page)) {//页面是否被锁
LOS_SpinUnlock(flock);
continue;
continue;//接着处理下一文件页
}
if (OsIsPageMapped(fpage) && (OsIsPageDirty(page) || (fpage->flags & VM_MAP_REGION_FLAG_PERM_EXECUTE))) {
LOS_SpinUnlock(flock);
continue;
LOS_SpinUnlock(flock);//文件页是否被映射而且是个脏页获取是个可执行文件 ?
continue;//接着处理下一文件页
}
if (OsIsPageDirty(page)) {
ftemp = OsDumpDirtyPage(fpage);
if (ftemp != NULL) {
LOS_ListTailInsert(list, &ftemp->node);
if (OsIsPageDirty(page)) {//是脏页
ftemp = OsDumpDirtyPage(fpage);//备份脏页
if (ftemp != NULL) {//备份成功了
LOS_ListTailInsert(list, &ftemp->node);//将脏页挂到参数链表上带走
}
}
OsDeletePageCacheLru(fpage);
OsDeletePageCacheLru(fpage);//将文件页从LRU和pagecache上摘除
LOS_SpinUnlock(flock);
nrReclaimed++;
nrReclaimed++;//成功回收了一页
if (--nScan <= 0) {
if (--nScan <= 0) {//继续回收
break;
}
}
@ -286,48 +290,48 @@ int OsShrinkInactiveList(LosVmPhysSeg *physSeg, int nScan, LOS_DL_LIST *list)
}
#ifdef LOSCFG_FS_VFS
int OsTryShrinkMemory(size_t nPage)
int OsTryShrinkMemory(size_t nPage)//尝试收缩文件页
{
UINT32 intSave;
size_t totalPages;
size_t nReclaimed = 0;
LosVmPhysSeg *physSeg = NULL;
UINT32 index;
LOS_DL_LIST_HEAD(dirtyList);
LOS_DL_LIST_HEAD(dirtyList);//初始化脏页链表,上面将挂所有脏页用于同步到磁盘后回收
LosFilePage *fpage = NULL;
LosFilePage *fnext = NULL;
if (nPage == 0) {
nPage = VM_FILEMAP_MIN_SCAN;
nPage = VM_FILEMAP_MIN_SCAN;//
}
if (nPage > VM_FILEMAP_MAX_SCAN) {
nPage = VM_FILEMAP_MAX_SCAN;
}
for (index = 0; index < g_vmPhysSegNum; index++) {
physSeg = &g_vmPhysSeg[index];
for (index = 0; index < g_vmPhysSegNum; index++) {//遍历整个物理段组
physSeg = &g_vmPhysSeg[index];//一段段来
LOS_SpinLockSave(&physSeg->lruLock, &intSave);
totalPages = physSeg->lruSize[VM_LRU_ACTIVE_FILE] + physSeg->lruSize[VM_LRU_INACTIVE_FILE];
if (totalPages < VM_FILEMAP_MIN_SCAN) {
totalPages = physSeg->lruSize[VM_LRU_ACTIVE_FILE] + physSeg->lruSize[VM_LRU_INACTIVE_FILE];//统计所有文件页
if (totalPages < VM_FILEMAP_MIN_SCAN) {//文件页占用内存不多的情况下,怎么处理?
LOS_SpinUnlockRestore(&physSeg->lruLock, intSave);
continue;
continue;//放过这一段,找下一段
}
if (OsInactiveListIsLow(physSeg)) {
OsShrinkActiveList(physSeg, (nPage < VM_FILEMAP_MIN_SCAN) ? VM_FILEMAP_MIN_SCAN : nPage);
OsShrinkActiveList(physSeg, (nPage < VM_FILEMAP_MIN_SCAN) ? VM_FILEMAP_MIN_SCAN : nPage);//缩小活动页
}
nReclaimed += OsShrinkInactiveList(physSeg, nPage, &dirtyList);
nReclaimed += OsShrinkInactiveList(physSeg, nPage, &dirtyList);//缩小未活动页,带出脏页链表
LOS_SpinUnlockRestore(&physSeg->lruLock, intSave);
if (nReclaimed >= nPage) {
break;
if (nReclaimed >= nPage) {//够了,够了,达到目的了.
break;//退出收缩
}
}
LOS_DL_LIST_FOR_EACH_ENTRY_SAFE(fpage, fnext, &dirtyList, LosFilePage, node) {
OsDoFlushDirtyPage(fpage);
LOS_DL_LIST_FOR_EACH_ENTRY_SAFE(fpage, fnext, &dirtyList, LosFilePage, node) {//遍历处理脏页数据
OsDoFlushDirtyPage(fpage);//冲洗脏页数据,将脏页数据回写磁盘
}
return nReclaimed;

@ -65,7 +65,7 @@ STATUS_T OsCheckMMapParams(VADDR_T *vaddr, unsigned long flags, size_t len, unsi
return -EINVAL;
}
if ((flags & MAP_SUPPORT_MASK) == 0) {
if ((flags & MAP_SUPPORT_MASK) == 0) {//映射权限限制
return -EINVAL;
}
if (((flags & MAP_SHARED_PRIVATE) == 0) || ((flags & MAP_SHARED_PRIVATE) == MAP_SHARED_PRIVATE)) {
@ -95,20 +95,54 @@ STATUS_T OsNamedMmapingPermCheck(struct file *filep, unsigned long flags, unsign
return LOS_OK;
}
///匿名映射
STATUS_T OsAnonMMap(LosVmMapRegion *region)
{
LOS_SetRegionTypeAnon(region);
return LOS_OK;
}
/**
mmap:
.
read,write
https://www.cnblogs.com/huxiao-tee/p/4660352.html
http://abcdxyzk.github.io/blog/2015/09/11/kernel-mm-mmap/
addr NULL
length
prot 访
PROT_EXEC
PROT_READ
PROT_WRITE
PROT_NONE
flags
MAP_FIXED start
MAP_SHARED
MAP_PRIVATE copy on write
MAP_ANONYMOUSfd
MAP_DENYWRITE
MAP_LOCKED swap
fd: 使flagsMAP_ANONYMOUSfd-1
使fopen/dev/zero
offset 0offsetPAGE_SIZE
(void *)-1
*/
VADDR_T LOS_MMap(VADDR_T vaddr, size_t len, unsigned prot, unsigned long flags, int fd, unsigned long pgoff)
{
STATUS_T status;
VADDR_T resultVaddr;
UINT32 regionFlags;
LosVmMapRegion *newRegion = NULL;
struct file *filep = NULL;
LosVmMapRegion *newRegion = NULL;//应用的内存分配对应到内核就是分配一个线性区
struct file *filep = NULL;// inode : file = 1:N ,一对多关系,一个inode可以被多个进程打开,返回不同的file但都指向同一个inode
LosVmSpace *vmSpace = OsCurrProcessGet()->vmSpace;
len = ROUNDUP(len, PAGE_SIZE);
@ -117,8 +151,8 @@ VADDR_T LOS_MMap(VADDR_T vaddr, size_t len, unsigned prot, unsigned long flags,
return checkRst;
}
if (LOS_IsNamedMapping(flags)) {
status = fs_getfilep(fd, &filep);
if (LOS_IsNamedMapping(flags)) {//是否文件映射
status = fs_getfilep(fd, &filep);//获取文件描述符和状态
if (status < 0) {
return -EBADF;
}
@ -131,30 +165,30 @@ VADDR_T LOS_MMap(VADDR_T vaddr, size_t len, unsigned prot, unsigned long flags,
(VOID)LOS_MuxAcquire(&vmSpace->regionMux);
/* user mode calls mmap to release heap physical memory without releasing heap virtual space */
status = OsUserHeapFree(vmSpace, vaddr, len);
if (status == LOS_OK) {
status = OsUserHeapFree(vmSpace, vaddr, len);//用户模式释放堆物理内存而不释放堆虚拟空间
if (status == LOS_OK) {//OsUserHeapFree 干两件事 1.解除映射关系 2.释放物理页
resultVaddr = vaddr;
goto MMAP_DONE;
}
regionFlags = OsCvtProtFlagsToRegionFlags(prot, flags);
newRegion = LOS_RegionAlloc(vmSpace, vaddr, len, regionFlags, pgoff);
//地址不在堆区
regionFlags = OsCvtProtFlagsToRegionFlags(prot, flags);//将参数flag转换Region的flag
newRegion = LOS_RegionAlloc(vmSpace, vaddr, len, regionFlags, pgoff);//分配一个线性区
if (newRegion == NULL) {
resultVaddr = (VADDR_T)-ENOMEM;
resultVaddr = (VADDR_T)-ENOMEM;//ENOMEM:内存溢出
goto MMAP_DONE;
}
newRegion->regionFlags |= VM_MAP_REGION_FLAG_MMAP;
resultVaddr = newRegion->range.base;
resultVaddr = newRegion->range.base;//线性区基地址为分配的地址
if (LOS_IsNamedMapping(flags)) {
status = OsNamedMMap(filep, newRegion);
status = OsNamedMMap(filep, newRegion);//文件映射
} else {
status = OsAnonMMap(newRegion);
status = OsAnonMMap(newRegion);//匿名映射
}
if (status != LOS_OK) {
LOS_RbDelNode(&vmSpace->regionRbTree, &newRegion->rbNode);
LOS_RegionFree(vmSpace, newRegion);
LOS_RbDelNode(&vmSpace->regionRbTree, &newRegion->rbNode);//从红黑树和双循环链表中删除
LOS_RegionFree(vmSpace, newRegion);//释放
resultVaddr = (VADDR_T)-ENOMEM;
goto MMAP_DONE;
}
@ -163,7 +197,7 @@ MMAP_DONE:
(VOID)LOS_MuxRelease(&vmSpace->regionMux);
return resultVaddr;
}
///解除映射关系
STATUS_T LOS_UnMMap(VADDR_T addr, size_t size)
{
if ((addr <= 0) || (size == 0)) {
@ -172,7 +206,6 @@ STATUS_T LOS_UnMMap(VADDR_T addr, size_t size)
return OsUnMMap(OsCurrProcessGet()->vmSpace, addr, size);
}
STATIC INLINE BOOL OsProtMprotectPermCheck(unsigned long prot, LosVmMapRegion *region)
{
UINT32 protFlags = 0;
@ -186,20 +219,28 @@ STATIC INLINE BOOL OsProtMprotectPermCheck(unsigned long prot, LosVmMapRegion *r
return ((protFlags & permFlags) == protFlags);
}
/// 收缩堆区
VOID *OsShrinkHeap(VOID *addr, LosVmSpace *space)
{
VADDR_T newBrk, oldBrk;
newBrk = LOS_Align((VADDR_T)(UINTPTR)addr, PAGE_SIZE);
oldBrk = LOS_Align(space->heapNow, PAGE_SIZE);
if (LOS_UnMMap(newBrk, (oldBrk - newBrk)) < 0) {
return (void *)(UINTPTR)space->heapNow;
newBrk = LOS_Align((VADDR_T)(UINTPTR)addr, PAGE_SIZE);//新堆顶
oldBrk = LOS_Align(space->heapNow, PAGE_SIZE);//旧堆顶
if (LOS_UnMMap(newBrk, (oldBrk - newBrk)) < 0) {//解除相差区的映射
return (void *)(UINTPTR)space->heapNow;//解除失败就持续现有的
}
space->heapNow = (VADDR_T)(UINTPTR)addr;
space->heapNow = (VADDR_T)(UINTPTR)addr;//返回新堆顶
return addr;
}
/**
线线
线
线线线
线
*/
VOID *LOS_DoBrk(VOID *addr)
{
LosVmSpace *space = OsCurrProcessGet()->vmSpace;
@ -209,60 +250,60 @@ VOID *LOS_DoBrk(VOID *addr)
VOID *alignAddr = NULL;
VOID *shrinkAddr = NULL;
if (addr == NULL) {
return (void *)(UINTPTR)space->heapNow;
if (addr == NULL) {//参数地址未传情况
return (void *)(UINTPTR)space->heapNow;//以现有指向地址为基础进行扩展
}
if ((UINTPTR)addr < (UINTPTR)space->heapBase) {
if ((UINTPTR)addr < (UINTPTR)space->heapBase) {//heapBase是堆区的开始地址所以参数地址不能低于它
return (VOID *)-ENOMEM;
}
size = (UINTPTR)addr - (UINTPTR)space->heapBase;
size = ROUNDUP(size, PAGE_SIZE);
alignAddr = (CHAR *)(UINTPTR)(space->heapBase) + size;
size = (UINTPTR)addr - (UINTPTR)space->heapBase;//算出大小
size = ROUNDUP(size, PAGE_SIZE); //圆整size
alignAddr = (CHAR *)(UINTPTR)(space->heapBase) + size;//得到新的线性区的结束地址
PRINT_INFO("brk addr %p , size 0x%x, alignAddr %p, align %d\n", addr, size, alignAddr, PAGE_SIZE);
(VOID)LOS_MuxAcquire(&space->regionMux);
if (addr < (VOID *)(UINTPTR)space->heapNow) {
shrinkAddr = OsShrinkHeap(addr, space);
if (addr < (VOID *)(UINTPTR)space->heapNow) {//如果地址小于堆区现地址
shrinkAddr = OsShrinkHeap(addr, space);//收缩堆区
(VOID)LOS_MuxRelease(&space->regionMux);
return shrinkAddr;
}
if ((UINTPTR)alignAddr >= space->mapBase) {
VM_ERR("Process heap memory space is insufficient");
if ((UINTPTR)alignAddr >= space->mapBase) {//参数地址 大于映射区地址
VM_ERR("Process heap memory space is insufficient");//进程堆空间不足
ret = (VOID *)-ENOMEM;
goto REGION_ALLOC_FAILED;
}
if (space->heapBase == space->heapNow) {
region = LOS_RegionAlloc(space, space->heapBase, size,
VM_MAP_REGION_FLAG_PERM_READ | VM_MAP_REGION_FLAG_PERM_WRITE |
if (space->heapBase == space->heapNow) {//往往是第一次调用本函数才会出现,因为初始化时 heapBase = heapNow
region = LOS_RegionAlloc(space, space->heapBase, size,//分配一个可读/可写/可使用的线性区,只需分配一次
VM_MAP_REGION_FLAG_PERM_READ | VM_MAP_REGION_FLAG_PERM_WRITE |//线性区的大小由range.size决定
VM_MAP_REGION_FLAG_FIXED | VM_MAP_REGION_FLAG_PERM_USER, 0);
if (region == NULL) {
ret = (VOID *)-ENOMEM;
VM_ERR("LOS_RegionAlloc failed");
goto REGION_ALLOC_FAILED;
}
region->regionFlags |= VM_MAP_REGION_FLAG_HEAP;
space->heap = region;
region->regionFlags |= VM_MAP_REGION_FLAG_HEAP;//贴上线性区类型为堆区的标签,注意一个线性区可以有多种标签
space->heap = region;//指定线性区为堆区
}
space->heapNow = (VADDR_T)(UINTPTR)alignAddr;
space->heap->range.size = size;
ret = (VOID *)(UINTPTR)space->heapNow;
space->heapNow = (VADDR_T)(UINTPTR)alignAddr;//更新堆区顶部位置
space->heap->range.size = size; //更新堆区大小,经此操作线性区变大或缩小了
ret = (VOID *)(UINTPTR)space->heapNow;//返回堆顶
REGION_ALLOC_FAILED:
(VOID)LOS_MuxRelease(&space->regionMux);
return ret;
}
/// 继承老线性区的标签
STATIC UINT32 OsInheritOldRegionName(UINT32 oldRegionFlags)
{
UINT32 vmFlags = 0;
if (oldRegionFlags & VM_MAP_REGION_FLAG_HEAP) {
vmFlags |= VM_MAP_REGION_FLAG_HEAP;
if (oldRegionFlags & VM_MAP_REGION_FLAG_HEAP) { //如果是从大堆区中申请的
vmFlags |= VM_MAP_REGION_FLAG_HEAP; //线性区则贴上堆区标签
} else if (oldRegionFlags & VM_MAP_REGION_FLAG_STACK) {
vmFlags |= VM_MAP_REGION_FLAG_STACK;
} else if (oldRegionFlags & VM_MAP_REGION_FLAG_TEXT) {
@ -277,7 +318,7 @@ STATIC UINT32 OsInheritOldRegionName(UINT32 oldRegionFlags)
return vmFlags;
}
///修改内存段的访问权限
INT32 LOS_DoMprotect(VADDR_T vaddr, size_t len, unsigned long prot)
{
LosVmSpace *space = OsCurrProcessGet()->vmSpace;
@ -287,7 +328,7 @@ INT32 LOS_DoMprotect(VADDR_T vaddr, size_t len, unsigned long prot)
int ret;
(VOID)LOS_MuxAcquire(&space->regionMux);
region = LOS_RegionFind(space, vaddr);
region = LOS_RegionFind(space, vaddr);//通过虚拟地址找到线性区
if (!IS_ALIGNED(vaddr, PAGE_SIZE) || (region == NULL) || (vaddr > vaddr + len)) {
ret = -EINVAL;
goto OUT_MPROTECT;
@ -297,19 +338,18 @@ INT32 LOS_DoMprotect(VADDR_T vaddr, size_t len, unsigned long prot)
ret = -EINVAL;
goto OUT_MPROTECT;
}
//如果是堆区或VDSO区,说明区内容是不能修改的
if ((region->regionFlags & VM_MAP_REGION_FLAG_VDSO) || (region->regionFlags & VM_MAP_REGION_FLAG_HEAP)) {
ret = -EPERM;
goto OUT_MPROTECT;
}
//如果是共享文件,说明内容也不能修改
if (LOS_IsRegionTypeFile(region) && (region->regionFlags & VM_MAP_REGION_FLAG_SHARED)) {
if (!OsProtMprotectPermCheck(prot, region)) {
ret = -EACCES;
goto OUT_MPROTECT;
}
}
len = LOS_Align(len, PAGE_SIZE);
/* can't operation cross region */
if ((region->range.base + region->range.size) < (vaddr + len)) {
@ -318,11 +358,11 @@ INT32 LOS_DoMprotect(VADDR_T vaddr, size_t len, unsigned long prot)
}
/* if only move some part of region, we need to split first */
if (region->range.size > len) {
OsVmRegionAdjust(space, vaddr, len);
if (region->range.size > len) {//如果只修改部分区域,我们需要先拆分区
OsVmRegionAdjust(space, vaddr, len);//调整下线性区范围
}
vmFlags = OsCvtProtFlagsToRegionFlags(prot, 0);
vmFlags = OsCvtProtFlagsToRegionFlags(prot, 0);//转换FLAGS
vmFlags |= (region->regionFlags & VM_MAP_REGION_FLAG_SHARED) ? VM_MAP_REGION_FLAG_SHARED : 0;
vmFlags |= OsInheritOldRegionName(region->regionFlags);
region = LOS_RegionFind(space, vaddr);
@ -332,7 +372,7 @@ INT32 LOS_DoMprotect(VADDR_T vaddr, size_t len, unsigned long prot)
}
region->regionFlags = vmFlags;
count = len >> PAGE_SHIFT;
ret = LOS_ArchMmuChangeProt(&space->archMmu, vaddr, count, region->regionFlags);
ret = LOS_ArchMmuChangeProt(&space->archMmu, vaddr, count, region->regionFlags);//修改访问权限实体函数
if (ret) {
ret = -ENOMEM;
goto OUT_MPROTECT;
@ -387,7 +427,7 @@ STATUS_T OsMremapCheck(VADDR_T addr, size_t oldLen, VADDR_T newAddr, size_t newL
}
}
/* avoid new region overlapping with the old one */
/* avoid new region overlaping with the old one */
if (flags & MREMAP_FIXED) {
if (((region->range.base + region->range.size) > newAddr) &&
(region->range.base < (newAddr + newLen))) {
@ -401,7 +441,7 @@ STATUS_T OsMremapCheck(VADDR_T addr, size_t oldLen, VADDR_T newAddr, size_t newL
return LOS_OK;
}
///重新映射虚拟内存地址。
VADDR_T LOS_DoMremap(VADDR_T oldAddress, size_t oldSize, size_t newSize, int flags, VADDR_T newAddr)
{
LosVmMapRegion *regionOld = NULL;
@ -496,7 +536,7 @@ OUT_MREMAP:
(VOID)LOS_MuxRelease(&space->regionMux);
return ret;
}
///输出内存线性区
VOID LOS_DumpMemRegion(VADDR_T vaddr)
{
LosVmSpace *space = NULL;
@ -506,12 +546,11 @@ VOID LOS_DumpMemRegion(VADDR_T vaddr)
return;
}
if (LOS_IsRangeInSpace(space, ROUNDDOWN(vaddr, MB), MB) == FALSE) {
if (LOS_IsRangeInSpace(space, ROUNDDOWN(vaddr, MB), MB) == FALSE) {//是否在空间范围内
return;
}
OsDumpPte(vaddr);
OsDumpAspace(space);
OsDumpPte(vaddr);//dump L1 L2
OsDumpAspace(space);//dump 空间
}
#endif

@ -47,8 +47,8 @@
#ifdef LOSCFG_KERNEL_VM
LITE_OS_SEC_BSS OomCB *g_oomCB = NULL;
static SPIN_LOCK_INIT(g_oomSpinLock);
LITE_OS_SEC_BSS OomCB *g_oomCB = NULL; //全局内存溢出控制块
static SPIN_LOCK_INIT(g_oomSpinLock);//内存溢出自旋锁
LITE_OS_SEC_TEXT_MINOR STATIC UINT32 OomScoreProcess(LosProcessCB *candidateProcess)
{
@ -57,20 +57,20 @@ LITE_OS_SEC_TEXT_MINOR STATIC UINT32 OomScoreProcess(LosProcessCB *candidateProc
#ifndef LOSCFG_KERNEL_SMP
(VOID)LOS_MuxAcquire(&candidateProcess->vmSpace->regionMux);
#endif
/* we only consider actual physical memory here. */
/* we only consider actual physical memory here. */ //只考虑实际的物理内存
OsUProcessPmUsage(candidateProcess->vmSpace, NULL, &actualPm);
#ifndef LOSCFG_KERNEL_SMP
(VOID)LOS_MuxRelease(&candidateProcess->vmSpace->regionMux);
#endif
return actualPm;
}
///用于设置 g_oomCB->processVictimCB 回调函数
LITE_OS_SEC_TEXT_MINOR STATIC UINT32 OomKillProcess(UINTPTR param)
{
/* we will not kill process, and do nothing here */
return LOS_OK;
}
///强制收缩内存
LITE_OS_SEC_TEXT_MINOR STATIC UINT32 OomForceShrinkMemory(VOID)
{
UINT32 i;
@ -80,13 +80,14 @@ LITE_OS_SEC_TEXT_MINOR STATIC UINT32 OomForceShrinkMemory(VOID)
* TryShrinkMemory maybe reclaim 0 pages in the first time from active list
* to inactive list, and in the second time reclaim memory from inactive list.
*/
//TryShrinkMemory可能会在第一时间从活动列表中回收0页到非活动列表并在第二次从非活动列表中回收内存。
for (i = 0; i < MAX_SHRINK_PAGECACHE_TRY; i++) {
reclaimMemPages += OsTryShrinkMemory(0);
}
return reclaimMemPages;
}
///内存不足时回收页高速缓存
LITE_OS_SEC_TEXT_MINOR STATIC BOOL OomReclaimPageCache(VOID)
{
UINT32 totalPm = 0;
@ -96,43 +97,44 @@ LITE_OS_SEC_TEXT_MINOR STATIC BOOL OomReclaimPageCache(VOID)
UINT32 i;
for (i = 0; i < MAX_SHRINK_PAGECACHE_TRY; i++) {
OsVmPhysUsedInfoGet(&usedPm, &totalPm);
isReclaimMemory = ((totalPm - usedPm) << PAGE_SHIFT) < g_oomCB->reclaimMemThreshold;
if (isReclaimMemory) {
OsVmPhysUsedInfoGet(&usedPm, &totalPm);//获取总的和已经使用的物理内存数量
isReclaimMemory = ((totalPm - usedPm) << PAGE_SHIFT) < g_oomCB->reclaimMemThreshold;//检查是否过了回收门槛
if (isReclaimMemory) {//要回收了
/*
* we do force memory reclaim from page cache here.
* if we get memory, we will reclaim pagecache memory again.
* if there is no memory to reclaim, we will return.
*/
reclaimMemPages = OomForceShrinkMemory();
if (reclaimMemPages > 0) {
//在这里强制从页缓存中回收内存,
reclaimMemPages = OomForceShrinkMemory();//强制回收内存
if (reclaimMemPages > 0) {//如果得到内存将再次回收pagecache内存
continue;
}
}
break;
break;//实在没有内存可回收
}
return isReclaimMemory;
return isReclaimMemory;//返回回收的数量
}
/*
* check is low memory or not, if low memory, try to kill process.
* return is kill process or not.
*/
LITE_OS_SEC_TEXT_MINOR BOOL OomCheckProcess(VOID)
LITE_OS_SEC_TEXT_MINOR BOOL OomCheckProcess(VOID)//检查内存是否不足,如果内存不足,请尝试终止进程,返回是否kill进程
{
UINT32 totalPm;
UINT32 usedPm;
BOOL isLowMemory = FALSE;
/*
* spinlock the current core schedule, make sure oom process atomic
* spinlock other place entering OomCheckProcess, make sure oom process mutex
* spinlock the current core schedule, make sure oom process atomic //旋转锁定当前核心计划确保oom进程原子化
* spinlock other place entering OomCheckProcess, make sure oom process mutex //旋转锁定其他进入OomCheckProcess的地方确保oom进程互斥
*/
LOS_SpinLock(&g_oomSpinLock);
/* first we will check if we need to reclaim pagecache memory */
if (OomReclaimPageCache() == FALSE) {
if (OomReclaimPageCache() == FALSE) {//
LOS_SpinUnlock(&g_oomSpinLock);
goto NO_VICTIM_PROCESS;
}
@ -140,9 +142,7 @@ LITE_OS_SEC_TEXT_MINOR BOOL OomCheckProcess(VOID)
/* get free bytes */
OsVmPhysUsedInfoGet(&usedPm, &totalPm);
isLowMemory = ((totalPm - usedPm) << PAGE_SHIFT) < g_oomCB->lowMemThreshold;
LOS_SpinUnlock(&g_oomSpinLock);
if (isLowMemory) {
PRINTK("[oom] OS is in low memory state\n"
"total physical memory: %#x(byte), used: %#x(byte),"
@ -155,14 +155,14 @@ NO_VICTIM_PROCESS:
return isLowMemory;
}
#ifdef LOSCFG_ENABLE_OOM_LOOP_TASK
STATIC VOID OomWriteEvent(VOID)
#ifdef LOSCFG_ENABLE_OOM_LOOP_TASK //内存溢出监测任务开关
STATIC VOID OomWriteEvent(VOID) // OomTaskInit中创建的定时器回调
{
OsWriteResourceEvent(OS_RESOURCE_EVENT_OOM);
OsWriteResourceEvent(OS_RESOURCE_EVENT_OOM);//广播内存溢出事件
}
#endif
LITE_OS_SEC_TEXT_MINOR VOID OomInfodump(VOID)
//打印内存不足时的信息
LITE_OS_SEC_TEXT_MINOR VOID OomInfodump(VOID) //打印内存溢出信息
{
PRINTK("[oom] oom loop task status: %s\n"
" oom low memory threshold: %#x(byte)\n"
@ -172,7 +172,7 @@ LITE_OS_SEC_TEXT_MINOR VOID OomInfodump(VOID)
g_oomCB->lowMemThreshold, g_oomCB->reclaimMemThreshold,
g_oomCB->checkInterval);
}
///设置低内存门槛
LITE_OS_SEC_TEXT_MINOR VOID OomSetLowMemThreashold(UINT32 lowMemThreshold)
{
if ((lowMemThreshold > OOM_DEFAULT_LOW_MEM_THRESHOLD_MAX)) {
@ -186,7 +186,7 @@ LITE_OS_SEC_TEXT_MINOR VOID OomSetLowMemThreashold(UINT32 lowMemThreshold)
g_oomCB->lowMemThreshold);
}
}
///设置回收内存的门槛
LITE_OS_SEC_TEXT_MINOR VOID OomSetReclaimMemThreashold(UINT32 reclaimMemThreshold)
{
UINT32 totalPm = 0;
@ -204,7 +204,7 @@ LITE_OS_SEC_TEXT_MINOR VOID OomSetReclaimMemThreashold(UINT32 reclaimMemThreshol
g_oomCB->reclaimMemThreshold);
}
}
///设置监控间隔
LITE_OS_SEC_TEXT_MINOR VOID OomSetCheckInterval(UINT32 checkInterval)
{
if ((checkInterval >= OOM_CHECK_MIN) && (checkInterval <= OOM_CHECK_MAX)) {
@ -216,7 +216,7 @@ LITE_OS_SEC_TEXT_MINOR VOID OomSetCheckInterval(UINT32 checkInterval)
g_oomCB->checkInterval, OOM_CHECK_MIN, OOM_CHECK_MAX);
}
}
///内存不足监控任务初始化, OOM 通过开一个软件定时器来检查内存的使用情况
LITE_OS_SEC_TEXT_MINOR UINT32 OomTaskInit(VOID)
{
g_oomCB = (OomCB *)LOS_MemAlloc(m_aucSysMem0, sizeof(OomCB));
@ -225,28 +225,27 @@ LITE_OS_SEC_TEXT_MINOR UINT32 OomTaskInit(VOID)
return LOS_NOK;
}
g_oomCB->lowMemThreshold = OOM_DEFAULT_LOW_MEM_THRESHOLD;
g_oomCB->reclaimMemThreshold = OOM_DEFAULT_RECLAIM_MEM_THRESHOLD;
g_oomCB->checkInterval = OOM_DEFAULT_CHECK_INTERVAL;
g_oomCB->processVictimCB = (OomFn)OomKillProcess;
g_oomCB->scoreCB = (OomFn)OomScoreProcess;
g_oomCB->enabled = FALSE;
g_oomCB->lowMemThreshold = OOM_DEFAULT_LOW_MEM_THRESHOLD; //运行任务的门槛
g_oomCB->reclaimMemThreshold = OOM_DEFAULT_RECLAIM_MEM_THRESHOLD; //回收内存的门槛
g_oomCB->checkInterval = OOM_DEFAULT_CHECK_INTERVAL; //检测时间间隔 1S
g_oomCB->processVictimCB = (OomFn)OomKillProcess; //出问题时对进程的处理函数
g_oomCB->scoreCB = (OomFn)OomScoreProcess; //统计进程占用的物理内存
g_oomCB->enabled = FALSE; //是否启用监控
#ifdef LOSCFG_ENABLE_OOM_LOOP_TASK
#ifdef LOSCFG_ENABLE_OOM_LOOP_TASK //内存溢出检测开关
g_oomCB->enabled = TRUE;
UINT32 ret = LOS_SwtmrCreate(g_oomCB->checkInterval, LOS_SWTMR_MODE_PERIOD, (SWTMR_PROC_FUNC)OomWriteEvent,
&g_oomCB->swtmrID, (UINTPTR)g_oomCB);
&g_oomCB->swtmrID, (UINTPTR)g_oomCB);//创建检测定时器
if (ret != LOS_OK) {
return ret;
}
return LOS_SwtmrStart(g_oomCB->swtmrID);
return LOS_SwtmrStart(g_oomCB->swtmrID);//启动定时器
#else
return LOS_OK;
#endif
}
LOS_MODULE_INIT(OomTaskInit, LOS_INIT_LEVEL_KMOD_TASK);
LOS_MODULE_INIT(OomTaskInit, LOS_INIT_LEVEL_KMOD_TASK);//初始化内存监控模块
#endif

@ -1,3 +1,17 @@
/*!
访
访C
malloc()访
访
线,使线.
*/
/*
* Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
* Copyright (c) 2020-2023 Huawei Device Co., Ltd. All rights reserved.
@ -55,9 +69,9 @@
#ifdef LOSCFG_KERNEL_SHM
#define SHM_SEG_FREE 0x2000
#define SHM_SEG_USED 0x4000
#define SHM_SEG_REMOVE 0x8000
#define SHM_SEG_FREE 0x2000 //空闲未使用
#define SHM_SEG_USED 0x4000 //已使用
#define SHM_SEG_REMOVE 0x8000 //删除
#ifndef SHM_M
#define SHM_M 010000
@ -66,21 +80,17 @@
#ifndef SHM_X
#define SHM_X 0100
#endif
#ifndef ACCESSPERMS
#define ACCESSPERMS (S_IRWXU | S_IRWXG | S_IRWXO)
#endif
#define ACCESSPERMS (S_IRWXU | S_IRWXG | S_IRWXO)//文件权限值意思就是 用户,用户组,其他可读可写.
#endif //代表含义U:user G:group O:other
#define SHM_S_IRUGO (S_IRUSR | S_IRGRP | S_IROTH)
#define SHM_S_IWUGO (S_IWUSR | S_IWGRP | S_IWOTH)
#define SHM_S_IXUGO (S_IXUSR | S_IXGRP | S_IXOTH)
#define SHM_GROUPE_TO_USER 3
#define SHM_OTHER_TO_USER 6
#ifndef LOSCFG_IPC_CONTAINER
STATIC LosMux g_sysvShmMux;
/* private data */
STATIC struct shminfo g_shmInfo;
STATIC struct shmIDSource *g_shmSegs = NULL;
@ -96,6 +106,60 @@ STATIC UINT32 g_shmUsedPageCount;
#define SYSV_SHM_LOCK() (VOID)LOS_MuxLock(&IPC_SHM_SYS_VSHM_MUTEX, LOS_WAIT_FOREVER)
#define SYSV_SHM_UNLOCK() (VOID)LOS_MuxUnlock(&IPC_SHM_SYS_VSHM_MUTEX)
#if 0 // @note_#if0
//内核为每一个IPC对象保存一个ipc_perm结构体该结构说明了IPC对象的权限和所有者
struct ipc_perm {
key_t __ipc_perm_key; //调用shmget()时给出的关键字
uid_t uid; //共享内存所有者的有效用户ID
gid_t gid; //共享内存所有者所属组的有效组ID
uid_t cuid; //共享内存创建 者的有效用户ID
gid_t cgid; //共享内存创建者所属组的有效组ID
mode_t mode; //权限 + SHM_DEST / SHM_LOCKED /SHM_HUGETLB 标志位
int __ipc_perm_seq; //序列号
long __pad1; //保留扩展用
long __pad2;
};
//每个共享内存段在内核中维护着一个内部结构shmid_ds
struct shmid_ds {
struct ipc_perm shm_perm;///< 操作许可里面包含共享内存的用户ID、组ID等信息
size_t shm_segsz; ///< 共享内存段的大小,单位为字节
time_t shm_atime; ///< 最后一个进程访问共享内存的时间
time_t shm_dtime; ///< 最后一个进程离开共享内存的时间
time_t shm_ctime; ///< 创建时间
pid_t shm_cpid; ///< 创建共享内存的进程ID
pid_t shm_lpid; ///< 最后操作共享内存的进程ID
unsigned long shm_nattch; ///< 当前使用该共享内存段的进程数量
unsigned long __pad1; //保留扩展用
unsigned long __pad2;
};
// 共享内存模块设置信息
struct shminfo {
unsigned long shmmax, shmmin, shmmni, shmseg, shmall, __unused[4];
};
struct shmIDSource {//共享内存描述符
struct shmid_ds ds; //是内核为每一个共享内存段维护的数据结构
UINT32 status; //状态 SHM_SEG_FREE ...
LOS_DL_LIST node; //节点,挂VmPage
#ifdef LOSCFG_SHELL
CHAR ownerName[OS_PCB_NAME_LEN];
#endif
};
/* private data */
STATIC struct shminfo g_shmInfo = { //描述共享内存范围的全局变量
.shmmax = SHM_MAX,//共享内存单个上限 4096页 即 16M
.shmmin = SHM_MIN,//共享内存单个下限 1页 即:4K
.shmmni = SHM_MNI,//共享内存总数 默认192
.shmseg = SHM_SEG,//每个用户进程可以使用的最多的共享内存段的数目 128
.shmall = SHM_ALL,//系统范围内共享内存的总页数, 4096页
};
STATIC struct shmIDSource *g_shmSegs = NULL;
STATIC UINT32 g_shmUsedPageCount;
#endif
//共享内存初始化
struct shmIDSource *OsShmCBInit(LosMux *sysvShmMux, struct shminfo *shmInfo, UINT32 *shmUsedPageCount)
{
UINT32 ret;
@ -104,7 +168,6 @@ struct shmIDSource *OsShmCBInit(LosMux *sysvShmMux, struct shminfo *shmInfo, UIN
if ((sysvShmMux == NULL) || (shmInfo == NULL) || (shmUsedPageCount == NULL)) {
return NULL;
}
ret = LOS_MuxInit(sysvShmMux, NULL);
if (ret != LOS_OK) {
goto ERROR;
@ -115,7 +178,6 @@ struct shmIDSource *OsShmCBInit(LosMux *sysvShmMux, struct shminfo *shmInfo, UIN
shmInfo->shmmni = SHM_MNI;
shmInfo->shmseg = SHM_SEG;
shmInfo->shmall = SHM_ALL;
struct shmIDSource *shmSegs = LOS_MemAlloc((VOID *)OS_SYS_MEM_ADDR, sizeof(struct shmIDSource) * shmInfo->shmmni);
if (shmSegs == NULL) {
(VOID)LOS_MuxDestroy(sysvShmMux);
@ -125,9 +187,9 @@ struct shmIDSource *OsShmCBInit(LosMux *sysvShmMux, struct shminfo *shmInfo, UIN
0, (sizeof(struct shmIDSource) * shmInfo->shmmni));
for (i = 0; i < shmInfo->shmmni; i++) {
shmSegs[i].status = SHM_SEG_FREE;
shmSegs[i].ds.shm_perm.seq = i + 1;
LOS_ListInit(&shmSegs[i].node);
shmSegs[i].status = SHM_SEG_FREE;//节点初始状态为空闲
shmSegs[i].ds.shm_perm.seq = i + 1;//struct ipc_perm shm_perm;系统为每一个IPC对象保存一个ipc_perm结构体,结构说明了IPC对象的权限和所有者
LOS_ListInit(&shmSegs[i].node);//初始化节点
}
*shmUsedPageCount = 0;
@ -137,7 +199,6 @@ ERROR:
VM_ERR("ShmInit fail\n");
return NULL;
}
UINT32 ShmInit(VOID)
{
#ifndef LOSCFG_IPC_CONTAINER
@ -149,8 +210,9 @@ UINT32 ShmInit(VOID)
return LOS_OK;
}
LOS_MODULE_INIT(ShmInit, LOS_INIT_LEVEL_VM_COMPLETE);
LOS_MODULE_INIT(ShmInit, LOS_INIT_LEVEL_VM_COMPLETE);//共享内存模块初始化
//共享内存反初始化
UINT32 ShmDeinit(VOID)
{
UINT32 ret;
@ -165,7 +227,7 @@ UINT32 ShmDeinit(VOID)
return 0;
}
///给共享段中所有物理页框贴上共享标签
STATIC inline VOID ShmSetSharedFlag(struct shmIDSource *seg)
{
LosVmPage *page = NULL;
@ -174,7 +236,7 @@ STATIC inline VOID ShmSetSharedFlag(struct shmIDSource *seg)
OsSetPageShared(page);
}
}
///给共享段中所有物理页框撕掉共享标签
STATIC inline VOID ShmClearSharedFlag(struct shmIDSource *seg)
{
LosVmPage *page = NULL;
@ -183,7 +245,7 @@ STATIC inline VOID ShmClearSharedFlag(struct shmIDSource *seg)
OsCleanPageShared(page);
}
}
///seg下所有共享页引用减少
STATIC VOID ShmPagesRefDec(struct shmIDSource *seg)
{
LosVmPage *page = NULL;
@ -193,6 +255,15 @@ STATIC VOID ShmPagesRefDec(struct shmIDSource *seg)
}
}
/**
* @brief
:size = 4097, LOS_Align(size, PAGE_SIZE) = 8192
size >> PAGE_SHIFT = 2
* @param key
* @param size
* @param shmflg
* @return STATIC
*/
STATIC INT32 ShmAllocSegCheck(key_t key, size_t *size, INT32 *segNum)
{
INT32 i;
@ -201,7 +272,7 @@ STATIC INT32 ShmAllocSegCheck(key_t key, size_t *size, INT32 *segNum)
return -EINVAL;
}
*size = LOS_Align(*size, PAGE_SIZE);
*size = LOS_Align(*size, PAGE_SIZE);//必须对齐
if ((IPC_SHM_USED_PAGE_COUNT + (*size >> PAGE_SHIFT)) > IPC_SHM_INFO.shmall) {
return -ENOMEM;
}
@ -211,11 +282,10 @@ STATIC INT32 ShmAllocSegCheck(key_t key, size_t *size, INT32 *segNum)
return -ENOMEM;
}
#endif
for (i = 0; i < IPC_SHM_INFO.shmmni; i++) {
if (IPC_SHM_SEGS[i].status & SHM_SEG_FREE) {
IPC_SHM_SEGS[i].status &= ~SHM_SEG_FREE;
*segNum = i;
for (i = 0; i < IPC_SHM_INFO.shmmni; i++) {//试图找到一个空闲段与参数key绑定
if (IPC_SHM_SEGS[i].status & SHM_SEG_FREE) {//找到空闲段
IPC_SHM_SEGS[i].status &= ~SHM_SEG_FREE;//变成非空闲状态
*segNum = i;//标号
break;
}
}
@ -236,49 +306,47 @@ STATIC INT32 ShmAllocSeg(key_t key, size_t size, INT32 shmflg)
if (ret < 0) {
return ret;
}
seg = &IPC_SHM_SEGS[segNum];
count = LOS_PhysPagesAlloc(size >> PAGE_SHIFT, &seg->node);
if (count != (size >> PAGE_SHIFT)) {
(VOID)LOS_PhysPagesFree(&seg->node);
seg->status = SHM_SEG_FREE;
count = LOS_PhysPagesAlloc(size >> PAGE_SHIFT, &seg->node);//分配共享页面,函数内部把node都挂好了.
if (count != (size >> PAGE_SHIFT)) {//当未分配到足够的内存时,处理方式是:不稀罕给那么点,舍弃!
(VOID)LOS_PhysPagesFree(&seg->node);//释放节点上的物理页框
seg->status = SHM_SEG_FREE;//共享段变回空闲状态
#ifdef LOSCFG_KERNEL_IPC_PLIMIT
OsIPCLimitShmFree(size);
#endif
return -ENOMEM;
}
ShmSetSharedFlag(seg);
ShmSetSharedFlag(seg);//将node的每个页面设置为共享页
IPC_SHM_USED_PAGE_COUNT += size >> PAGE_SHIFT;
seg->status |= SHM_SEG_USED;
seg->status |= SHM_SEG_USED; //共享段贴上已在使用的标签
seg->ds.shm_perm.mode = (UINT32)shmflg & ACCESSPERMS;
seg->ds.shm_perm.key = key;
seg->ds.shm_segsz = size;
seg->ds.shm_perm.cuid = LOS_GetUserID();
seg->ds.shm_perm.uid = LOS_GetUserID();
seg->ds.shm_perm.cgid = LOS_GetGroupID();
seg->ds.shm_perm.gid = LOS_GetGroupID();
seg->ds.shm_lpid = 0;
seg->ds.shm_nattch = 0;
seg->ds.shm_cpid = LOS_GetCurrProcessID();
seg->ds.shm_atime = 0;
seg->ds.shm_dtime = 0;
seg->ds.shm_ctime = time(NULL);
seg->ds.shm_perm.key = key;//保存参数key,如此 key 和 共享ID绑定在一块
seg->ds.shm_segsz = size; //共享段的大小
seg->ds.shm_perm.cuid = LOS_GetUserID(); //设置用户ID
seg->ds.shm_perm.uid = LOS_GetUserID(); //设置用户ID
seg->ds.shm_perm.cgid = LOS_GetGroupID(); //设置组ID
seg->ds.shm_perm.gid = LOS_GetGroupID(); //设置组ID
seg->ds.shm_lpid = 0; //最后一个操作的进程
seg->ds.shm_nattch = 0; //绑定进程的数量
seg->ds.shm_cpid = LOS_GetCurrProcessID(); //获取进程ID
seg->ds.shm_atime = 0; //访问时间
seg->ds.shm_dtime = 0; //detach 分离时间 共享内存使用完之后,需要将它从进程地址空间中分离出来;将共享内存分离并不是删除它,只是使该共享内存对当前的进程不再可用
seg->ds.shm_ctime = time(NULL);//创建时间
#ifdef LOSCFG_SHELL
(VOID)memcpy_s(seg->ownerName, OS_PCB_NAME_LEN, OsCurrProcessGet()->processName, OS_PCB_NAME_LEN);
#endif
return segNum;
}
///释放seg->node 所占物理页框,seg本身重置
STATIC INLINE VOID ShmFreeSeg(struct shmIDSource *seg, UINT32 *shmUsedPageCount)
{
UINT32 count;
ShmClearSharedFlag(seg);
count = LOS_PhysPagesFree(&seg->node);
if (count != (seg->ds.shm_segsz >> PAGE_SHIFT)) {
ShmClearSharedFlag(seg);//先撕掉 seg->node 中vmpage的共享标签
count = LOS_PhysPagesFree(&seg->node);//再挨个删除物理页框
if (count != (seg->ds.shm_segsz >> PAGE_SHIFT)) {//异常,必须要一样
VM_ERR("free physical pages failed, count = %d, size = %d", count, seg->ds.shm_segsz >> PAGE_SHIFT);
return;
}
@ -288,31 +356,31 @@ STATIC INLINE VOID ShmFreeSeg(struct shmIDSource *seg, UINT32 *shmUsedPageCount)
if (shmUsedPageCount != NULL) {
(*shmUsedPageCount) -= seg->ds.shm_segsz >> PAGE_SHIFT;
}
seg->status = SHM_SEG_FREE;
LOS_ListInit(&seg->node);
seg->status = SHM_SEG_FREE;//seg恢复自由之身
LOS_ListInit(&seg->node);//重置node
}
///通过key查找 shmId
STATIC INT32 ShmFindSegByKey(key_t key)
{
INT32 i;
struct shmIDSource *seg = NULL;
for (i = 0; i < IPC_SHM_INFO.shmmni; i++) {
for (i = 0; i < IPC_SHM_INFO.shmmni; i++) {//遍历共享段池,找到与key绑定的共享ID
seg = &IPC_SHM_SEGS[i];
if ((seg->status & SHM_SEG_USED) &&
(seg->ds.shm_perm.key == key)) {
(seg->ds.shm_perm.key == key)) {//满足两个条件,找到后返回
return i;
}
}
return -1;
}
///共享内存段有效性检查
STATIC INT32 ShmSegValidCheck(INT32 segNum, size_t size, INT32 shmFlg)
{
struct shmIDSource *seg = &IPC_SHM_SEGS[segNum];
struct shmIDSource *seg = &IPC_SHM_SEGS[segNum];//拿到shmID
if (size > seg->ds.shm_segsz) {
if (size > seg->ds.shm_segsz) {//段长
return -EINVAL;
}
@ -323,7 +391,7 @@ STATIC INT32 ShmSegValidCheck(INT32 segNum, size_t size, INT32 shmFlg)
return segNum;
}
///通过ID找到共享内存资源
STATIC struct shmIDSource *ShmFindSeg(int shmid)
{
struct shmIDSource *seg = NULL;
@ -341,7 +409,7 @@ STATIC struct shmIDSource *ShmFindSeg(int shmid)
return seg;
}
///共享内存映射
STATIC VOID ShmVmmMapping(LosVmSpace *space, LOS_DL_LIST *pageList, VADDR_T vaddr, UINT32 regionFlags)
{
LosVmPage *vmPage = NULL;
@ -349,64 +417,64 @@ STATIC VOID ShmVmmMapping(LosVmSpace *space, LOS_DL_LIST *pageList, VADDR_T vadd
PADDR_T pa;
STATUS_T ret;
LOS_DL_LIST_FOR_EACH_ENTRY(vmPage, pageList, LosVmPage, node) {
pa = VM_PAGE_TO_PHYS(vmPage);
LOS_AtomicInc(&vmPage->refCounts);
ret = LOS_ArchMmuMap(&space->archMmu, va, pa, 1, regionFlags);
LOS_DL_LIST_FOR_EACH_ENTRY(vmPage, pageList, LosVmPage, node) {//遍历一页一页映射
pa = VM_PAGE_TO_PHYS(vmPage);//拿到物理地址
LOS_AtomicInc(&vmPage->refCounts);//自增
ret = LOS_ArchMmuMap(&space->archMmu, va, pa, 1, regionFlags);//虚实映射
if (ret != 1) {
VM_ERR("LOS_ArchMmuMap failed, ret = %d", ret);
}
va += PAGE_SIZE;
}
}
///fork 一个共享线性区
VOID OsShmFork(LosVmSpace *space, LosVmMapRegion *oldRegion, LosVmMapRegion *newRegion)
{
struct shmIDSource *seg = NULL;
SYSV_SHM_LOCK();
seg = ShmFindSeg(oldRegion->shmid);
seg = ShmFindSeg(oldRegion->shmid);//通过老区ID获取对应的共享资源ID结构体
if (seg == NULL) {
SYSV_SHM_UNLOCK();
VM_ERR("shm fork failed!");
return;
}
newRegion->shmid = oldRegion->shmid;
newRegion->forkFlags = oldRegion->forkFlags;
ShmVmmMapping(space, &seg->node, newRegion->range.base, newRegion->regionFlags);
seg->ds.shm_nattch++;
newRegion->shmid = oldRegion->shmid;//一样的共享区ID
newRegion->forkFlags = oldRegion->forkFlags;//forkFlags也一样了
ShmVmmMapping(space, &seg->node, newRegion->range.base, newRegion->regionFlags);//新线性区与共享内存进行映射
seg->ds.shm_nattch++;//附在共享线性区上的进程数++
SYSV_SHM_UNLOCK();
}
///释放共享线性区
VOID OsShmRegionFree(LosVmSpace *space, LosVmMapRegion *region)
{
struct shmIDSource *seg = NULL;
SYSV_SHM_LOCK();
seg = ShmFindSeg(region->shmid);
seg = ShmFindSeg(region->shmid);//通过线性区ID获取对应的共享资源ID结构体
if (seg == NULL) {
SYSV_SHM_UNLOCK();
return;
}
LOS_ArchMmuUnmap(&space->archMmu, region->range.base, region->range.size >> PAGE_SHIFT);
ShmPagesRefDec(seg);
seg->ds.shm_nattch--;
LOS_ArchMmuUnmap(&space->archMmu, region->range.base, region->range.size >> PAGE_SHIFT);//解除线性区的映射
ShmPagesRefDec(seg);//ref --
seg->ds.shm_nattch--;//附在共享线性区上的进程数--
if (seg->ds.shm_nattch <= 0 && (seg->status & SHM_SEG_REMOVE)) {
ShmFreeSeg(seg, &IPC_SHM_USED_PAGE_COUNT);
ShmFreeSeg(seg, &IPC_SHM_USED_PAGE_COUNT);//就释放掉物理内存!注意是:物理内存
} else {
seg->ds.shm_dtime = time(NULL);
seg->ds.shm_lpid = LOS_GetCurrProcessID(); /* may not be the space's PID. */
}
SYSV_SHM_UNLOCK();
}
///是否为共享线性区,是否有标签?
BOOL OsIsShmRegion(LosVmMapRegion *region)
{
return (region->regionFlags & VM_MAP_REGION_FLAG_SHM) ? TRUE : FALSE;
}
///获取共享内存池中已被使用的段数量
STATIC INT32 ShmSegUsedCount(VOID)
{
INT32 i;
@ -415,16 +483,16 @@ STATIC INT32 ShmSegUsedCount(VOID)
for (i = 0; i < IPC_SHM_INFO.shmmni; i++) {
seg = &IPC_SHM_SEGS[i];
if (seg->status & SHM_SEG_USED) {
if (seg->status & SHM_SEG_USED) {//找到一个
count++;
}
}
return count;
}
///对共享内存段权限检查
STATIC INT32 ShmPermCheck(struct shmIDSource *seg, mode_t mode)
{
INT32 uid = LOS_GetUserID();
INT32 uid = LOS_GetUserID();//当前进程的用户ID
UINT32 tmpMode = 0;
mode_t privMode = seg->ds.shm_perm.mode;
mode_t accMode;
@ -466,6 +534,22 @@ STATIC INT32 ShmPermCheck(struct shmIDSource *seg, mode_t mode)
}
}
/*!
* @brief ShmGet
*
* @param key IPC使IPC
IPCkey,IPCmsggetsemgetshmget
IPC, key_tftok,ftok,.
* @param shmflg IPC_CREAT IPC_EXCL
IPC_CREAT IPCkeyIPC_PRIVATEIPCflagIPC_CREAT
IPCIPCIPC_CREATIPC_CREAT
IPC_EXCL IPC_CREAT使IPCIPC
EEXISTopenO_CREATO_EXCL
* @param size
* @return
*
* @see
*/
INT32 ShmGet(key_t key, size_t size, INT32 shmflg)
{
INT32 ret;
@ -476,13 +560,13 @@ INT32 ShmGet(key_t key, size_t size, INT32 shmflg)
if (key == IPC_PRIVATE) {
ret = ShmAllocSeg(key, size, shmflg);
} else {
ret = ShmFindSegByKey(key);
ret = ShmFindSegByKey(key);//通过key查找资源ID
if (ret < 0) {
if (((UINT32)shmflg & IPC_CREAT) == 0) {
if (((UINT32)shmflg & IPC_CREAT) == 0) {//
ret = -ENOENT;
goto ERROR;
} else {
ret = ShmAllocSeg(key, size, shmflg);
ret = ShmAllocSeg(key, size, shmflg);//分配一个共享内存
}
} else {
shmid = ret;
@ -491,7 +575,7 @@ INT32 ShmGet(key_t key, size_t size, INT32 shmflg)
ret = -EEXIST;
goto ERROR;
}
ret = ShmPermCheck(ShmFindSeg(shmid), (UINT32)shmflg & ACCESSPERMS);
ret = ShmPermCheck(ShmFindSeg(shmid), (UINT32)shmflg & ACCESSPERMS);//对共享内存权限检查
if (ret != 0) {
ret = -ret;
goto ERROR;
@ -526,13 +610,13 @@ INT32 ShmatParamCheck(const VOID *shmaddr, INT32 shmflg)
return 0;
}
///分配一个共享线性区并映射好
LosVmMapRegion *ShmatVmmAlloc(struct shmIDSource *seg, const VOID *shmaddr,
INT32 shmflg, UINT32 prot)
{
LosVmSpace *space = OsCurrProcessGet()->vmSpace;
LosVmMapRegion *region = NULL;
UINT32 flags = MAP_ANONYMOUS | MAP_SHARED;
UINT32 flags = MAP_ANONYMOUS | MAP_SHARED;//本线性区为共享+匿名标签
UINT32 mapFlags = flags | MAP_FIXED;
VADDR_T vaddr;
UINT32 regionFlags;
@ -543,29 +627,29 @@ LosVmMapRegion *ShmatVmmAlloc(struct shmIDSource *seg, const VOID *shmaddr,
}
regionFlags = OsCvtProtFlagsToRegionFlags(prot, flags);
(VOID)LOS_MuxAcquire(&space->regionMux);
if (shmaddr == NULL) {
region = LOS_RegionAlloc(space, 0, seg->ds.shm_segsz, regionFlags, 0);
} else {
if (shmaddr == NULL) {//未指定了共享内存连接到当前进程中的地址位置
region = LOS_RegionAlloc(space, 0, seg->ds.shm_segsz, regionFlags, 0);//分配线性区
} else {//指定时,就需要先找地址所在的线性区
if ((UINT32)shmflg & SHM_RND) {
vaddr = ROUNDDOWN((VADDR_T)(UINTPTR)shmaddr, SHMLBA);
} else {
vaddr = (VADDR_T)(UINTPTR)shmaddr;
}
}//找到线性区并重新映射,当指定地址时需贴上重新映射的标签
if (!((UINT32)shmflg & SHM_REMAP) && (LOS_RegionFind(space, vaddr) ||
LOS_RegionFind(space, vaddr + seg->ds.shm_segsz - 1) ||
LOS_RegionRangeFind(space, vaddr, seg->ds.shm_segsz - 1))) {
ret = EINVAL;
goto ERROR;
}
vaddr = (VADDR_T)LOS_MMap(vaddr, seg->ds.shm_segsz, prot, mapFlags, -1, 0);
region = LOS_RegionFind(space, vaddr);
vaddr = (VADDR_T)LOS_MMap(vaddr, seg->ds.shm_segsz, prot, mapFlags, -1, 0);//做好映射
region = LOS_RegionFind(space, vaddr);//重新查找线性区,用于返回.
}
if (region == NULL) {
ret = ENOMEM;
goto ERROR;
}
ShmVmmMapping(space, &seg->node, region->range.base, regionFlags);
ShmVmmMapping(space, &seg->node, region->range.base, regionFlags);//共享内存映射
(VOID)LOS_MuxRelease(&space->regionMux);
return region;
ERROR:
@ -574,6 +658,17 @@ ERROR:
return NULL;
}
/*!
* @brief ShmAt
* 访
* @param shm_flg 0
* @param shmaddr
* @param shmid shmget()
* @return
* shmat使shmid_dsshm_nattch1
shmid ,线ID g_shmSegs[shmid] shmid > 192
* @see
*/
VOID *ShmAt(INT32 shmid, const VOID *shmaddr, INT32 shmflg)
{
INT32 ret;
@ -582,13 +677,13 @@ VOID *ShmAt(INT32 shmid, const VOID *shmaddr, INT32 shmflg)
struct shmIDSource *seg = NULL;
LosVmMapRegion *r = NULL;
ret = ShmatParamCheck(shmaddr, shmflg);
ret = ShmatParamCheck(shmaddr, shmflg);//参数检查
if (ret != 0) {
set_errno(ret);
return (VOID *)-1;
}
if ((UINT32)shmflg & SHM_EXEC) {
if ((UINT32)shmflg & SHM_EXEC) {//flag 转换
prot |= PROT_EXEC;
acc_mode |= SHM_S_IXUGO;
} else if (((UINT32)shmflg & SHM_RDONLY) == 0) {
@ -597,7 +692,7 @@ VOID *ShmAt(INT32 shmid, const VOID *shmaddr, INT32 shmflg)
}
SYSV_SHM_LOCK();
seg = ShmFindSeg(shmid);
seg = ShmFindSeg(shmid);//找到段
if (seg == NULL) {
SYSV_SHM_UNLOCK();
return (VOID *)-1;
@ -608,18 +703,18 @@ VOID *ShmAt(INT32 shmid, const VOID *shmaddr, INT32 shmflg)
goto ERROR;
}
seg->ds.shm_nattch++;
r = ShmatVmmAlloc(seg, shmaddr, shmflg, prot);
seg->ds.shm_nattch++;//ds上记录有一个进程绑定上来
r = ShmatVmmAlloc(seg, shmaddr, shmflg, prot);//在当前进程空间分配一个线性区并映射到共享内存
if (r == NULL) {
seg->ds.shm_nattch--;
SYSV_SHM_UNLOCK();
return (VOID *)-1;
}
r->shmid = shmid;
r->regionFlags |= VM_MAP_REGION_FLAG_SHM;
seg->ds.shm_atime = time(NULL);
seg->ds.shm_lpid = LOS_GetCurrProcessID();
r->shmid = shmid;//把ID给线性区的shmid
r->regionFlags |= VM_MAP_REGION_FLAG_SHM;//这是一个共享线性区
seg->ds.shm_atime = time(NULL);//访问时间
seg->ds.shm_lpid = LOS_GetCurrProcessID();//进程ID
SYSV_SHM_UNLOCK();
return (VOID *)(UINTPTR)r->range.base;
@ -630,6 +725,19 @@ ERROR:
return (VOID *)-1;
}
/*!
* @brief ShmCtl
* shmid
* @param buf 访
* @param cmd command
IPC_STATshmid_dsshmid_ds
IPC_SETshmid_ds
IPC_RMID
* @param shmid shmget()
* @return
*
* @see
*/
INT32 ShmCtl(INT32 shmid, INT32 cmd, struct shmid_ds *buf)
{
struct shmIDSource *seg = NULL;
@ -642,7 +750,7 @@ INT32 ShmCtl(INT32 shmid, INT32 cmd, struct shmid_ds *buf)
SYSV_SHM_LOCK();
if ((cmd != IPC_INFO) && (cmd != SHM_INFO)) {
seg = ShmFindSeg(shmid);
seg = ShmFindSeg(shmid);//通过索引ID找到seg
if (seg == NULL) {
SYSV_SHM_UNLOCK();
return -1;
@ -656,13 +764,13 @@ INT32 ShmCtl(INT32 shmid, INT32 cmd, struct shmid_ds *buf)
switch (cmd) {
case IPC_STAT:
case SHM_STAT:
case SHM_STAT://取段结构
ret = ShmPermCheck(seg, SHM_S_IRUGO);
if (ret != 0) {
goto ERROR;
}
ret = LOS_ArchCopyToUser(buf, &seg->ds, sizeof(struct shmid_ds));
ret = LOS_ArchCopyToUser(buf, &seg->ds, sizeof(struct shmid_ds));//把内核空间的共享页数据拷贝到用户空间
if (ret != 0) {
ret = EFAULT;
goto ERROR;
@ -671,13 +779,13 @@ INT32 ShmCtl(INT32 shmid, INT32 cmd, struct shmid_ds *buf)
ret = (unsigned int)((unsigned int)seg->ds.shm_perm.seq << 16) | (unsigned int)((unsigned int)shmid & 0xffff); /* 16: use the seq as the upper 16 bits */
}
break;
case IPC_SET:
case IPC_SET://重置共享段
ret = ShmPermCheck(seg, SHM_M);
if (ret != 0) {
ret = EPERM;
goto ERROR;
}
//从用户空间拷贝数据到内核空间
ret = LOS_ArchCopyFromUser(&shm_perm, &buf->shm_perm, sizeof(struct ipc_perm));
if (ret != 0) {
ret = EFAULT;
@ -686,14 +794,14 @@ INT32 ShmCtl(INT32 shmid, INT32 cmd, struct shmid_ds *buf)
seg->ds.shm_perm.uid = shm_perm.uid;
seg->ds.shm_perm.gid = shm_perm.gid;
seg->ds.shm_perm.mode = (seg->ds.shm_perm.mode & ~ACCESSPERMS) |
(shm_perm.mode & ACCESSPERMS);
(shm_perm.mode & ACCESSPERMS);//可访问
seg->ds.shm_ctime = time(NULL);
#ifdef LOSCFG_SHELL
(VOID)memcpy_s(seg->ownerName, OS_PCB_NAME_LEN, OS_PCB_FROM_PID(shm_perm.uid)->processName,
OS_PCB_NAME_LEN);
#endif
break;
case IPC_RMID:
case IPC_RMID://删除共享段
ret = ShmPermCheck(seg, SHM_M);
if (ret != 0) {
ret = EPERM;
@ -701,11 +809,11 @@ INT32 ShmCtl(INT32 shmid, INT32 cmd, struct shmid_ds *buf)
}
seg->status |= SHM_SEG_REMOVE;
if (seg->ds.shm_nattch <= 0) {
if (seg->ds.shm_nattch <= 0) {//没有任何进程在使用了
ShmFreeSeg(seg, &IPC_SHM_USED_PAGE_COUNT);
}
break;
case IPC_INFO:
case IPC_INFO://把内核空间的共享页数据拷贝到用户空间
ret = LOS_ArchCopyToUser(buf, &IPC_SHM_INFO, sizeof(struct shminfo));
if (ret != 0) {
ret = EFAULT;
@ -719,8 +827,8 @@ INT32 ShmCtl(INT32 shmid, INT32 cmd, struct shmid_ds *buf)
shmInfo.shm_tot = 0;
shmInfo.swap_attempts = 0;
shmInfo.swap_successes = 0;
shmInfo.used_ids = ShmSegUsedCount();
ret = LOS_ArchCopyToUser(buf, &shmInfo, sizeof(struct shm_info));
shmInfo.used_ids = ShmSegUsedCount();//在使用的seg数
ret = LOS_ArchCopyToUser(buf, &shmInfo, sizeof(struct shm_info));//把内核空间的共享页数据拷贝到用户空间
if (ret != 0) {
ret = EFAULT;
goto ERROR;
@ -743,55 +851,63 @@ ERROR:
return -1;
}
/**
* @brief shmdt
shmat使shmid_dsshm_nattch1
* @attention
IPC_RMIDshmctl
* @param shmaddr
* @return INT32
*/
INT32 ShmDt(const VOID *shmaddr)
{
LosVmSpace *space = OsCurrProcessGet()->vmSpace;
LosVmSpace *space = OsCurrProcessGet()->vmSpace;//获取进程空间
struct shmIDSource *seg = NULL;
LosVmMapRegion *region = NULL;
INT32 shmid;
INT32 ret;
if (IS_PAGE_ALIGNED(shmaddr) == 0) {
if (IS_PAGE_ALIGNED(shmaddr) == 0) {//地址是否对齐
ret = EINVAL;
goto ERROR;
}
(VOID)LOS_MuxAcquire(&space->regionMux);
region = LOS_RegionFind(space, (VADDR_T)(UINTPTR)shmaddr);
region = LOS_RegionFind(space, (VADDR_T)(UINTPTR)shmaddr);//找到线性区
if (region == NULL) {
ret = EINVAL;
goto ERROR_WITH_LOCK;
}
shmid = region->shmid;
shmid = region->shmid;//线性区共享ID
if (region->range.base != (VADDR_T)(UINTPTR)shmaddr) {
ret = EINVAL;
if (region->range.base != (VADDR_T)(UINTPTR)shmaddr) {//这是用户空间和内核空间的一次解绑
ret = EINVAL; //shmaddr 必须要等于region->range.base
goto ERROR_WITH_LOCK;
}
/* remove it from aspace */
LOS_RbDelNode(&space->regionRbTree, &region->rbNode);
LOS_ArchMmuUnmap(&space->archMmu, region->range.base, region->range.size >> PAGE_SHIFT);
LOS_RbDelNode(&space->regionRbTree, &region->rbNode);//从红黑树和链表中摘除节点
LOS_ArchMmuUnmap(&space->archMmu, region->range.base, region->range.size >> PAGE_SHIFT);//解除线性区的映射
(VOID)LOS_MuxRelease(&space->regionMux);
/* free it */
free(region);
free(region);//释放线性区所占内存池中的内存
SYSV_SHM_LOCK();
seg = ShmFindSeg(shmid);
seg = ShmFindSeg(shmid);//找到seg,线性区和共享段的关系是 1:N 的关系,其他空间的线性区也会绑在共享段上
if (seg == NULL) {
ret = EINVAL;
SYSV_SHM_UNLOCK();
goto ERROR;
}
ShmPagesRefDec(seg);
seg->ds.shm_nattch--;
if ((seg->ds.shm_nattch <= 0) &&
(seg->status & SHM_SEG_REMOVE)) {
ShmFreeSeg(seg, &IPC_SHM_USED_PAGE_COUNT);
ShmPagesRefDec(seg);//页面引用数 --
seg->ds.shm_nattch--;//使用共享内存的进程数少了一个
if ((seg->ds.shm_nattch <= 0) && //无任何进程使用共享内存
(seg->status & SHM_SEG_REMOVE)) {//状态为删除时需要释放物理页内存了,否则其他进程还要继续使用共享内存
ShmFreeSeg(seg, &IPC_SHM_USED_PAGE_COUNT);//释放seg 页框链表中的页框内存,再重置seg状态
} else {
seg->ds.shm_dtime = time(NULL);
seg->ds.shm_lpid = LOS_GetCurrProcessID();
seg->ds.shm_dtime = time(NULL);//记录分离的时间
seg->ds.shm_lpid = LOS_GetCurrProcessID();//记录操作进程ID
}
SYSV_SHM_UNLOCK();
@ -847,7 +963,6 @@ STATIC VOID OsShmInfoCmd(VOID)
}
SYSV_SHM_UNLOCK();
}
STATIC VOID OsShmDeleteCmd(INT32 shmid)
{
struct shmIDSource *seg = NULL;
@ -876,7 +991,7 @@ STATIC VOID OsShmCmdUsage(VOID)
"\t-r [shmid], Recycle the specified shared memory about shmid\n"
"\t-h | --help, print shm command usage\n");
}
///共享内存
UINT32 OsShellCmdShm(INT32 argc, const CHAR *argv[])
{
INT32 shmid;
@ -911,4 +1026,3 @@ DONE:
SHELLCMD_ENTRY(shm_shellcmd, CMD_TYPE_SHOW, "shm", 2, (CmdCallBackFunc)OsShellCmdShm);
#endif
#endif

Loading…
Cancel
Save