HarmonyOS内核进程调度相关函数

一、调度初始化函数 OsSchedInit()

调度初始化函数 UINT32 OsSchedInit() 在任务初始化函数 UINT32 OsTaskInit() 中调用。主要是初始化任务就绪队列以及初始化任务排序链表。

UINT32 OsSchedInit(VOID)
{
    UINT16 index, pri;
    UINT32 ret;

    g_sched = (Sched *)LOS_MemAlloc(m_aucSysMem0, sizeof(Sched));//分配32个队列头节点
    if (g_sched == NULL) {
        return LOS_ERRNO_TSK_NO_MEMORY;
    }

    (VOID)memset_s(g_sched, sizeof(Sched), 0, sizeof(Sched));

    for (index = 0; index < OS_PRIORITY_QUEUE_NUM; index++) {
        SchedQueue *queueList = &g_sched->queueList[index];
        LOS_DL_LIST *priList = &queueList->priQueueList[0];
        for (pri = 0; pri < OS_PRIORITY_QUEUE_NUM; pri++) {
            LOS_ListInit(&priList[pri]);//队列初始化,前后指针指向自己
        }
    }

    for (index = 0; index < LOSCFG_KERNEL_CORE_NUM; index++) {
        Percpu *cpu = OsPercpuGetByID(index);
        ret = OsSortLinkInit(&cpu->taskSortLink);
        if (ret != LOS_OK) {
            return LOS_ERRNO_TSK_NO_MEMORY;
        }
        cpu->responseTime = OS_SCHED_MAX_RESPONSE_TIME;
        LOS_SpinInit(&cpu->taskSortLinkSpin);
        LOS_SpinInit(&cpu->swtmrSortLinkSpin);
    }

    g_sched->taskScan = OsSchedScanTimerList;

#ifdef LOSCFG_SCHED_TICK_DEBUG
    ret = OsSchedDebugInit();
    if (ret != LOS_OK) {
        return ret;
    }
#endif
    return LOS_OK;
}

g_sched作用在文章HarmonyOS内核进程调度已做了解释。

二、任务调度函数 LOS_Schedule()

任务调度函数 LOS_Schedule() 。当系统完成初始化开始调度,并且没有锁任务调度时,进行任务调度。无论是时间中断还是线程的创建还是线程的销毁,都会调用一次 LOS_Schedule() 来调度下一个线程给 CPU 运行。具体过程为:
调用 OsTimeSliceUpdate() 中更新时间片,设置新任务开始运行时间;
调用 OsSchedTaskEnQueue() 如果是抢占式,把当前运行任务放入就绪队列队首,如果不是抢占式,是FIFO,把当前运行任务放入就绪队列队尾;
调用 OsSchedResched() 获取就绪队列中优先级最高的任务,把该任务状态设置为运行状态,把新任务从就绪队列中出队。

/* 调度切换 */
VOID LOS_Schedule(VOID)
{
    UINT32 intSave;
    LosTaskCB *runTask = OsCurrTaskGet();  //当前运行任务

    if (OS_INT_ACTIVE) {  //系统正在处理中断
        OsPercpuGet()->schedFlag |= INT_PEND_RESCH;
        return;
    }

    if (!OsPreemptable()) {  //判断是否可以抢占调度时,先关中断,避免当前的任务迁移到其他核,返回错误的是否可以抢占调度状态
        return;
    }

    /*
     * trigger schedule in task will also do the slice check
     * if neccessary, it will give up the timeslice more in time.
     * otherwhise, there's no other side effects.
     */
    SCHEDULER_LOCK(intSave);  //在执行任务调度的过程要确保关中断

    OsTimeSliceUpdate(runTask, OsGetCurrSchedTimeCycle());

    /* add run task back to ready queue */
    OsSchedTaskEnQueue(runTask);

    /* reschedule to new thread */
    OsSchedResched();

    SCHEDULER_UNLOCK(intSave);  //开中断
}

OsTimeSliceUpdate()

