LiteOS内核源码分析系列六 -任务及调度(3)-任务LOS_Task

举报
zhushy 发表于 2021/04/01 08:16:56 2021/04/01
【摘要】 3、任务模块常用操作 3.1 创建和删除任务 3.1.1 创建任务LiteOS提供了4个创建任务的函数,有LOS_TaskCreate、LOS_TaskCreateOnly、LOS_TaskCreateStatic、LOS_TaskCreateOnlyStatic。LOS_TaskCreate和LOS_TaskCreateOnly的区别是,前者创建任务完毕就使任务进入就绪状态,并触发调度,...

3、任务模块常用操作

3.1 创建和删除任务

3.1.1 创建任务

LiteOS提供了4个创建任务的函数,有LOS_TaskCreateLOS_TaskCreateOnlyLOS_TaskCreateStaticLOS_TaskCreateOnlyStaticLOS_TaskCreateLOS_TaskCreateOnly的区别是,前者创建任务完毕就使任务进入就绪状态,并触发调度,如果就绪队列中没有更高优先级的任务,则运行该任务。后者只创建任务,设置任务状态为阻塞suspend状态,需要开发者去调用LOS_TaskResume使该任务进入ready状态。
LOS_TaskCreateStaticLOS_TaskCreateOnlyStatic的区别同上,是否自动去调度。LOS_TaskCreateLOS_TaskCreateOnlyLOS_TaskCreateStaticLOS_TaskCreateOnlyStatic的区别是,创建任务时,任务栈由系统创建还是使用用户提供的任务栈。这4个函数都会调用OsTaskCreateOnly来创建函数,我们以函数LOS_TaskCreate为例一起剖析下创建任务的源码。

函数LOS_TaskCreate代码如下,可以看出创建任务的时候,调用⑴处的函数OsTaskCreateOnly(taskId, initParam, NULL, FALSE),其中FALSE表示不使用用户传入的栈。如果创建精通分配用户栈的任务,需要传递用户栈顶指针、TRUE2个参数,如OsTaskCreateOnly(taskId, initParam, topStack, TRUE)。创建任务后,执行⑶处的代码使任务进行ready就绪状态,下文分别详细分析下这2个函数。

  LITE_OS_SEC_TEXT_INIT UINT32 LOS_TaskCreateOnly(UINT32 *taskId, TSK_INIT_PARAM_S *initParam)
  {return OsTaskCreateOnly(taskId, initParam, NULL, FALSE);
  }

  LITE_OS_SEC_TEXT_INIT UINT32 LOS_TaskCreate(UINT32 *taskId, TSK_INIT_PARAM_S *initParam)
  {
      UINT32 ret;

⑵    ret = LOS_TaskCreateOnly(taskId, initParam);
      if (ret != LOS_OK) {
          return ret;
      }OsTaskResume(taskId);

      return LOS_OK;
  }

我们接着分析下如何使用函数UINT32 OsTaskCreateOnly()创建任务,参数BOOL useUsrStack此时取值为FALSE。⑴处调用OsTaskCreateParamCheck(taskId, initParam, &pool)检测创建任务的参数的合法性。⑵处代码调用OsTaskGetFreeTaskCB(&taskCB)g_losFreeTask空闲任务链表中获取一个可用的taskCB。⑶处调用OsTaskSyncCreate(taskCB),只有开启SMP模式、开启LOSCFG_KERNEL_SMP_TASK_SYNC宏时,才支持多任务同步操作,我们先忽略这个方法的实现。⑷处代码不使用用户栈时,调用函数OsTaskStackAlloc(),申请内存,栈顶为topStack。⑸处代码调用OsTaskStackInit()初始化任务栈,函数返回值是栈指针地址stackPtr,⑹调用函数OsTaskCBInit()初始化任务TCB,然后更新taskId,完成任务的创建。OsTaskStackInit()OsTaskCBInit()的具体实现在下文分析。⑺创建任务成功后,返回任务Id

