提交 9e466f5e 编写于 作者: 鸿蒙内核源码分析's avatar 鸿蒙内核源码分析
上级 1b01ca35
...@@ -64,9 +64,9 @@ ...@@ -64,9 +64,9 @@
#endif #endif
/* Initial bit32 stack value. */ /* Initial bit32 stack value. */
#define OS_STACK_INIT 0xCACACACA #define OS_STACK_INIT 0xCACACACA //栈指针初始化值 0b 1010 1010 1010
/* Bit32 stack top magic number. */ /* Bit32 stack top magic number. */
#define OS_STACK_MAGIC_WORD 0xCCCCCCCC #define OS_STACK_MAGIC_WORD 0xCCCCCCCC //用于栈顶值,可标识为栈是否被使用过,神奇的 "烫烫烫烫"的根源所在! 0b 1100 1100 1100
#ifdef LOSCFG_GDB #ifdef LOSCFG_GDB
#define OS_EXC_UNDEF_STACK_SIZE 512 #define OS_EXC_UNDEF_STACK_SIZE 512
......
...@@ -87,7 +87,7 @@ extern "C" { ...@@ -87,7 +87,7 @@ extern "C" {
* Identification registers (c0) * Identification registers (c0)
*/ */
#define MIDR CP15_REG(c0, 0, c0, 0) /* Main ID Register */ #define MIDR CP15_REG(c0, 0, c0, 0) /* Main ID Register */
#define MPIDR CP15_REG(c0, 0, c0, 5) /* Multiprocessor Affinity Register */ #define MPIDR CP15_REG(c0, 0, c0, 5) /* Multiprocessor Affinity Register *///多处理器关联寄存器给每个CPU制定一个逻辑地址
#define CCSIDR CP15_REG(c0, 1, c0, 0) /* Cache Size ID Registers */ #define CCSIDR CP15_REG(c0, 1, c0, 0) /* Cache Size ID Registers */
#define CLIDR CP15_REG(c0, 1, c0, 1) /* Cache Level ID Register */ #define CLIDR CP15_REG(c0, 1, c0, 1) /* Cache Level ID Register */
#define VPIDR CP15_REG(c0, 4, c0, 0) /* Virtualization Processor ID Register */ #define VPIDR CP15_REG(c0, 4, c0, 0) /* Virtualization Processor ID Register */
...@@ -141,12 +141,12 @@ STATIC INLINE VOID ArchCurrUserTaskSet(UINTPTR val) ...@@ -141,12 +141,12 @@ STATIC INLINE VOID ArchCurrUserTaskSet(UINTPTR val)
{ {
ARM_SYSREG_WRITE(TPIDRURO, (UINT32)val); ARM_SYSREG_WRITE(TPIDRURO, (UINT32)val);
} }
//https://www.keil.com/pack/doc/cmsis/Core_A/html/group__CMSIS__MPIDR.html
STATIC INLINE UINT32 ArchCurrCpuid(VOID) STATIC INLINE UINT32 ArchCurrCpuid(VOID)
{ {
#if (LOSCFG_KERNEL_SMP == YES) #if (LOSCFG_KERNEL_SMP == YES)//CPU多核情况
return ARM_SYSREG_READ(MPIDR) & MPIDR_CPUID_MASK; return ARM_SYSREG_READ(MPIDR) & MPIDR_CPUID_MASK;
#else #else//ARM架构通过MPIDR(Multiprocessor Affinity Register)寄存器给每个CPU制定一个逻辑地址。
return 0; return 0;
#endif #endif
} }
......
...@@ -39,7 +39,7 @@ extern "C" { ...@@ -39,7 +39,7 @@ extern "C" {
#endif /* __cplusplus */ #endif /* __cplusplus */
/* support cpu vendors */ /* support cpu vendors */
CpuVendor g_cpuTable[] = { CpuVendor g_cpuTable[] = {//支持的CPU供应商
/* armv7-a */ /* armv7-a */
{ 0xc07, "Cortex-A7" }, { 0xc07, "Cortex-A7" },
{ 0xc09, "Cortex-A9" }, { 0xc09, "Cortex-A9" },
......
...@@ -607,21 +607,21 @@ LITE_OS_SEC_TEXT VOID OsProcessCBRecyleToFree(VOID) ...@@ -607,21 +607,21 @@ LITE_OS_SEC_TEXT VOID OsProcessCBRecyleToFree(VOID)
SCHEDULER_UNLOCK(intSave); SCHEDULER_UNLOCK(intSave);
} }
//获取一个可用的PCB(进程控制块)
STATIC LosProcessCB *OsGetFreePCB(VOID) STATIC LosProcessCB *OsGetFreePCB(VOID)
{ {
LosProcessCB *processCB = NULL; LosProcessCB *processCB = NULL;
UINT32 intSave; UINT32 intSave;
SCHEDULER_LOCK(intSave); SCHEDULER_LOCK(intSave);
if (LOS_ListEmpty(&g_freeProcess)) { if (LOS_ListEmpty(&g_freeProcess)) {//空闲池里还有未分配的task?
SCHEDULER_UNLOCK(intSave); SCHEDULER_UNLOCK(intSave);
PRINT_ERR("No idle PCB in the system!\n"); PRINT_ERR("No idle PCB in the system!\n");
return NULL; return NULL;
} }
processCB = OS_PCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(&g_freeProcess)); processCB = OS_PCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(&g_freeProcess));//拿到PCB,通过OS_PCB_FROM_PENDLIST是因为通过pendlist 节点挂在 freelist链表上.
LOS_ListDelete(&processCB->pendList); LOS_ListDelete(&processCB->pendList);//分配出来了就要在freelist将自己摘除
SCHEDULER_UNLOCK(intSave); SCHEDULER_UNLOCK(intSave);
return processCB; return processCB;
...@@ -1336,16 +1336,16 @@ ERROR: ...@@ -1336,16 +1336,16 @@ ERROR:
SCHEDULER_UNLOCK(intSave); SCHEDULER_UNLOCK(intSave);
return pid; return pid;
} }
//设置进程组检查
STATIC UINT32 OsSetProcessGroupCheck(const LosProcessCB *processCB, UINT32 gid) STATIC UINT32 OsSetProcessGroupCheck(const LosProcessCB *processCB, UINT32 gid)
{ {
LosProcessCB *runProcessCB = OsCurrProcessGet(); LosProcessCB *runProcessCB = OsCurrProcessGet();//拿到当前运行进程
LosProcessCB *groupProcessCB = OS_PCB_FROM_PID(gid); LosProcessCB *groupProcessCB = OS_PCB_FROM_PID(gid);//通过组ID拿到组长PCB实体
if (OsProcessIsInactive(processCB)) { if (OsProcessIsInactive(processCB)) {//进程是否活动
return LOS_ESRCH; return LOS_ESRCH;
} }
//参数进程不在用户态或者组长不在用户态
if (!OsProcessIsUserMode(processCB) || !OsProcessIsUserMode(groupProcessCB)) { if (!OsProcessIsUserMode(processCB) || !OsProcessIsUserMode(groupProcessCB)) {
return LOS_EPERM; return LOS_EPERM;
} }
......
...@@ -335,8 +335,8 @@ LITE_OS_SEC_TEXT_INIT UINT32 OsTaskInit(VOID) ...@@ -335,8 +335,8 @@ LITE_OS_SEC_TEXT_INIT UINT32 OsTaskInit(VOID)
for (index = 0; index < g_taskMaxNum; index++) { for (index = 0; index < g_taskMaxNum; index++) {
g_taskCBArray[index].taskStatus = OS_TASK_STATUS_UNUSED; g_taskCBArray[index].taskStatus = OS_TASK_STATUS_UNUSED;
g_taskCBArray[index].taskID = index;//任务ID最大默认127 g_taskCBArray[index].taskID = index;//任务ID最大默认127
LOS_ListTailInsert(&g_losFreeTask, &g_taskCBArray[index].pendList);//都插入空闲任务列表 LOS_ListTailInsert(&g_losFreeTask, &g_taskCBArray[index].pendList);//都插入空闲任务列表
} }//注意:这里挂的是pendList节点,所以取TCB要通过 OS_TCB_FROM_PENDLIST 取.
ret = OsPriQueueInit();//创建32个任务优先级队列,即32个双向循环链表 ret = OsPriQueueInit();//创建32个任务优先级队列,即32个双向循环链表
if (ret != LOS_OK) { if (ret != LOS_OK) {
...@@ -352,32 +352,32 @@ LITE_OS_SEC_TEXT_INIT UINT32 OsTaskInit(VOID) ...@@ -352,32 +352,32 @@ LITE_OS_SEC_TEXT_INIT UINT32 OsTaskInit(VOID)
} }
return LOS_OK; return LOS_OK;
} }
//获取IdletaskId,每个CPU核都对Task进行了内部管理,做到真正的并行处理
UINT32 OsGetIdleTaskId(VOID) UINT32 OsGetIdleTaskId(VOID)
{ {
Percpu *perCpu = OsPercpuGet(); Percpu *perCpu = OsPercpuGet();//获取当前Cpu信息
return perCpu->idleTaskID; return perCpu->idleTaskID;//返回当前CPU 空闲任务ID
} }
//创建一个空闲任务
LITE_OS_SEC_TEXT_INIT UINT32 OsIdleTaskCreate(VOID) LITE_OS_SEC_TEXT_INIT UINT32 OsIdleTaskCreate(VOID)
{ {
UINT32 ret; UINT32 ret;
TSK_INIT_PARAM_S taskInitParam; TSK_INIT_PARAM_S taskInitParam;
Percpu *perCpu = OsPercpuGet(); Percpu *perCpu = OsPercpuGet();//获取CPU信息
UINT32 *idleTaskID = &perCpu->idleTaskID; UINT32 *idleTaskID = &perCpu->idleTaskID;
(VOID)memset_s((VOID *)(&taskInitParam), sizeof(TSK_INIT_PARAM_S), 0, sizeof(TSK_INIT_PARAM_S)); (VOID)memset_s((VOID *)(&taskInitParam), sizeof(TSK_INIT_PARAM_S), 0, sizeof(TSK_INIT_PARAM_S));//任务初始参数清0
taskInitParam.pfnTaskEntry = (TSK_ENTRY_FUNC)OsIdleTask; taskInitParam.pfnTaskEntry = (TSK_ENTRY_FUNC)OsIdleTask;//入口函数指定idle
taskInitParam.uwStackSize = LOSCFG_BASE_CORE_TSK_IDLE_STACK_SIZE; taskInitParam.uwStackSize = LOSCFG_BASE_CORE_TSK_IDLE_STACK_SIZE;//任务栈大小
taskInitParam.pcName = "Idle"; taskInitParam.pcName = "Idle";//任务名称 叫pcName有点怪怪的,不能换个撒
taskInitParam.usTaskPrio = OS_TASK_PRIORITY_LOWEST; taskInitParam.usTaskPrio = OS_TASK_PRIORITY_LOWEST;//默认最低优先级 31
taskInitParam.uwResved = OS_TASK_FLAG_IDLEFLAG; taskInitParam.uwResved = OS_TASK_FLAG_IDLEFLAG;//默认idle flag
#if (LOSCFG_KERNEL_SMP == YES) #if (LOSCFG_KERNEL_SMP == YES)//CPU多核情况
taskInitParam.usCpuAffiMask = CPUID_TO_AFFI_MASK(ArchCurrCpuid()); taskInitParam.usCpuAffiMask = CPUID_TO_AFFI_MASK(ArchCurrCpuid());//意思是归于哪个CPU核调度,
#endif #endif
ret = LOS_TaskCreate(idleTaskID, &taskInitParam); ret = LOS_TaskCreate(idleTaskID, &taskInitParam);//创建task并申请调度,
OS_TCB_FROM_TID(*idleTaskID)->taskStatus |= OS_TASK_FLAG_SYSTEM_TASK; OS_TCB_FROM_TID(*idleTaskID)->taskStatus |= OS_TASK_FLAG_SYSTEM_TASK;//设置task状态为系统任务,系统任务运行在内核态.
//这里说下系统任务有哪些?比如: idle,swtmr(软时钟)等等
return ret; return ret;
} }
...@@ -398,7 +398,7 @@ LITE_OS_SEC_TEXT UINT32 LOS_CurTaskIDGet(VOID) ...@@ -398,7 +398,7 @@ LITE_OS_SEC_TEXT UINT32 LOS_CurTaskIDGet(VOID)
#if (LOSCFG_BASE_CORE_TSK_MONITOR == YES) #if (LOSCFG_BASE_CORE_TSK_MONITOR == YES)
LITE_OS_SEC_TEXT STATIC VOID OsTaskStackCheck(LosTaskCB *oldTask, LosTaskCB *newTask) LITE_OS_SEC_TEXT STATIC VOID OsTaskStackCheck(LosTaskCB *oldTask, LosTaskCB *newTask)
{ {
if (!OS_STACK_MAGIC_CHECK(oldTask->topOfStack)) { if (!OS_STACK_MAGIC_CHECK(oldTask->topOfStack)) {//magic检查无效情况
LOS_Panic("CURRENT task ID: %s:%d stack overflow!\n", oldTask->taskName, oldTask->taskID); LOS_Panic("CURRENT task ID: %s:%d stack overflow!\n", oldTask->taskName, oldTask->taskID);
} }
...@@ -414,20 +414,20 @@ LITE_OS_SEC_TEXT STATIC VOID OsTaskStackCheck(LosTaskCB *oldTask, LosTaskCB *new ...@@ -414,20 +414,20 @@ LITE_OS_SEC_TEXT STATIC VOID OsTaskStackCheck(LosTaskCB *oldTask, LosTaskCB *new
} }
#endif #endif
//任务切换检查
LITE_OS_SEC_TEXT_MINOR UINT32 OsTaskSwitchCheck(LosTaskCB *oldTask, LosTaskCB *newTask) LITE_OS_SEC_TEXT_MINOR UINT32 OsTaskSwitchCheck(LosTaskCB *oldTask, LosTaskCB *newTask)
{ {
#if (LOSCFG_BASE_CORE_TSK_MONITOR == YES) #if (LOSCFG_BASE_CORE_TSK_MONITOR == YES)//这里宏指任务栈有没有启动监控
OsTaskStackCheck(oldTask, newTask); OsTaskStackCheck(oldTask, newTask);//任务栈监控检查
#endif /* LOSCFG_BASE_CORE_TSK_MONITOR == YES */ #endif /* LOSCFG_BASE_CORE_TSK_MONITOR == YES */
#if (LOSCFG_KERNEL_TRACE == YES) #if (LOSCFG_KERNEL_TRACE == YES)
LOS_Trace(LOS_TRACE_SWITCH, newTask->taskID, oldTask->taskID); LOS_Trace(LOS_TRACE_SWITCH, newTask->taskID, oldTask->taskID);//打印新老任务
#endif #endif
return LOS_OK; return LOS_OK;
} }
//任务退出
LITE_OS_SEC_TEXT VOID OsTaskToExit(LosTaskCB *taskCB, UINT32 status) LITE_OS_SEC_TEXT VOID OsTaskToExit(LosTaskCB *taskCB, UINT32 status)
{ {
UINT32 intSave; UINT32 intSave;
...@@ -478,10 +478,10 @@ LITE_OS_SEC_TEXT_INIT VOID OsTaskEntry(UINT32 taskID) ...@@ -478,10 +478,10 @@ LITE_OS_SEC_TEXT_INIT VOID OsTaskEntry(UINT32 taskID)
(VOID)LOS_IntUnLock(); (VOID)LOS_IntUnLock();
taskCB = OS_TCB_FROM_TID(taskID); taskCB = OS_TCB_FROM_TID(taskID);
taskCB->joinRetval = taskCB->taskEntry(taskCB->args[0], taskCB->args[1], taskCB->joinRetval = taskCB->taskEntry(taskCB->args[0], taskCB->args[1],//调用入口函数
taskCB->args[2], taskCB->args[3]); /* 2 & 3: just for args array index */ taskCB->args[2], taskCB->args[3]); /* 2 & 3: just for args array index */
if (taskCB->taskStatus & OS_TASK_FLAG_DETACHED) { if (taskCB->taskStatus & OS_TASK_FLAG_DETACHED) {//task有分离标签时
taskCB->joinRetval = 0; taskCB->joinRetval = 0;//结合数为0
} }
OsTaskToExit(taskCB, 0); OsTaskToExit(taskCB, 0);
...@@ -766,21 +766,21 @@ LITE_OS_SEC_TEXT_INIT STATIC UINT32 OsTaskCBInit(LosTaskCB *taskCB, const TSK_IN ...@@ -766,21 +766,21 @@ LITE_OS_SEC_TEXT_INIT STATIC UINT32 OsTaskCBInit(LosTaskCB *taskCB, const TSK_IN
return LOS_OK; return LOS_OK;
} }
//获取一个空闲TCB
LITE_OS_SEC_TEXT LosTaskCB *OsGetFreeTaskCB(VOID) LITE_OS_SEC_TEXT LosTaskCB *OsGetFreeTaskCB(VOID)
{ {
UINT32 intSave; UINT32 intSave;
LosTaskCB *taskCB = NULL; LosTaskCB *taskCB = NULL;
SCHEDULER_LOCK(intSave); SCHEDULER_LOCK(intSave);
if (LOS_ListEmpty(&g_losFreeTask)) { if (LOS_ListEmpty(&g_losFreeTask)) {//全局空闲task为空
SCHEDULER_UNLOCK(intSave); SCHEDULER_UNLOCK(intSave);
PRINT_ERR("No idle TCB in the system!\n"); PRINT_ERR("No idle TCB in the system!\n");
return NULL; return NULL;
} }
taskCB = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(&g_losFreeTask)); taskCB = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(&g_losFreeTask));//拿到第一节点并通过pendlist拿到完整的TCB
LOS_ListDelete(LOS_DL_LIST_FIRST(&g_losFreeTask)); LOS_ListDelete(LOS_DL_LIST_FIRST(&g_losFreeTask));//从g_losFreeTask链表中摘除自己
SCHEDULER_UNLOCK(intSave); SCHEDULER_UNLOCK(intSave);
return taskCB; return taskCB;
......
...@@ -66,7 +66,7 @@ typedef struct { ...@@ -66,7 +66,7 @@ typedef struct {
} Percpu; } Percpu;
/* the kernel per-cpu structure */ /* the kernel per-cpu structure */
extern Percpu g_percpu[LOSCFG_KERNEL_CORE_NUM]; extern Percpu g_percpu[LOSCFG_KERNEL_CORE_NUM];//CPU核
STATIC INLINE Percpu *OsPercpuGet(VOID) STATIC INLINE Percpu *OsPercpuGet(VOID)
{ {
......
...@@ -239,7 +239,7 @@ typedef struct ProcessCB { ...@@ -239,7 +239,7 @@ typedef struct ProcessCB {
* The process is dying or already dying. * The process is dying or already dying.
*/ */
#define OS_PROCESS_STATUS_INACTIVE (OS_PROCESS_FLAG_EXIT | OS_PROCESS_STATUS_ZOMBIES) #define OS_PROCESS_STATUS_INACTIVE (OS_PROCESS_FLAG_EXIT | OS_PROCESS_STATUS_ZOMBIES)
//不活跃进程定义: 身上贴有退出便签且状态为僵死的进程 //进程不活跃状态定义: 身上贴有退出便签且状态为僵死的进程
/** /**
* @ingroup los_process * @ingroup los_process
* Used to check if the process control block is unused. * Used to check if the process control block is unused.
...@@ -256,7 +256,7 @@ STATIC INLINE BOOL OsProcessIsUnused(const LosProcessCB *processCB)//查下进 ...@@ -256,7 +256,7 @@ STATIC INLINE BOOL OsProcessIsUnused(const LosProcessCB *processCB)//查下进
STATIC INLINE BOOL OsProcessIsInactive(const LosProcessCB *processCB)//查下进程是否不活跃? STATIC INLINE BOOL OsProcessIsInactive(const LosProcessCB *processCB)//查下进程是否不活跃?
{ {
return ((processCB->processStatus & (OS_PROCESS_FLAG_UNUSED | OS_PROCESS_STATUS_INACTIVE)) != 0); return ((processCB->processStatus & (OS_PROCESS_FLAG_UNUSED | OS_PROCESS_STATUS_INACTIVE)) != 0);
} }//进程不活跃函数定义:身上贴有不使用且不活跃标签的进程
/** /**
* @ingroup los_process * @ingroup los_process
......
...@@ -177,16 +177,16 @@ extern SPIN_LOCK_S g_taskSpin; ...@@ -177,16 +177,16 @@ extern SPIN_LOCK_S g_taskSpin;
* *
* The task is joinable. * The task is joinable.
*/ */
#define OS_TASK_FLAG_PTHREAD_JOIN 0x0400U #define OS_TASK_FLAG_PTHREAD_JOIN 0x0400U //主task和子task连在一块不分离
//一个可结合的线程能够被其他线程收回其资源和杀死。在被其他线程回收之前,它的存储器资源(例如栈)是不释放的。
/** /**
* @ingroup los_task * @ingroup los_task
* Flag that indicates the task or task control block status. * Flag that indicates the task or task control block status.
* *
* The task is status detached. * The task is status detached.
*/ */
#define OS_TASK_FLAG_DETACHED 0x0800U #define OS_TASK_FLAG_DETACHED 0x0800U //任务分离 主task与子task分离,子task结束后,资源自动回收
//一个分离的线程是不能被其他线程回收或杀死的,它的存储器资源在它终止时由系统自动释放。
/** /**
* @ingroup los_task * @ingroup los_task
* Flag that indicates the task property. * Flag that indicates the task property.
...@@ -264,7 +264,7 @@ extern SPIN_LOCK_S g_taskSpin; ...@@ -264,7 +264,7 @@ extern SPIN_LOCK_S g_taskSpin;
* @see * @see
*/ */
#define OS_TCB_FROM_PENDLIST(ptr) LOS_DL_LIST_ENTRY(ptr, LosTaskCB, pendList) #define OS_TCB_FROM_PENDLIST(ptr) LOS_DL_LIST_ENTRY(ptr, LosTaskCB, pendList)
//通过pendList取出TCB,用于挂入链表节点时使用 pendList的情况
/** /**
* @ingroup los_task * @ingroup los_task
* @brief Obtain the pointer to a task control block. * @brief Obtain the pointer to a task control block.
...@@ -281,7 +281,7 @@ extern SPIN_LOCK_S g_taskSpin; ...@@ -281,7 +281,7 @@ extern SPIN_LOCK_S g_taskSpin;
* @see * @see
*/ */
#define OS_TCB_FROM_TID(taskID) (((LosTaskCB *)g_taskCBArray) + (taskID)) #define OS_TCB_FROM_TID(taskID) (((LosTaskCB *)g_taskCBArray) + (taskID))
//通过Tid找到TCB
#ifndef LOSCFG_STACK_POINT_ALIGN_SIZE #ifndef LOSCFG_STACK_POINT_ALIGN_SIZE
#define LOSCFG_STACK_POINT_ALIGN_SIZE (sizeof(UINTPTR) * 2) #define LOSCFG_STACK_POINT_ALIGN_SIZE (sizeof(UINTPTR) * 2)
#endif #endif
......
...@@ -112,7 +112,7 @@ VOID OsExcStackInfoReg(const StackInfo *stackInfo, UINT32 stackNum) ...@@ -112,7 +112,7 @@ VOID OsExcStackInfoReg(const StackInfo *stackInfo, UINT32 stackNum)
g_stackInfo = stackInfo; g_stackInfo = stackInfo;
g_stackNum = stackNum; g_stackNum = stackNum;
} }
//栈的初始化,设置固定的值. 0xcccccccc 和 0xcacacaca
VOID OsStackInit(VOID *stacktop, UINT32 stacksize) VOID OsStackInit(VOID *stacktop, UINT32 stacksize)
{ {
/* initialize the task stack, write magic num to stack top */ /* initialize the task stack, write magic num to stack top */
......
...@@ -222,7 +222,7 @@ extern UINT32 __heap_end; // 堆区结束地址 ...@@ -222,7 +222,7 @@ extern UINT32 __heap_end; // 堆区结束地址
* @ingroup los_config * @ingroup los_config
* Configuration item for task (stack) monitoring module tailoring * Configuration item for task (stack) monitoring module tailoring
*/ */
#ifndef LOSCFG_BASE_CORE_TSK_MONITOR #ifndef LOSCFG_BASE_CORE_TSK_MONITOR //任务(栈)监控模块裁剪配置项
#define LOSCFG_BASE_CORE_TSK_MONITOR YES #define LOSCFG_BASE_CORE_TSK_MONITOR YES
#endif #endif
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册