传入的参数为当前任务和当前调度经历的时间周期。首先通过 LOS_ ASSERT() 函数,验证当前时间的合法性,即 currTime >= taskCB->startTime。
定义一个临时变量 intTime 为当前时间-任务开始时间-中断消耗时间。认为是当前任务的在该时间片下执行时间。
再次调用 LOS_ASSERT() 函数检验这个参数的合法性,即是否大于零,如果不合法就会陷入循环等待,直到合法为止。
若合法,判断任务调度的方式是不是时间片轮转的方式,如果是时间片轮转的方式,更新剩余时间片为原始时间片 timeSlice 减去在当前时间片下已经用掉的时间片incTime,每一个任务的实际使用时间是之前时间片使用的时间 + 当前时间片使用的时间。此时以保留当前时间片的相关信息,为下一个时间片使用时间准备。
初始化中断消耗的时间,将中断时间设置为 0;
初始化任务开始的时间为当前时间,开始运行时间设置为当前时间。如果是时间片轮转算法,即上一个时间片用完的时间,或者被抢占后的当前时间。如果是 FIFO 算法,即是上一个线程执行结束的时间,或者是被抢占的时间。

/* 更新时间片 */
STATIC INLINE VOID OsTimeSliceUpdate(LosTaskCB *taskCB, UINT64 currTime)
{
    LOS_ASSERT(currTime >= taskCB->startTime);

    INT32 incTime = (currTime - taskCB->startTime - taskCB->irqUsedTime);  //任务运行时间

    LOS_ASSERT(incTime >= 0);

    if (taskCB->policy == LOS_SCHED_RR) {  //判断调度算法是否为时间轮转算法,因为FiFO算法的时间永远大于0,除非yield
        taskCB->timeSlice -= incTime;  //更新剩余时间片
#ifdef LOSCFG_SCHED_DEBUG
        taskCB->schedStat.timeSliceRealTime += incTime;
#endif
    }
    taskCB->irqUsedTime = 0;  //中断时间设置为0
    taskCB->startTime = currTime;  //开始运行时间设置为当前时间

#ifdef LOSCFG_SCHED_DEBUG
    taskCB->schedStat.allRuntime += incTime;
#endif
}

OsSchedTaskEnQueue()

OsSchedTaskEnQueue() 该函数实现将当前任务插入就绪队列,根据当前程序状态决定将当前任务是入队首还是队尾。
该函数的主要功能通过函数内调用的 OchedEnTaskQueue() 函数以及在调用前的准备工作共同实现。其准备工作主要是准备 OchedEnTaskQueue() 函数所需要的参数。
将该函数本身的 TCB 参数与由当前 TCB 获得的任务所属进程 PCB 作为入 OsSchedEnTaskQueue() 函数中的两个参数。简言之,这个函数就是找对应进程实体,并和线程一起传入 OsSchedEnTaskQueue() 函数中。

/* 任务入调度队列 */
VOID OsSchedTaskEnQueue(LosTaskCB *taskCB)
{
    LosProcessCB *processCB = OS_PCB_FROM_PID(taskCB->processID);//获得当前线程的进程PCB
#ifdef LOSCFG_SCHED_DEBUG
    if (!(taskCB->taskStatus & OS_TASK_STATUS_RUNNING)) {
        taskCB->startTime = OsGetCurrSchedTimeCycle();
    }
#endif
    OsSchedEnTaskQueue(taskCB, processCB);  //入任务调度队列
}

OsSchedEnTaskQueue()

将任务入队。