STATIC UINT32 OsTaskCreateOnly(UINT32 *taskId, TSK_INIT_PARAM_S *initParam, VOID *topStack, BOOL useUsrStack)
{
    UINT32 intSave, errRet;
    VOID *stackPtr = NULL;
    LosTaskCB *taskCB = NULL;
    VOID *pool = NULL;

#ifdef LOSCFG_TASK_STATIC_ALLOCATION
    if (useUsrStack) {
        errRet = OsTaskCreateParamCheckStatic(taskId, initParam, topStack);
    } else {
#endif
⑴      errRet = OsTaskCreateParamCheck(taskId, initParam, &pool);
#ifdef LOSCFG_TASK_STATIC_ALLOCATION
    }
#endif
    if (errRet != LOS_OK) {
        return errRet;
    }

    SCHEDULER_LOCK(intSave);
⑵  errRet = OsTaskGetFreeTaskCB(&taskCB);
    if (errRet != LOS_OK) {
        OS_GOTO_ERREND();
    }
    SCHEDULER_UNLOCK(intSave);

⑶  errRet = OsTaskSyncCreate(taskCB);
    if (errRet != LOS_OK) {
        goto LOS_ERREND_REWIND_TCB;
    }if (useUsrStack == FALSE) {
        OsTaskStackAlloc(&topStack, initParam->uwStackSize, pool);
        if (topStack == NULL) {
            errRet = LOS_ERRNO_TSK_NO_MEMORY;
            goto LOS_ERREND_REWIND_SYNC;
        }
    }
⑸  stackPtr = OsTaskStackInit(taskCB->taskId, initParam->uwStackSize, topStack);OsTaskCBInit(taskCB, initParam, stackPtr, topStack, useUsrStack);

    if (OsConsoleIDSetHook != NULL) {
        OsConsoleIDSetHook(taskCB->taskId, OsCurrTaskGet()->taskId);
    }

#ifdef LOSCFG_KERNEL_CPUP
    OsCpupCB *cpup = OsCpupCBGet(taskCB->taskId);
    cpup->id = taskCB->taskId;
    cpup->status = taskCB->taskStatus;
#endif*taskId = taskCB->taskId;
    return LOS_OK;

LOS_ERREND_REWIND_SYNC:
    OsTaskSyncDestroy(taskCB);
LOS_ERREND_REWIND_TCB:
    SCHEDULER_LOCK(intSave);
    LOS_ListAdd(&g_losFreeTask, &taskCB->pendList);
LOS_ERREND:
    SCHEDULER_UNLOCK(intSave);
    return errRet;
}

