Kernel Internals · HRTOS 4.0

Wait State Model 等待状态与任务调度资格

从任务状态、等待上下文与资源等待关系三个层面理解 HRTOS 的等待模型。 WAIT 描述任务当前的状态,wait_type 描述等待原因, 而调度资格由相应的任务管理字段共同维护。

Task State WAIT wait_type wait_mask READY Recovery
01

模型概述

区分任务状态、等待原因和调度资格

HRTOS 的等待机制并不是单独增加一种任务类型, 而是在任务记录中保存等待上下文,并通过任务状态与调度标志的配合, 表示任务暂时不能参与正常调度的情况。

任务状态 · state

表示任务当前所处的状态。所提供的等待实现明确将普通等待任务设置为 WAIT,唤醒后设置为 READY。

等待原因 · wait_type

记录等待类型。普通任务进入等待时写入传入的 type,唤醒清理时恢复为 WAIT_NONE。

调度资格 · OS_PROCESS_OK

等待时清除任务对应标志的最低位,唤醒时重新设置该位。 它与 state 是不同的管理信息。

等待结果 · wait_flag

保存唤醒时传入的结果标志。任务恢复执行后, 等待调用可通过该字段返回结果。

阅读原则: 状态模型说明任务如何被标记和恢复,并不单独决定调度器何时切换任务。 实际切换时机还需要结合调度器和中断处理代码理解。
02

任务状态

等待机制实际使用的状态转换

在当前提供的 wait.c 与 wake_task.c 中, 可以直接确认普通任务等待过程使用了 WAIT 和 READY 两个状态。完整状态集合及其数值定义, 应以 HRTOS 4.0 的实际头文件为准。

状态标识 等待机制中的行为
WAIT 普通任务进入等待时写入 OS_TASK[tid].state。
READY 有效唤醒完成时,目标任务被设置为 READY。
DEAD wake_task() 检测到该状态时,不继续执行后续唤醒处理。
关于完整状态表: 原页面列出的 RUNNING、SUSPEND 及状态数值没有出现在这两份等待函数的定义中。 在核对实际状态宏之前,不应将它们的数值和完整转换规则写成已由这两份源码证实的事实。
03

等待上下文模型

一次普通任务等待需要记录的信息

源码通过 OS_TASK[tid] 访问任务记录, 并使用以下字段维护普通任务的等待上下文。 这里沿用真实代码中的字段名称,不额外假定结构体类型名称。

等待上下文写入 wait.c
OS_TASK[tid].wait_type = type;
OS_TASK[tid].wait_obj  = obj;
OS_TASK[tid].wait_tick = tick;
OS_TASK[tid].wait_flag = 0;
字段 含义
wait_type 等待类型;WAIT_NONE 表示当前没有登记中的普通任务等待上下文。
wait_obj 等待对象编号;唤醒清理后设为 OS_INVALID_ID。
wait_tick 等待时间参数,以系统 Tick 为单位;唤醒清理时设为 0。
wait_flag 等待结果字段;进入等待时初始化为 0,唤醒时写入传入的 flag。

这组字段让等待原因和等待结果与任务记录关联起来。 不同等待类型可以共享这套上下文,但各类型的资源检查、超时判断和唤醒触发条件, 仍需结合对应的资源管理代码分析。

04

状态转换

从进入等待到重新获得调度资格

RUNNING 当前任务正在执行时发起等待请求
↓ os_wait() 设置等待信息
WAIT 任务状态更新,调度资格标志最低位被清除
↓ 等待条件满足或等待超时后,由相应路径发起唤醒
READY wake_task() 恢复状态与调度资格
↓ 后续调度选择
RUNNING 任务再次获得 CPU 后继续执行

上图是普通任务等待的概念路径。实际代码中, os_wait() 通过设置 OS_SCHED_REASON 和 TF0 请求后续调度处理; wake_task() 同样更新调度请求并触发 T0 中断。 这些操作本身不代表函数直接完成了上下文切换。

状态转换边界: 图中的 RUNNING 起点表示调用等待接口时的典型使用情形。 具体任务状态宏的定义、调度器内部的状态更新,以及超时与资源唤醒的调用链, 应以对应实现为准。
05