/* 入任务调度队列 */
STATIC INLINE VOID OsSchedEnTaskQueue(LosTaskCB *taskCB, LosProcessCB *processCB)
{
    LOS_ASSERT(!(taskCB->taskStatus & OS_TASK_STATUS_READY));

    switch (taskCB->policy) {  //根据任务的调度模式进行判断
        case LOS_SCHED_RR: {  //时间片轮转调度模式
            if (taskCB->timeSlice > OS_TIME_SLICE_MIN/* 50us */) { //该任务时间片未使用完就发生了调度,说明是被抢占了
                OsSchedPriQueueEnHead(processCB->priority, &taskCB->pendList, taskCB->priority);  //将该任务入任务调度队列首,以便下次调度该就绪队列时继续消耗时间片
            } else {  //时间片使用完了,说明是正常的时间片轮转发生调度切换
                taskCB->initTimeSlice = OsSchedCalculateTimeSlice(processCB->priority, taskCB->priority);  //重新计算并分配的时间片
                taskCB->timeSlice = taskCB->initTimeSlice;  //并赋值给剩余时间片用于下次调度使用
                OsSchedPriQueueEnTail(processCB->priority, &taskCB->pendList, taskCB->priority);  //将该任务入任务调度队列尾
#ifdef LOSCFG_SCHED_DEBUG
                taskCB->schedStat.timeSliceTime = taskCB->schedStat.timeSliceRealTime;
                taskCB->schedStat.timeSliceCount++;
#endif
            }
            break;
        }
        case LOS_SCHED_FIFO: {  //先进先出调度模式
            /* The time slice of FIFO is always greater than 0 unless the yield is called */
            if ((taskCB->timeSlice > OS_TIME_SLICE_MIN) && (taskCB->taskStatus & OS_TASK_STATUS_RUNNING)) {//RUNNING,发生了抢占
                /* 虽然根据上述英文注释可以看出if的第一个判断是句废话,但任然有存在的必要 */
                OsSchedPriQueueEnHead(processCB->priority, &taskCB->pendList, taskCB->priority);  //入队首
            } else {//NOT RUNNING,正常的结束发生切换
                taskCB->initTimeSlice = OS_SCHED_FIFO_TIMEOUT;  //调度结束TIMEOUT
                taskCB->timeSlice = taskCB->initTimeSlice;
                OsSchedPriQueueEnTail(processCB->priority, &taskCB->pendList, taskCB->priority);  //将该任务入任务调度队列尾
            }
            break;
        }
        case LOS_SCHED_IDLE:
#ifdef LOSCFG_SCHED_DEBUG
            taskCB->schedStat.timeSliceCount = 1;
#endif
            break;
        default:
            LOS_ASSERT(0);
            break;
    }

    taskCB->taskStatus &= ~OS_TASK_STATUS_BLOCKED;  //从阻塞状态切换
    taskCB->taskStatus |= OS_TASK_STATUS_READY;  //切换至就绪状态

    processCB->processStatus &= ~(OS_PROCESS_STATUS_INIT | OS_PROCESS_STATUS_PENDING);  //从初始化和挂起状态切换
    processCB->processStatus |= OS_PROCESS_STATUS_READY;  //切换至就绪状态
    processCB->readyTaskNum++;  //进程的就绪任务数加一
}

使用断言LOS_ASSERT函数检测taskCB 不能为就绪状态,根源在于**OsCurrTaskGet()**函数获得的是当前运行在 CPU 上的任务,因此要么运行要么阻塞。在抢占式模式下:

  1. 任务的剩余时间片大于 OS_TIME_SLICE_MIN(50 微秒), 即说明该任务的时间片没有使用完就发生了调度,即发生了抢占,此时调用函数 OsSchedPriQueueEnHead(),传入参数为进程优先级、任务阻塞时所挂的链表、任务优先级。其中进程优先级和任务优先级用来决定任务插入具体哪条链表的头部,而任务阻塞时所挂的链表的作用在于由于该任务被抢占因此它能够在对应 TCB 的阻塞链表上找到。且由于该任务被抢占而并非执行完一整个时间片,所以插在对应优先级链表的头部,保证其相比同一链表上的任务有一定优先权。
    (a) 若当前进程的任务优先级调度位图为 0,则在全局调度中将对应进程的优先级置 1。
    (b) 若当前进程优先级最高的任务调度队列中当前任务优先级的位置为空,则在任务优先级调度位图中,将当前任务所在的优先级位置 1。
    将挂有任务的阻塞链表插入当前进程优先级最高的任务调度队列中当前任务优先级队列的头部。
    当前任务所在优先级的就绪任务数加 1。
  2. 任务的剩余时间片小于 OS_TIME_SLICE_MIN(50 微秒)。
    (a) 根据进程优先级与任务优先级重新计算任务的初始时间片。
    (b) 将挂有任务的阻塞链表插入当前进程优先级最高的任务调度队列中当前任务优先级队列的尾部
    在先进先出的模式下:
    根据先进先出的原则,所有任务只有执行完初始时间片长度的时间才能够被插入到尾部,因此若剩余时间片充足且任务在运行中,则插入头部;否则将剩余时间片及初始时间片置为 OS_SCHED_FIFO_TIM,并将任务插入尾部。
    解除任务 BLOCKED 的相关状态位(置为 0),将就绪对应位置为 1。
    进程初始化、阻塞对应位置为 0,就绪对应位置为 1,进程中就绪任务数加 1。