最后,来看看函数VOID OsTaskResume(const UINT32 *taskId),⑴处把任务的状态从挂起状态``OS_TASK_STATUS_SUSPEND改为就绪状态OS_TASK_STATUS_READY,⑵处代码先判断是否支持调度,支持调度则调用LOS_Schedule()`函数调度,如果就绪队列中没有更高优先级的任务,则运行该新创建的任务。

STATIC VOID OsTaskResume(const UINT32 *taskId)
{
    UINT32 intSave;
    LosTaskCB *taskCB = NULL;

    taskCB = OS_TCB_FROM_TID(*taskId);

    SCHEDULER_LOCK(intSave);

⑴  taskCB->taskStatus &= ~OS_TASK_STATUS_SUSPEND;
    taskCB->taskStatus |= OS_TASK_STATUS_READY;
    OsPriQueueEnqueue(&taskCB->pendList, taskCB->priority);

    SCHEDULER_UNLOCK(intSave);

    LOS_TRACE(TASK_CREATE, taskCB->taskId, taskCB->taskStatus, taskCB->priority);

    LOS_MpSchedule(OS_MP_CPU_ALL);
    if (OS_SCHEDULER_ACTIVE) {LOS_Schedule();
    }
}

3.1.2 删除任务UINT32 LOS_TaskDelete()

该函数根据传入的参数UINT32 taskId删除任务。我们分析下删除任务的源代码,⑴处检验传入的参数,然后获取任务taskCB。如果是系统任务,或者任务未创建,返回错误码。⑵处如果删除的是运行中的任务,需要调用函数OsTaskDeleteCheckOnRun(taskCB, &errRet)判断是否允许删除,该函数做如下判断:

  • 开启SMP多核时,不允许跨核删除运行状态的任务
  • 不允许调度时,不能删除运行状态的任务
  • 中断处理期间,不能删除运行状态的任务

⑶处如果任务处于就绪队列中,着从就绪队列出队,并设置状态为非就绪状态。如果处于阻塞状态,从阻塞队列中删除。⑷如果任务处于超时等待状态,从超时排序链表中删除。⑸处设置任务状态非阻塞,未创建。如果开启LOSCFG_BASE_IPC_EVENTLOSCFG_LAZY_STACK宏,重置相关的信息。⑹处调用函数OsTaskDelAction(taskCB, taskCB->usrStack)执行删除任务操作。

LITE_OS_SEC_TEXT_INIT UINT32 LOS_TaskDelete(UINT32 taskId)
{
    LosTaskCB *taskCB = NULL;
    UINT32 intSave, errRet;
    UINT16 tempStatus;if (OS_TASK_ID_CHECK_INVALID(taskId)) {
        return LOS_ERRNO_TSK_ID_INVALID;
    }

    taskCB = OS_TCB_FROM_TID(taskId);
    if (taskCB->taskFlags & OS_TASK_FLAG_SYSTEM) {
        return LOS_ERRNO_TSK_OPERATE_SYSTEM_TASK;
    }

    SCHEDULER_LOCK(intSave);

    tempStatus = taskCB->taskStatus;
    if (tempStatus & OS_TASK_STATUS_UNUSED) {
        errRet = LOS_ERRNO_TSK_NOT_CREATED;
        OS_GOTO_ERREND();
    }if ((tempStatus & OS_TASK_STATUS_RUNNING) &&
        !OsTaskDeleteCheckOnRun(taskCB, &errRet)) {
        OS_GOTO_ERREND();
    }if (tempStatus & OS_TASK_STATUS_READY) {
        OsPriQueueDequeue(&taskCB->pendList);
        taskCB->taskStatus &= ~OS_TASK_STATUS_READY;
    } else if (tempStatus & OS_TASK_STATUS_PEND) {
        LOS_ListDelete(&taskCB->pendList);
    }if (tempStatus & (OS_TASK_STATUS_DELAY | OS_TASK_STATUS_PEND_TIME)) {
        OsTimerListDelete(taskCB);
    }

⑸  taskCB->taskStatus &= ~OS_TASK_STATUS_SUSPEND;
    taskCB->taskStatus |= OS_TASK_STATUS_UNUSED;
#ifdef LOSCFG_BASE_IPC_EVENT
    taskCB->event.uwEventID = OS_INVALID_VALUE;
    taskCB->eventMask = 0;
#endif
#ifdef LOSCFG_LAZY_STACK
    taskCB->stackFrame = 0;
#endif
#ifdef LOSCFG_KERNEL_CPUP
    (VOID)memset_s((VOID *)OsCpupCBGet(taskCB->taskId), sizeof(OsCpupCB), 0, sizeof(OsCpupCB));
#endif
    OS_MEM_CLEAR(taskId);

    OsTaskSyncWake(taskCB);if (OsTaskDelAction(taskCB, taskCB->usrStack)) {
        OsSchedResched();
    }

    SCHEDULER_UNLOCK(intSave);
    return LOS_OK;

LOS_ERREND:
    SCHEDULER_UNLOCK(intSave);
    return errRet;
}

接下来,我们分析下负责删除任务的核心函数BOOL OsTaskDelAction(LosTaskCB *taskCB, BOOL useUsrStack)。⑴先处理下运行状态的任务的删除。⑵如果支持静态分配任务并且使用用户栈,则直接把任务加入空闲任务链表g_losFreeTask,否则把任务加入待回收列表g_losFreeTask,等执行空闲任务时再回收。⑶处的函数会把待删除任务的信息备份到任务池的最后g_taskCBArray[g_taskMaxNum],然后把任务taskCB的状态设置为未使用。

⑷处删除非运行状态的任务,把任务taskCB的状态设置为未使用,然后把任务加入空闲任务链表g_losFreeTask。⑸处如果使用的不是用户栈,需要释放任务栈使用的内存空间。LOSCFG_TASK_STACK_PROTECT宏只有部分平台支持,可以忽略。或者任务栈顶指针,然后执行⑹释放内存,完成任务删除。

LITE_OS_SEC_TEXT_INIT STATIC BOOL OsTaskDelAction(LosTaskCB *taskCB, BOOL useUsrStack)
{
    VOID *pool = (VOID *)m_aucSysMem1;
    UINTPTR taskStack;

    LOS_TRACE(TASK_DELETE, taskCB->taskId, taskCB->taskStatus, taskCB->usrStack);if (taskCB->taskStatus & OS_TASK_STATUS_RUNNING) {
#ifdef LOSCFG_TASK_STATIC_ALLOCATIONif (useUsrStack) {
            LOS_ListAdd(&g_losFreeTask, &taskCB->pendList);
        } else {
#endif
            LOS_ListTailInsert(&g_taskRecycleList, &taskCB->pendList);
#ifdef LOSCFG_TASK_STATIC_ALLOCATION
        }
#endifOsTaskDelActionOnRun(taskCB);
        return TRUE;
    }

⑷  taskCB->taskStatus = OS_TASK_STATUS_UNUSED;
    LOS_ListAdd(&g_losFreeTask, &taskCB->pendList);if (useUsrStack == FALSE) {
#ifdef LOSCFG_TASK_STACK_PROTECT
        taskStack = taskCB->topOfStack - MMU_4K;
        OsTaskStackProtect(taskStack, MMU_4K, ACCESS_PERM_RW_RW);
#else
        taskStack = taskCB->topOfStack;
#endif

#ifdef LOSCFG_EXC_INTERACTION
        if (taskStack < (UINTPTR)m_aucSysMem1) {
            pool = (VOID *)m_aucSysMem0;
        }
#endif(VOID)LOS_MemFree(pool, (VOID *)taskStack);
    }
    taskCB->topOfStack = 0;
    return FALSE;
}

3.2 控制任务状态

3.2.1 恢复挂起的任务LOS_TaskResume()

恢复挂起的任务,使该任务进入就绪状态,和下文中的LOS_TaskSuspend()成对使用。⑴处获取任务的TCB,执行⑵清理任务的信号标记为非挂起状态。⑶处获取任务的状态进行判断,如果任务没有创建或者不是挂起状态,则返回相应的错误码。 ⑷设置任务状态非挂起。⑸检查任务状态是否为OS_CHECK_TASK_BLOCK,即(OS_TASK_STATUS_DELAY | OS_TASK_STATUS_PEND | OS_TASK_STATUS_SUSPEND)中的一种,这几个状态影响恢复挂起的任务。⑹把任务状态改为就绪状态,入队就绪队列,并标记是否需要调度needSched = TRUE。⑺如果需要调度,则触发调度。

LITE_OS_SEC_TEXT_INIT UINT32 LOS_TaskResume(UINT32 taskId)
{
    UINT32 intSave;
    LosTaskCB *taskCB = NULL;
    UINT16 tempStatus;
    UINT32 errRet;
    BOOL needSched = FALSE;

    if (OS_TASK_ID_CHECK_INVALID(taskId)) {
        return LOS_ERRNO_TSK_ID_INVALID;
    }

 ⑴ taskCB = OS_TCB_FROM_TID(taskId);

    SCHEDULER_LOCK(intSave);

    /* clear pending signal */
⑵  taskCB->signal &= ~SIGNAL_SUSPEND;

⑶  tempStatus = taskCB->taskStatus;

    if (tempStatus & OS_TASK_STATUS_UNUSED) {
        errRet = LOS_ERRNO_TSK_NOT_CREATED;
        OS_GOTO_ERREND();
    } else if (!(tempStatus & OS_TASK_STATUS_SUSPEND)) {
        errRet = LOS_ERRNO_TSK_NOT_SUSPENDED;
        OS_GOTO_ERREND();
    }

⑷  taskCB->taskStatus &= ~OS_TASK_STATUS_SUSPEND;if (!(taskCB->taskStatus & OS_CHECK_TASK_BLOCK)) {
⑹      taskCB->taskStatus |= OS_TASK_STATUS_READY;
        OsPriQueueEnqueue(&taskCB->pendList, taskCB->priority);
        if (OS_SCHEDULER_ACTIVE) {
            needSched = TRUE;
        }
    }

    SCHEDULER_UNLOCK(intSave);

    LOS_TRACE(TASK_RESUME, taskCB->taskId, taskCB->taskStatus, taskCB->priority);

    if (needSched) {LOS_MpSchedule(OS_MP_CPU_ALL);
        LOS_Schedule();
    }

    return LOS_OK;

LOS_ERREND:
    SCHEDULER_UNLOCK(intSave);
    return errRet;
}

3.2.2 挂起指定的任务LOS_TaskSuspend()

函数用于挂起指定的任务。⑴处获取任务的TCB,如果当前任务是系统任务,返回错误码。⑵处开始获取任务的状态进行判断,如果任务没有创建、任务已经挂起,返回相应的错误码。⑶处如果任务是运行状态,并且不满足挂起任务的条件时,跳转到LOS_ERREND结束挂起操作。BOOL OsTaskSuspendCheckOnRun(LosTaskCB *taskCB, UINT32 *ret)可以检查运行的任务能否挂起的条件,包括:

  • 开启SMP多核时,不允许跨核挂起运行状态的任务
  • 不允许调度时,不能挂起运行状态的任务
  • 中断处理期间,不能挂起运行状态的任务

⑷处如果任务是就绪状态,从就绪队列出队,并取消任务的就绪状态,下一条语句设置任务状态为阻塞状态。⑸处获取当前任务,如果挂起的是当前任务,则需要执行⑹触发调度。

LITE_OS_SEC_TEXT_INIT UINT32 LOS_TaskSuspend(UINT32 taskId)
{
    UINT32 intSave;
    LosTaskCB *taskCB = NULL;
    LosTaskCB *runTask = NULL;
    UINT16 tempStatus;
    UINT32 errRet;

    if (OS_TASK_ID_CHECK_INVALID(taskId)) {
        return LOS_ERRNO_TSK_ID_INVALID;
    }

⑴  taskCB = OS_TCB_FROM_TID(taskId);
    if (taskCB->taskFlags & OS_TASK_FLAG_SYSTEM) {
        return LOS_ERRNO_TSK_OPERATE_SYSTEM_TASK;
    }

    SCHEDULER_LOCK(intSave);
⑵  tempStatus = taskCB->taskStatus;
    if (tempStatus & OS_TASK_STATUS_UNUSED) {
        errRet = LOS_ERRNO_TSK_NOT_CREATED;
        OS_GOTO_ERREND();
    }

    if (tempStatus & OS_TASK_STATUS_SUSPEND) {
        errRet = LOS_ERRNO_TSK_ALREADY_SUSPENDED;
        OS_GOTO_ERREND();
    }if ((tempStatus & OS_TASK_STATUS_RUNNING) &&
        !OsTaskSuspendCheckOnRun(taskCB, &errRet)) {
        OS_GOTO_ERREND();
    }if (tempStatus & OS_TASK_STATUS_READY) {
        OsPriQueueDequeue(&taskCB->pendList);
        taskCB->taskStatus &= ~OS_TASK_STATUS_READY;
    }

    taskCB->taskStatus |= OS_TASK_STATUS_SUSPEND;

⑸  runTask = OsCurrTaskGet();

    LOS_TRACE(TASK_SUSPEND, taskCB->taskId, taskCB->taskStatus, runTask->taskId);

    if (taskId == runTask->taskId) {OsSchedResched();
    }

    SCHEDULER_UNLOCK(intSave);
    return LOS_OK;

LOS_ERREND:
    SCHEDULER_UNLOCK(intSave);
    return errRet;
}

3.2.3 任务延时等待LOS_TaskDelay()

任务延时等待,释放CPU,等待时间到期后该任务会重新进入就绪状态。⑴处代码获取当前运行任务,如果当前任务是系统任务,则打印回溯栈并返回错误码。⑵如果不可调度,返回错误码。
⑶处如果延迟的时间为0,则执行让权操作,否则执行⑷,把当前任务取消就绪状态并加入排序链表进行等待,然后调用OsSchedResched()触发调度。

LITE_OS_SEC_TEXT UINT32 LOS_TaskDelay(UINT32 tick)
{
    UINT32 intSave;
    LosTaskCB *runTask = NULL;

    if (OS_INT_ACTIVE) {
        return LOS_ERRNO_TSK_DELAY_IN_INT;
    }

⑴  runTask = OsCurrTaskGet();
    if (runTask->taskFlags & OS_TASK_FLAG_SYSTEM) {
        OsBackTrace();
        return LOS_ERRNO_TSK_OPERATE_SYSTEM_TASK;
    }if (!OsPreemptable()) {
        return LOS_ERRNO_TSK_DELAY_IN_LOCK;
    }if (tick == 0) {
        return LOS_TaskYield();
    } else {
        SCHEDULER_LOCK(intSave);OsTaskAdd2TimerList(runTask, tick);
        runTask->taskStatus |= OS_TASK_STATUS_DELAY;
        OsSchedResched();
        SCHEDULER_UNLOCK(intSave);
    }

    return LOS_OK;
}

3.2.4 任务让权LOS_TaskYield()

当前任务释放CPU,并将其移到具有相同优先级的就绪任务队列的末尾。⑴处如果当前处理中断,返回错误码。⑵处判断是否可以抢占调度,如果不可调度,返回错误码。

⑶处获取当前运行中的任务runTask,执行⑷获取当前任务优先级下的就绪任务的数量。如果数量不足,返回错误。否则执行⑸,把当前任务改为就绪状态并入队就绪队列。然后,执行⑹触发调度。

LITE_OS_SEC_TEXT_MINOR UINT32 LOS_TaskYield(VOID)
{
    UINT32 tskCount;
    UINT32 intSave;
    LosTaskCB *runTask = NULL;if (OS_INT_ACTIVE) {
        return LOS_ERRNO_TSK_YIELD_IN_INT;
    }if (!OsPreemptable()) {
        return LOS_ERRNO_TSK_YIELD_IN_LOCK;
    }

⑶  runTask = OsCurrTaskGet();
    if (runTask->taskId >= g_taskMaxNum) {
        return LOS_ERRNO_TSK_ID_INVALID;
    }

    SCHEDULER_LOCK(intSave);

#ifdef LOSCFG_BASE_CORE_TIMESLICE
    /* reset timeslice of yielded task */
    runTask->timeSlice = 0;
#endif

⑷  tskCount = OsPriQueueSize(runTask->priority);
    if (tskCount > 0) {
        runTask->taskStatus |= OS_TASK_STATUS_READY;OsPriQueueEnqueue(&(runTask->pendList), runTask->priority);
    } else {
        SCHEDULER_UNLOCK(intSave);
        return LOS_ERRNO_TSK_YIELD_NOT_ENOUGH_TASK;
    }OsSchedResched();
    SCHEDULER_UNLOCK(intSave);
    return LOS_OK;
}

3.3 控制任务调度

3.3.1 锁任务调度LOS_TaskLock()

锁任务调度LOS_TaskLock()比较简单,获取当前CPU对应的任务锁调度计数器,然后对其增加1。

LITE_OS_SEC_TEXT_MINOR VOID LOS_TaskLock(VOID)
{
    UINT32 intSave;
    UINT32 *losTaskLock = NULL;

    intSave = LOS_IntLock();
    losTaskLock = &OsPercpuGet()->taskLockCnt;
    (*losTaskLock)++;
    LOS_IntRestore(intSave);
}

3.3.2 解锁任务调度LOS_TaskUnlock()

我们看看解锁任务调度函数LOS_TaskUnlock(),⑴处获取当前CPU对应的任务锁调度计数器,如果数值大于0,对其减1。⑵处如果任务锁调度计数器等于0,并且等待的调度,则执行⑶处触发调度。这里,也解释了INT_PEND_RESCH请求调度标记的用途。

LITE_OS_SEC_TEXT_MINOR VOID LOS_TaskUnlock(VOID)
{
    UINT32 intSave;
    UINT32 *losTaskLock = NULL;
    Percpu *percpu = NULL;

    intSave = LOS_IntLock();

    percpu = OsPercpuGet();
⑴  losTaskLock = &percpu->taskLockCnt;
    if (*losTaskLock > 0) {
        (*losTaskLock)--;if ((*losTaskLock == 0) && (percpu->schedFlag == INT_PEND_RESCH) &&
            OS_SCHEDULER_ACTIVE) {
            percpu->schedFlag = INT_NO_RESCH;
            LOS_IntRestore(intSave);LOS_Schedule();
            return;
        }
    }

    LOS_IntRestore(intSave);
}
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。