From 48598e1fa979ebdf6a871d70e6c967d08f270de9 Mon Sep 17 00:00:00 2001 From: Dio Date: Thu, 28 Dec 2023 08:48:39 +0800 Subject: [PATCH] mp --- .../kernel/base/mp/los_lockdep.c | 366 ++++++++++++++++++ src/kernel_liteos_a/kernel/base/mp/los_mp.c | 119 ++++-- .../kernel/base/mp/los_percpu.c | 2 +- 3 files changed, 450 insertions(+), 37 deletions(-) diff --git a/src/kernel_liteos_a/kernel/base/mp/los_lockdep.c b/src/kernel_liteos_a/kernel/base/mp/los_lockdep.c index fb093591..9696a145 100644 --- a/src/kernel_liteos_a/kernel/base/mp/los_lockdep.c +++ b/src/kernel_liteos_a/kernel/base/mp/los_lockdep.c @@ -238,7 +238,373 @@ OUT: } VOID OsLockDepRecord(SPIN_LOCK_S *lock) +{/* + * Copyright (c) 2013-2019 Huawei Technologies 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: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "los_base.h" +#include "los_spinlock.h" +#include "los_task_pri.h" +#include "los_printf_pri.h" +#include "los_atomic.h" +#include "los_exc.h" + +#ifdef LOSCFG_KERNEL_SMP_LOCKDEP + +#define PRINT_BUF_SIZE 256 + +#define LOCKDEP_GET_NAME(lockDep, index) (((SPIN_LOCK_S *)((lockDep)->heldLocks[(index)].lockPtr))->name) +#define LOCKDEP_GET_ADDR(lockDep, index) ((lockDep)->heldLocks[(index)].lockAddr) + +STATIC Atomic g_lockdepAvailable = 1; + +/* atomic insurance for lockdep check */ +STATIC INLINE VOID OsLockDepRequire(UINT32 *intSave) +{ + *intSave = LOS_IntLock(); + while (LOS_AtomicCmpXchg32bits(&g_lockdepAvailable, 0, 1)) { + /* busy waiting */ + } +} + +STATIC INLINE VOID OsLockDepRelease(UINT32 intSave) +{ + LOS_AtomicSet(&g_lockdepAvailable, 1); + LOS_IntRestore(intSave); +} + +STATIC INLINE UINT64 OsLockDepGetCycles(VOID) +{ + UINT32 high, low; + + LOS_GetCpuCycle(&high, &low); + /* combine cycleHi and cycleLo into 8 bytes cycles */ + return (((UINT64)high << 32) + low); // 32 bits for lower half of UINT64 +} + +STATIC INLINE CHAR *OsLockDepErrorStringGet(enum LockDepErrType type) { + CHAR *errorString = NULL; + + switch (type) { + case LOCKDEP_ERR_DOUBLE_LOCK: + errorString = "double lock"; + break; + case LOCKDEP_ERR_DEAD_LOCK: + errorString = "dead lock"; + break; + case LOCKDEP_ERR_UNLOCK_WITOUT_LOCK: + errorString = "unlock without lock"; + break; + case LOCKDEP_ERR_OVERFLOW: + errorString = "lockdep overflow"; + break; + default: + errorString = "unknown error code"; + break; + } + + return errorString; +} + +WEAK VOID OsLockDepPanic(enum LockDepErrType errType) +{ + /* halt here */ + (VOID)errType; + (VOID)LOS_IntLock(); + OsBackTrace(); + while (1) {} +} + +STATIC VOID OsLockDepPrint(const CHAR *fmt, va_list ap) +{ + UINT32 len; + CHAR buf[PRINT_BUF_SIZE] = {0}; + + len = vsnprintf_s(buf, PRINT_BUF_SIZE, PRINT_BUF_SIZE - 1, fmt, ap); + if ((len == -1) && (*buf == '\0')) { + /* parameter is illegal or some features in fmt dont support */ + UartPuts("OsLockDepPrint is error\n", strlen("OsLockDepPrint is error\n"), 0); + return; + } + *(buf + len) = '\0'; + + UartPuts(buf, len, 0); +} + +STATIC VOID OsPrintLockDepInfo(const CHAR *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + OsLockDepPrint(fmt, ap); + va_end(ap); +} + +STATIC VOID OsLockDepDumpLock(const LosTaskCB *task, const SPIN_LOCK_S *lock, + const VOID *requestAddr, enum LockDepErrType errType) +{ + INT32 i; + const LockDep *lockDep = &task->lockDep; + const LosTaskCB *temp = task; + + OsPrintLockDepInfo("lockdep check failed\n"); + OsPrintLockDepInfo("error type : %s\n", OsLockDepErrorStringGet(errType)); + OsPrintLockDepInfo("request addr : 0x%x\n", requestAddr); + + while (1) { + OsPrintLockDepInfo("task name : %s\n", temp->taskName); + OsPrintLockDepInfo("task id : %u\n", temp->taskID); + OsPrintLockDepInfo("cpu num : %u\n", temp->currCpu); + OsPrintLockDepInfo("start dumping lockdep information\n"); + for (i = 0; i < lockDep->lockDepth; i++) { + if (lockDep->heldLocks[i].lockPtr == lock) { + OsPrintLockDepInfo("[%d] %s <-- addr:0x%x\n", i, LOCKDEP_GET_NAME(lockDep, i), + LOCKDEP_GET_ADDR(lockDep, i)); + } else { + OsPrintLockDepInfo("[%d] %s \n", i, LOCKDEP_GET_NAME(lockDep, i)); + } + } + OsPrintLockDepInfo("[%d] %s <-- now\n", i, lock->name); + + if (errType == LOCKDEP_ERR_DEAD_LOCK) { + temp = lock->owner; + lock = temp->lockDep.waitLock; + lockDep = &temp->lockDep; + } + + if (temp == task) { + break; + } + } +} + +STATIC BOOL OsLockDepCheckDependency(const LosTaskCB *current, LosTaskCB *lockOwner) +{ + BOOL checkResult = TRUE; + SPIN_LOCK_S *lockTemp = NULL; + + do { + if (current == lockOwner) { + checkResult = FALSE; + return checkResult; + } + if (lockOwner->lockDep.waitLock != NULL) { + lockTemp = lockOwner->lockDep.waitLock; + lockOwner = lockTemp->owner; + } else { + break; + } + } while (lockOwner != SPINLOCK_OWNER_INIT); + + return checkResult; +} + +VOID OsLockDepCheckIn(SPIN_LOCK_S *lock) +{ + UINT32 intSave; + enum LockDepErrType checkResult = LOCKDEP_SUCCESS; + VOID *requestAddr = (VOID *)__builtin_return_address(1); + LosTaskCB *current = OsCurrTaskGet(); + LockDep *lockDep = ¤t->lockDep; + LosTaskCB *lockOwner = NULL; + + OsLockDepRequire(&intSave); + + if (lockDep->lockDepth >= (INT32)MAX_LOCK_DEPTH) { + checkResult = LOCKDEP_ERR_OVERFLOW; + goto OUT; + } + + lockOwner = lock->owner; + /* not owned by any tasks yet, not doing following checks */ + if (lockOwner == SPINLOCK_OWNER_INIT) { + goto OUT; + } + + if (current == lockOwner) { + checkResult = LOCKDEP_ERR_DOUBLE_LOCK; + goto OUT; + } + + if (OsLockDepCheckDependency(current, lockOwner) != TRUE) { + checkResult = LOCKDEP_ERR_DEAD_LOCK; + goto OUT; + } + +OUT: + if (checkResult == LOCKDEP_SUCCESS) { + /* + * though the check may succeed, the waitLock still need to be set. + * because the OsLockDepCheckIn and OsLockDepRecord is not strictly multi-core + * sequential, there would be more than two tasks can pass the checking, but + * only one task can successfully obtain the lock. + */ + lockDep->waitLock = lock; + lockDep->heldLocks[lockDep->lockDepth].lockAddr = requestAddr; + lockDep->heldLocks[lockDep->lockDepth].waitTime = OsLockDepGetCycles(); /* start time */ + OsLockDepRelease(intSave); + return; + } + OsLockDepDumpLock(current, lock, requestAddr, checkResult); + OsLockDepRelease(intSave); + OsLockDepPanic(checkResult); +} + +VOID OsLockDepRecord(SPIN_LOCK_S *lock) +{ + UINT32 intSave; + UINT64 cycles; + LosTaskCB *current = OsCurrTaskGet(); + LockDep *lockDep = ¤t->lockDep; + HeldLocks *heldlock = &lockDep->heldLocks[lockDep->lockDepth]; + + OsLockDepRequire(&intSave); + + /* + * OsLockDepCheckIn records start time t1, after the lock is obtained, we + * get the time t2, (t2 - t1) is the time of waiting for the lock + */ + cycles = OsLockDepGetCycles(); + heldlock->waitTime = cycles - heldlock->waitTime; + heldlock->holdTime = cycles; + + /* record lock info */ + lock->owner = current; + lock->cpuid = ArchCurrCpuid(); + + /* record lockdep info */ + heldlock->lockPtr = lock; + lockDep->lockDepth++; + lockDep->waitLock = NULL; + + OsLockDepRelease(intSave); +} + +VOID OsLockDepCheckOut(SPIN_LOCK_S *lock) +{ + UINT32 intSave; + INT32 depth; + enum LockDepErrType checkResult; + VOID *requestAddr = (VOID *)__builtin_return_address(1); + LosTaskCB *current = OsCurrTaskGet(); + LosTaskCB *owner = NULL; + LockDep *lockDep = NULL; + HeldLocks *heldlocks = NULL; + + OsLockDepRequire(&intSave); + + owner = lock->owner; + if (owner == SPINLOCK_OWNER_INIT) { + checkResult = LOCKDEP_ERR_UNLOCK_WITOUT_LOCK; + OsLockDepDumpLock(current, lock, requestAddr, checkResult); + OsLockDepRelease(intSave); + OsLockDepPanic(checkResult); + return; + } + + lockDep = &owner->lockDep; + heldlocks = &lockDep->heldLocks[0]; + depth = lockDep->lockDepth; + + /* find the lock position */ + while (--depth >= 0) { + if (heldlocks[depth].lockPtr == lock) { + break; + } + } + + LOS_ASSERT(depth >= 0); + + /* record lock holding time */ + heldlocks[depth].holdTime = OsLockDepGetCycles() - heldlocks[depth].holdTime; + + /* if unlock an older lock, needs move heldLock records */ + while (depth < lockDep->lockDepth - 1) { + lockDep->heldLocks[depth] = lockDep->heldLocks[depth + 1]; + depth++; + } + + lockDep->lockDepth--; + lock->cpuid = (UINT32)(-1); + lock->owner = SPINLOCK_OWNER_INIT; + + OsLockDepRelease(intSave); +} + +VOID OsLockdepClearSpinlocks(VOID) +{ + LosTaskCB *task = OsCurrTaskGet(); + LockDep *lockDep = &task->lockDep; + SPIN_LOCK_S *lock = NULL; + + /* + * Unlock spinlocks that running task has held. + * lockDepth will decrease after each spinlock is unlockded. + */ + while (lockDep->lockDepth) { + lock = lockDep->heldLocks[lockDep->lockDepth - 1].lockPtr; + LOS_SpinUnlock(lock); + } +} + +#else /* LOSCFG_KERNEL_SMP_LOCKDEP */ + +VOID OsLockDepCheckIn(SPIN_LOCK_S *lock) +{ + (VOID)lock; + + return; +} + +VOID OsLockDepRecord(SPIN_LOCK_S *lock) +{ + (VOID)lock; + + return; +} + +VOID OsLockDepCheckOut(SPIN_LOCK_S *lock) +{ + (VOID)lock; + + return; +} + +VOID OsLockdepClearSpinlocks(VOID) +{ + return; +} + +#endif + + UINT32 intSave; UINT64 cycles; LosTaskCB *current = OsCurrTaskGet(); diff --git a/src/kernel_liteos_a/kernel/base/mp/los_mp.c b/src/kernel_liteos_a/kernel/base/mp/los_mp.c index 67bc7012..41825f20 100644 --- a/src/kernel_liteos_a/kernel/base/mp/los_mp.c +++ b/src/kernel_liteos_a/kernel/base/mp/los_mp.c @@ -1,3 +1,32 @@ +/*! + * @file los_mp.c + * @brief + * @link + * @verbatim + 多CPU核的操作系统3种处理模式(SMP+AMP+BMP) 鸿蒙实现的是 SMP 的方式 + 非对称多处理(Asymmetric multiprocessing,AMP)每个CPU内核 + 运行一个独立的操作系统或同一操作系统的独立实例(instantiation)。 + + 对称多处理(Symmetric multiprocessing,SMP)一个操作系统的实例 + 可以同时管理所有CPU内核,且应用并不绑定某一个内核。 + + 混合多处理(Bound multiprocessing,BMP)一个操作系统的实例可以 + 同时管理所有CPU内核,但每个应用被锁定于某个指定的核心。 + + 多核多线程处理器的中断 + 由 PIC(Programmable Interrupt Controller)统一控制。PIC 允许一个 + 硬件线程中断其他的硬件线程,这种方式被称为核间中断(Inter-Processor Interrupts,IPI) + + SGI:软件触发中断(Software Generated Interrupt)。在arm处理器中, + SGI共有16个,硬件中断号分别为ID0~ID15。它通常用于多核间通讯。 + * @endverbatim + * @version + * @author weharmonyos.com | 鸿蒙研究站 | 每天死磕一点点 + * @date 2021-11-18 + * + * @history + * + */ /* * Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved. * Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved. @@ -36,43 +65,43 @@ #include "los_swtmr.h" #include "los_task_pri.h" -#ifdef LOSCFG_KERNEL_SMP +#ifdef LOSCFG_KERNEL_SMP +//给参数CPU发送调度信号 #ifdef LOSCFG_KERNEL_SMP_CALL LITE_OS_SEC_BSS SPIN_LOCK_INIT(g_mpCallSpin); #define MP_CALL_LOCK(state) LOS_SpinLockSave(&g_mpCallSpin, &(state)) #define MP_CALL_UNLOCK(state) LOS_SpinUnlockRestore(&g_mpCallSpin, (state)) #endif - -VOID LOS_MpSchedule(UINT32 target) +VOID LOS_MpSchedule(UINT32 target)//target每位对应CPU core { UINT32 cpuid = ArchCurrCpuid(); - target &= ~(1U << cpuid); - HalIrqSendIpi(target, LOS_MP_IPI_SCHEDULE); + target &= ~(1U << cpuid);//获取除了自身之外的其他CPU + HalIrqSendIpi(target, LOS_MP_IPI_SCHEDULE);//向目标CPU发送调度信号,核间中断(Inter-Processor Interrupts),IPI } - +///硬中断唤醒处理函数 VOID OsMpWakeHandler(VOID) { /* generic wakeup ipi, do nothing */ } - +///硬中断调度处理函数 VOID OsMpScheduleHandler(VOID) -{ +{//将调度标志设置为与唤醒功能不同,这样就可以在硬中断结束时触发调度程序。 /* * set schedule flag to differ from wake function, * so that the scheduler can be triggered at the end of irq. */ OsSchedRunqueuePendingSet(); } - +///硬中断暂停处理函数 VOID OsMpHaltHandler(VOID) { (VOID)LOS_IntLock(); - OsPercpuGet()->excFlag = CPU_HALT; + OsPercpuGet()->excFlag = CPU_HALT;//让当前Cpu停止工作 - while (1) {} + while (1) {}//陷入空循环,也就是空闲状态 } - +///MP定时器处理函数, 递归检查所有可用任务 VOID OsMpCollectTasks(VOID) { LosTaskCB *taskCB = NULL; @@ -80,19 +109,19 @@ VOID OsMpCollectTasks(VOID) UINT32 ret; /* recursive checking all the available task */ - for (; taskID <= g_taskMaxNum; taskID++) { + for (; taskID <= g_taskMaxNum; taskID++) { //递归检查所有可用任务 taskCB = &g_taskCBArray[taskID]; if (OsTaskIsUnused(taskCB) || OsTaskIsRunning(taskCB)) { continue; } - /* + /* 虽然任务状态不是原子的,但此检查可能成功,但无法完成删除,此删除将在下次运行之前处理 * though task status is not atomic, this check may success but not accomplish * the deletion; this deletion will be handled until the next run. */ - if (taskCB->signal & SIGNAL_KILL) { - ret = LOS_TaskDelete(taskID); + if (taskCB->signal & SIGNAL_KILL) {//任务收到被干掉信号 + ret = LOS_TaskDelete(taskID);//干掉任务,回归任务池 if (ret != LOS_OK) { PRINT_WARN("GC collect task failed err:0x%x\n", ret); } @@ -101,6 +130,17 @@ VOID OsMpCollectTasks(VOID) } #ifdef LOSCFG_KERNEL_SMP_CALL +/*! + * @brief OsMpFuncCall + * 向指定CPU的funcLink上注册回调函数, 该怎么理解这个函数呢 ? 具体有什么用呢 ? + * \n 可由CPU a核向b核发起一个请求,让b核去执行某个函数, 这是否是分布式调度的底层实现基础 ? + * @param args + * @param func + * @param target + * @return + * + * @see + */ VOID OsMpFuncCall(UINT32 target, SMP_FUNC_CALL func, VOID *args) { UINT32 index; @@ -110,13 +150,13 @@ VOID OsMpFuncCall(UINT32 target, SMP_FUNC_CALL func, VOID *args) return; } - if (!(target & OS_MP_CPU_ALL)) { + if (!(target & OS_MP_CPU_ALL)) {//检查目标CPU是否正确 return; } - for (index = 0; index < LOSCFG_KERNEL_CORE_NUM; index++) { + for (index = 0; index < LOSCFG_KERNEL_CORE_NUM; index++) {//遍历所有核 if (CPUID_TO_AFFI_MASK(index) & target) { - MpCallFunc *mpCallFunc = (MpCallFunc *)LOS_MemAlloc(m_aucSysMem0, sizeof(MpCallFunc)); + MpCallFunc *mpCallFunc = (MpCallFunc *)LOS_MemAlloc(m_aucSysMem0, sizeof(MpCallFunc));//从内核空间 分配回调结构体 if (mpCallFunc == NULL) { PRINT_ERR("smp func call malloc failed\n"); return; @@ -125,59 +165,66 @@ VOID OsMpFuncCall(UINT32 target, SMP_FUNC_CALL func, VOID *args) mpCallFunc->args = args; MP_CALL_LOCK(intSave); - LOS_ListAdd(&g_percpu[index].funcLink, &(mpCallFunc->node)); + LOS_ListAdd(&g_percpu[index].funcLink, &(mpCallFunc->node));//将回调结构体挂入链表尾部 MP_CALL_UNLOCK(intSave); } } - HalIrqSendIpi(target, LOS_MP_IPI_FUNC_CALL); + HalIrqSendIpi(target, LOS_MP_IPI_FUNC_CALL);//向目标CPU发起核间中断 } +/*! + * @brief OsMpFuncCallHandler + * 回调向当前CPU注册过的函数 + * @return + * + * @see + */ VOID OsMpFuncCallHandler(VOID) { UINT32 intSave; - UINT32 cpuid = ArchCurrCpuid(); + UINT32 cpuid = ArchCurrCpuid();//获取当前CPU LOS_DL_LIST *list = NULL; MpCallFunc *mpCallFunc = NULL; MP_CALL_LOCK(intSave); - while (!LOS_ListEmpty(&g_percpu[cpuid].funcLink)) { - list = LOS_DL_LIST_FIRST(&g_percpu[cpuid].funcLink); - LOS_ListDelete(list); + while (!LOS_ListEmpty(&g_percpu[cpuid].funcLink)) {//遍历回调函数链表,知道为空 + list = LOS_DL_LIST_FIRST(&g_percpu[cpuid].funcLink);//获取链表第一个数据 + LOS_ListDelete(list);//将自己从链表上摘除 MP_CALL_UNLOCK(intSave); - mpCallFunc = LOS_DL_LIST_ENTRY(list, MpCallFunc, node); - mpCallFunc->func(mpCallFunc->args); - (VOID)LOS_MemFree(m_aucSysMem0, mpCallFunc); + mpCallFunc = LOS_DL_LIST_ENTRY(list, MpCallFunc, node);//获取回调函数 + mpCallFunc->func(mpCallFunc->args);//获取参数并回调该函数 + (VOID)LOS_MemFree(m_aucSysMem0, mpCallFunc);//释放回调函数内存 MP_CALL_LOCK(intSave); } MP_CALL_UNLOCK(intSave); } - +/// CPU层级的回调模块初始化 VOID OsMpFuncCallInit(VOID) { UINT32 index; - /* init funclink for each core */ + /* init funclink for each core | 为每个CPU核整一个回调函数链表*/ for (index = 0; index < LOSCFG_KERNEL_CORE_NUM; index++) { - LOS_ListInit(&g_percpu[index].funcLink); + LOS_ListInit(&g_percpu[index].funcLink);//链表初始化 } } #endif /* LOSCFG_KERNEL_SMP_CALL */ - +//MP(multiprocessing) 多核处理器初始化 UINT32 OsMpInit(VOID) { UINT16 swtmrId; - (VOID)LOS_SwtmrCreate(OS_MP_GC_PERIOD, LOS_SWTMR_MODE_PERIOD, - (SWTMR_PROC_FUNC)OsMpCollectTasks, &swtmrId, 0); - (VOID)LOS_SwtmrStart(swtmrId); + (VOID)LOS_SwtmrCreate(OS_MP_GC_PERIOD, LOS_SWTMR_MODE_PERIOD, //创建一个周期性,持续时间为 100个tick的定时器 + (SWTMR_PROC_FUNC)OsMpCollectTasks, &swtmrId, 0);//OsMpCollectTasks为超时回调函数 + (VOID)LOS_SwtmrStart(swtmrId);//开始定时任务 #ifdef LOSCFG_KERNEL_SMP_CALL OsMpFuncCallInit(); #endif return LOS_OK; } -LOS_MODULE_INIT(OsMpInit, LOS_INIT_LEVEL_KMOD_TASK); +LOS_MODULE_INIT(OsMpInit, LOS_INIT_LEVEL_KMOD_TASK);//多处理器模块初始化 #endif diff --git a/src/kernel_liteos_a/kernel/base/mp/los_percpu.c b/src/kernel_liteos_a/kernel/base/mp/los_percpu.c index 4d883916..527c028f 100644 --- a/src/kernel_liteos_a/kernel/base/mp/los_percpu.c +++ b/src/kernel_liteos_a/kernel/base/mp/los_percpu.c @@ -33,7 +33,7 @@ #include "los_printf.h" #ifdef LOSCFG_KERNEL_SMP -Percpu g_percpu[LOSCFG_KERNEL_CORE_NUM]; +Percpu g_percpu[LOSCFG_KERNEL_CORE_NUM]; ///< CPU池,池大小由CPU核数决定 VOID OsAllCpuStatusOutput(VOID) {