OsSchedPriQueueEnHead()

以头插的方式入队列。

/* 入任务调度队列的方式 */
STATIC INLINE VOID OsSchedPriQueueEnHead(UINT32 proPriority, LOS_DL_LIST *priqueueItem, UINT32 priority)
{
    /* 头插入队列 */
    SchedQueue *queueList = &g_sched->queueList[proPriority];
    LOS_DL_LIST *priQueueList = &queueList->priQueueList[0];
    UINT32 *bitMap = &queueList->queueBitmap;

    /*
     * Task control blocks are inited as zero. And when task is deleted,
     * and at the same time would be deleted from priority queue or
     * other lists, task pend node will restored as zero.
     */
    LOS_ASSERT(priqueueItem->pstNext == NULL);

    if (*bitMap == 0) {  //当前进程调度队列没有任务调度
        g_sched->queueBitmap |= PRIQUEUE_PRIOR0_BIT >> proPriority;  //全局调度器对应进程调度队列进行标记
    }

    if (LOS_ListEmpty(&priQueueList[priority])) {  //对应优先级的任务调度队列为空
        *bitMap |= PRIQUEUE_PRIOR0_BIT >> priority;//任务调度队列进行标记
    }

    LOS_ListHeadInsert(&priQueueList[priority], priqueueItem);  //常见的链表头插算法
    queueList->readyTasks[priority]++;  //就绪任务数加一
}

OsSchedCalculateTimeSlice()

/* 时间片初始化计算算法 */
STATIC INLINE UINT32 OsSchedCalculateTimeSlice(UINT16 proPriority, UINT16 priority)
{
    /* 根据当前任务队列就绪个数占最大可就绪个数的比例进行时间片计算 */
    UINT32 ratTime, readTasks;

    SchedQueue *queueList = &g_sched->queueList[proPriority];  //该优先级任务调度队列
    readTasks = queueList->readyTasks[priority];  //该优先级任务调度队列中就绪任务个数
    if (readTasks > OS_SCHED_READY_MAX) {  //该优先级任务调度队列中就绪任务个数超过最大值
        return OS_SCHED_TIME_SLICES_MIN;  //不分配时间片(最小时间片)
    }
    ratTime = (/*剩余可就绪个数*/(OS_SCHED_READY_MAX - readTasks) * /*可调度时间片幅度*/OS_SCHED_TIME_SLICES_DIFF) / /*最大可就绪个数*/OS_SCHED_READY_MAX;
    /* ratTime指的是在最小可调度时间片的基础上多出的时间片部分,(0%~100%)*diff = 0ms~15ms */
    return (ratTime + OS_SCHED_TIME_SLICES_MIN);  //5ms+(0ms~15ms)=5ms~20ms
}

OsSchedPriQueueEnTail()

以尾插的方式入队列

STATIC INLINE VOID OsSchedPriQueueEnTail(UINT32 proPriority, LOS_DL_LIST *priqueueItem, UINT32 priority)
{
    /* 尾插入队列 */
    SchedQueue *queueList = &g_sched->queueList[proPriority];
    LOS_DL_LIST *priQueueList = &queueList->priQueueList[0];
    UINT32 *bitMap = &queueList->queueBitmap;

    /*
     * Task control blocks are inited as zero. And when task is deleted,
     * and at the same time would be deleted from priority queue or
     * other lists, task pend node will restored as zero.
     */
    LOS_ASSERT(priqueueItem->pstNext == NULL);

    if (*bitMap == 0) {
        g_sched->queueBitmap |= PRIQUEUE_PRIOR0_BIT >> proPriority;
    }

    if (LOS_ListEmpty(&priQueueList[priority])) {
        *bitMap |= PRIQUEUE_PRIOR0_BIT >> priority;
    }

    LOS_ListTailInsert(&priQueueList[priority], priqueueItem);  //常见的链表尾插算法
    queueList->readyTasks[priority]++;
}

OsSchedResched()