资源等待关系

wait_mask 与 wait_cnt

普通任务等待非延时资源时,如果对象编号有效, os_wait() 会将任务 ID 对应的位写入资源等待位图, 并递增等待计数。

登记资源等待 wait.c
if(type != WAIT_DELAY && obj != OS_INVALID_ID)
{
    OS_RES[obj].wait_mask |= ((u16)1 << tid);
    OS_RES[obj].wait_cnt++;
}
清理资源等待 wake_task.c
if(obj != OS_INVALID_ID)
{
    OS_RES[obj].wait_mask &= ~((u16)1 << tid);

    if(OS_RES[obj].wait_cnt)
    {
        OS_RES[obj].wait_cnt--;
    }
}
  • wait_mask 使用位操作记录对应任务的等待关系。
  • wait_cnt 在等待登记时递增,在唤醒清理时于非零条件下递减。
  • WAIT_DELAY 不通过普通资源登记代码加入等待位图。
  • 资源位图只记录等待关系;具体资源何时可用,以及哪个任务应被唤醒,需要结合资源操作代码判断。
维护要求: 等待位图、等待计数和任务的等待上下文必须保持一致。 分析重复唤醒、任务删除或异常退出时,应检查所有修改这些字段的调用路径。
06

唤醒后的状态恢复

wake_task() 如何结束普通任务等待

对通过普通任务等待上下文管理的任务, wake_task() 会写入唤醒结果、清理等待字段, 并恢复任务的 READY 状态与调度资格。

等待上下文清理与状态恢复 wake_task.c
OS_TASK[tid].wait_flag = flag;

OS_TASK[tid].wait_type = WAIT_NONE;
OS_TASK[tid].wait_obj  = OS_INVALID_ID;
OS_TASK[tid].wait_tick = 0;

OS_TASK[tid].state = READY;
OS_PROCESS_OK[tid] |= 1;

OS_SCHED_REASON = 1;

任务状态恢复和调度资格恢复是两个不同的操作。 设置 READY 表明任务已离开等待状态; 恢复 OS_PROCESS_OK[tid] 的最低位, 则使该任务重新具备相应的调度资格。

特殊情况: 实际的 wake_task() 在清理资源等待关系后, 会检查 OS_PROCESS_OK[tid] == 0 和 state == DEAD。若命中这些条件,就直接返回, 不执行后续的结果写入与 READY 恢复。
07

高速任务的特殊路径

延时处理不等同于普通任务阻塞

当等待类型为 WAIT_DELAY 时, os_wait() 会先检查当前任务编号。 对两个高速任务,源码使用独立的 OS_WAIT_DI2[] 计数器保存延时参数,而不是建立普通任务的等待上下文。

高速任务计数器 wait.c
OS_WAIT_DI2[0] = tick;  /* 高速任务 A */
OS_WAIT_DI2[1] = tick;  /* 高速任务 B */

该分支还会更新相应的高速任务标志、设置调度请求并触发 T0 中断, 随后直接返回 WAIT_TIMEOUT。 因此,不能将这一分支描述成普通任务从 WAIT 状态被唤醒后返回结果的完整流程。

实现范围: 高速任务延时计数的递减、计时完成后的恢复条件和后续调度行为, 需要结合 T0 中断及高速任务调度代码进一步说明。
08

设计要点

等待模型的结构性特点

状态与等待原因分离

state 记录任务状态,wait_type 记录等待原因。多个等待类型可以共享 WAIT 状态, 不必仅因等待原因不同就增加独立的任务状态。

等待信息保存在任务记录中

普通任务等待字段通过 OS_TASK[tid] 访问, 使等待上下文与对应任务直接关联。

资源等待使用位图登记

wait_mask 和 wait_cnt 用于维护资源对象与等待任务之间的关系,具体唤醒策略由相关调用路径决定。

唤醒与实际运行分离

wake_task() 恢复任务的 READY 状态并请求调度; 任务重新获得 CPU 的时机由后续调度过程决定。

高速任务使用独立延时处理

高速任务的延时分支使用专用计数器,与普通任务等待上下文分开。 描述和审计时,应将两条路径明确区分。