申请调度。首先通过断言函数检测相关参数的正确性。接着,将当前执行任务的 CPU 的调度标识中标志阻塞位(最低位)置为 0。获取当前任务和优先级最高的任务。之后,判断当前任务和新任务是否是一个任务。若当前任务即优先级最高的任务,则退出(不用进入后续程序切换)。如果当前任务和新任务不是同一个任务,则调用函数 OsSchedTaskSwicth(),进行任
务调度切换。

/* 抢占调度 */
VOID OsSchedResched(VOID)
{
    LOS_ASSERT(LOS_SpinHeld(&g_taskSpin));
#ifdef LOSCFG_KERNEL_SMP
    LOS_ASSERT(OsPercpuGet()->taskLockCnt == 1);
#else
    LOS_ASSERT(OsPercpuGet()->taskLockCnt == 0);
#endif

    OsPercpuGet()->schedFlag &= ~INT_PEND_RESCH;
    LosTaskCB *runTask = OsCurrTaskGet();  //当前运行任务
    LosTaskCB *newTask = OsGetTopTask();  //任务就绪队列中优先级最高的任务
    if (runTask == newTask) {  //不存在抢占
        return;
    }

    OsSchedTaskSwicth(runTask, newTask);  //执行任务调度切换
}

OsCurrTaskGet()

获得当前任务。

OsGetTopTask()

获得优先级最高的任务并出队列。
OsGetTopTask() 函数在所有的进程和线程中找到优先级最高的线程。循环查找进程位图。通过函数 CLZ 得到计算操作数最高端 0 的个数,当下进程中的最高优先级。并获得对应进程。
循环对应进程的位图,通过函数 CLZ 获得当然任务队列中最高的优先级,并通过函数 LOS_DL_LIST_FOR_EACH_ENTRY 遍历最高优先级的任务队列。在时间轮换调度算法中,处在最高优先级的任务队列,循环得到时间片,循环执行。(?) 循环结束后,将位图中线程对应优先级位置置为 0。并将位图中进程对应优先级位置置为 0。
通过 cpu 的 idle 任务的 ID 获得任务实体,赋给 newTask。
调用函数 OsSchedDeTaskQueue() 找到最高优先级就绪任务,并出队列。
返回的室最够优先级的就绪任务队列。

/* 获取最高优先级就绪任务并出队列 */
STATIC LosTaskCB *OsGetTopTask(VOID)
{
    UINT32 priority, processPriority;
    UINT32 bitmap;
    LosTaskCB *newTask = NULL;
    UINT32 processBitmap = g_sched->queueBitmap;
#ifdef LOSCFG_KERNEL_SMP
    UINT32 cpuid = ArchCurrCpuid();
#endif

    while (processBitmap) {
        processPriority = CLZ(processBitmap);  //得到位图中高位前导0的个数,即得到进程最高优先级
        SchedQueue *queueList = &g_sched->queueList[processPriority];  //获取对应进程
        bitmap = queueList->queueBitmap;
            while (bitmap) {
                priority = CLZ(bitmap);  //得到位图中高位前导0的个数,即得到任务最高优先级
                LOS_DL_LIST_FOR_EACH_ENTRY(newTask, &queueList->priQueueList[priority], LosTaskCB, pendList) {  //遍历最高优先级的任务队列
#ifdef LOSCFG_KERNEL_SMP
                    if (newTask->cpuAffiMask & (1U << cpuid)) {
#endif
                        goto FIND_TASK;
#ifdef LOSCFG_KERNEL_SMP
                    }
#endif
                }
            bitmap &= ~(1U << (OS_PRIORITY_QUEUE_NUM - priority - 1));  //对应优先级位置0
        }
        processBitmap &= ~(1U << (OS_PRIORITY_QUEUE_NUM - processPriority - 1));  //对应优先级位置0
    }

    newTask = OS_TCB_FROM_TID(OsPercpuGet()->idleTaskID);

FIND_TASK:
    OsSchedDeTaskQueue(newTask, OS_PCB_FROM_PID(newTask->processID));  //找到最高优先级就绪任务,并出队列
    return newTask;  //返回该任务
}

三、任务调度切换函数 OsSchedTaskSwicth()

将当前和新任务进行切换。runTask 指向当前正在执行的任务,newTask 执行即将被
执行的函数。
(1)任务确认:调用函数 OsSchedSwitchCheck() 对新任务和正在进行的任务进行确认。更改任务状态位:将当前任务的状态的改为非运行状态,并将新的任务的运行状态对应位改为 1。
(2)重置当前任务:通过调用函数 OsCurrTaskSet(),设置新任务为当前任务。
(3)进程切换:获取新任务和正在执行的任务的所属进程,并判断是否是同一个进程。如果不是同一个进程,就执行进程切换,调用并执行函数 OsSchedSwitchProcess()
(4)处理用户态进程:判断新进程是不是用户态进程,如果是用户态进程,则需要限制该进程的执行范围,不可超过用户态而使用系统态。调用并执行函数 OsCurrUserTaskSet()
(5)设置新任务的开始时间:判断当前任务是不是就绪状态,如果该任务是就绪状态,将新任务的开始时间更新为当前时间。如果新的任务不是就绪状态,说明该任务是阻塞状态,还是将任务开始时间设置为当前时间,并调用并执行函数 OsTimeSliceUpdate,更新当前正在执行的任务时间片。
(6)处理等待时间过长的任务:如果当前的任务处于 OS_TASK_STATUS_PEND_TIME状态或者 OS_TASK_STATUS_DELAY 状态。将调用函数 OsAdd2SortLink(),将延迟或者等待时间过长的任务放在任务列表的头部。
(7)设置新任务的结束时间:如果新任务的执行算法是时间片轮转算法,则设置新任务的结束时间为新任务的开始时间 + 时间片时间。如果新任务的不是执行时间片轮转算法,执行的是先入先出算法,任务执行结束的时间为调度最大响应时间-时间片响应精度?
(8)设置下一过期时间:调用函数 OsSchedSetNextExpireTime(),设置下次过期时间。
(9)最后,调用函数 OsTaskSchedule() 执行任务调度, 并将寄存器值按 TaskContext 的格式保存起来。

/* 任务调度切换 */
STATIC VOID OsSchedTaskSwicth(LosTaskCB *runTask, LosTaskCB *newTask)
{
    UINT64 endTime;

    OsSchedSwitchCheck(runTask, newTask);

    runTask->taskStatus &= ~OS_TASK_STATUS_RUNNING;
    newTask->taskStatus |= OS_TASK_STATUS_RUNNING;

#ifdef LOSCFG_KERNEL_SMP
    /* mask new running task's owner processor */
    runTask->currCpu = OS_TASK_INVALID_CPUID;
    newTask->currCpu = ArchCurrCpuid();
#endif

    OsCurrTaskSet((VOID *)newTask);  //设置新的任务为当前任务
    LosProcessCB *newProcess = OS_PCB_FROM_PID(newTask->processID);
    LosProcessCB *runProcess = OS_PCB_FROM_PID(runTask->processID);
    if (runProcess != newProcess) {  //上一个任务与下一个任务不属于同一进程
        OsSchedSwitchProcess(runProcess, newProcess);  //进程需要切换
    }

    if (OsProcessIsUserMode(newProcess)) {  //判断新进程是不是用户态
        OsCurrUserTaskSet(newTask->userArea);  //汇编mcr
    }

#ifdef LOSCFG_KERNEL_CPUP
    OsCpupCycleEndStart(runTask->taskID, newTask->taskID);
#endif

#ifdef LOSCFG_SCHED_DEBUG
    UINT64 waitStartTime = newTask->startTime;
#endif
    if (runTask->taskStatus & OS_TASK_STATUS_READY) {
        /* When a thread enters the ready queue, its slice of time is updated */
        newTask->startTime = runTask->startTime;  //此时的runTask->startTime就是切换时的currTime
    } else {
        /* The currently running task is blocked */
        newTask->startTime = OsGetCurrSchedTimeCycle();
        /* The task is in a blocking state and needs to update its time slice before pend */
        OsTimeSliceUpdate(runTask, newTask->startTime);

        if (runTask->taskStatus & (OS_TASK_STATUS_PEND_TIME | OS_TASK_STATUS_DELAY)) {
            OsAdd2SortLink(&runTask->sortList, runTask->startTime, runTask->waitTimes, OS_SORT_LINK_TASK);
        }
    }

    if (newTask->policy == LOS_SCHED_RR) {  //新的任务以时间片轮转算法
        endTime = newTask->startTime + newTask->timeSlice;//endTime
    } else {
        endTime = OS_SCHED_MAX_RESPONSE_TIME - OS_TICK_RESPONSE_PRECISION;
    }
    OsSchedSetNextExpireTime(newTask->startTime, newTask->taskID, endTime, runTask->taskID);

#ifdef LOSCFG_SCHED_DEBUG
    newTask->schedStat.waitSchedTime += newTask->startTime - waitStartTime;
    newTask->schedStat.waitSchedCount++;
    runTask->schedStat.runTime = runTask->schedStat.allRuntime;
    runTask->schedStat.switchCount++;
#endif
    /* do the task context switch */
    OsTaskSchedule(newTask, runTask);
}

OsSchedSwitchCheck()

/* 任务切换检查 */
STATIC INLINE VOID OsSchedSwitchCheck(LosTaskCB *runTask, LosTaskCB *newTask)
{
#ifdef LOSCFG_BASE_CORE_TSK_MONITOR
    OsTaskStackCheck(runTask, newTask);
#endif /* LOSCFG_BASE_CORE_TSK_MONITOR */
    OsHookCall(LOS_HOOK_TYPE_TASK_SWITCHEDIN, newTask, runTask);
}

四、相关宏定义

进程状态宏定义:

#define OS_TASK_STATUS_INIT 0x0001U //初始化状态 
#define OS_TASK_STATUS_READY 0x0002U //就绪状态的任务都将插入就绪队列,注意就绪队列 的本质是个双向链表 
#define OS_TASK_STATUS_RUNNING 0x0004U //运行状态 
#define OS_TASK_STATUS_SUSPENDED 0x0008U
#define OS_TASK_STATUS_PENDING 0x0010U //阻塞状态 
#define OS_TASK_STATUS_PEND_TIME 0x0080U
#define OS_TASK_STATUS_DELAY 0x0020U //延期状态 
#define OS_TASK_STATUS_TIMEOUT 0x0040U //任务超时

进程和线程最高优先级定义:

#define OS_PROCESS_PRIORITY_HIGHEST 0 //进程最高优先级 
#define OS_PROCESS_PRIORITY_LOWEST 31 //进程最低优先级 
#define OS_TASK_PRIORITY_HIGHEST 0 //任务最高优先级,软时钟任务就是最高级任务,见于 OsSwtmrTaskCreate 
#define OS_TASK_PRIORITY_LOWEST 31 //任务最低优先级

本篇文章内容为操作系统兴趣小组成员共同学习成果总结,同时参考了其他作者的总结分析文章,具体参考都已在文中标明。

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SAP OData 是一种用于在SAP系统中访问和操作数据的协议。在调用 update_entity 之前没有调用 get_entity 可能是因为以下几个原因: 1. 数据已经在客户端处于可靠的状态:在客户端进行某个实体的更改操作时,可能已经使用其他方法,如 create_entity 或者 query_entity,获得了该实体的信息,并且已经在客户端进行了相关的处理,确保了数据的准确性。这种情况下,可能不需要再次调用 get_entity 方法获取数据。 2. 客户端已了解此实体的信息:在某些情况下,客户端已经通过其他方式对实体的属性和数据进行了了解,并且不需要再次调用 get_entity 方法来获取实体的最新数据。例如,在客户端已经通过其他途径获取到了实体的属性和值,可以直接使用这些数据进行更新操作。 3. 服务端提供了默认值或者约束:根据服务端的实现方式,可能不需要在调用 update_entity 前调用 get_entity,因为服务端会为更新操作提供默认值或者根据某些约束进行处理。这种情况下,不需要再去请求实体的最新数据。 尽管在调用 update_entity 前没有调用 get_entity 可能是合理的,但是要确保在进行更新操作时,已经拥有实体的必要信息、数据的准确性以及保持数据的一致性,避免出现错误和异常情况。如果在调用 update_entity 前需要获取实体的最新数据,可以考虑在代码中增加相应的查询和读取逻辑,以确保数据的准确性和一致性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值