在实时操作系统中,任务并不是始终处于运行状态。

当任务需要等待一段时间后再次执行时,通常会进入Delay阻塞状态。在这段时间内,任务不参与正常运行,由系统负责管理其剩余等待时间。

对于开发人员而言,仅仅知道某个任务当前没有运行是不够的,还需要进一步了解:

  • 哪些任务正在Delay阻塞?

  • 当前有多少任务处于Delay状态?

  • 某个任务还需要等待多少时间片?

  • 任务为什么没有进入运行状态?

针对这些问题,HRTOS Debug提供了 Delay Wait任务阻塞状态监控功能

该模块直接读取HRTOS任务控制块中的状态信息,对普通任务的Delay阻塞状态进行分析。

与其他Debug模块一样:

本模块只读取内核任务控制块,不修改内核数据。


一、什么是Delay阻塞

在RTOS中,一个任务执行延时操作后,并不需要一直占用CPU等待。

例如:

HRTOS_Delay(100);

任务进入Delay状态后:

任务运行
   │
   ▼
请求Delay
   │
   ▼
进入WAIT_DELAY
   │
   ▼
任务暂时阻塞
   │
   │  等待Tick
   │
   ▼
等待时间到达
   │
   ▼
重新进入Ready
   │
   ▼
等待调度运行

这种机制能够避免任务在延时期间持续占用CPU。

因此,Delay本质上是一种基于系统Tick的时间阻塞机制


二、HRTOS Debug如何判断Delay状态

HRTOS Debug并不需要重新建立一套任务状态管理机制。

它直接读取HRTOS内部任务控制块:

extern OS_TCB xdata OS_TASK[HRTOS_DEBUG_TASK_MAX];

当前普通任务数量配置为:

#define HRTOS_DEBUG_TASK_MAX    16

因此Debug模块可以直接分析:

Task 0
Task 1
Task 2
...
Task 15

任务是否处于Delay阻塞,主要依据:

OS_TASK[id].wait_type

当:

wait_type == WAIT_DELAY

即可判断该任务当前处于Delay阻塞状态。


三、WAIT_DELAY状态

模块定义:

#define WAIT_DELAY    1

因此判断逻辑非常直接:

if (OS_TASK[task_id].wait_type == WAIT_DELAY)
{
    return 1;
}

整体可以表示为:

OS_TASK[id]
     │
     ▼
 wait_type
     │
     ├── WAIT_DELAY
     │      │
     │      ▼
     │   Delay阻塞
     │
     └── 其他状态
            │
            ▼
         非Delay

这种设计直接复用了HRTOS内核已有的任务状态信息。

Debug模块不参与任务调度,也不改变任务状态。


四、判断指定任务是否处于Delay阻塞

模块提供:

unsigned char HRTOS_DEBUG_DelayWait_IsWaiting(
    unsigned char task_id)

用于判断指定任务当前是否处于Delay状态。

例如:

if (HRTOS_DEBUG_DelayWait_IsWaiting(3))
{
    /* Task 3 正处于 Delay 阻塞 */
}

函数首先检查任务ID:

if (task_id >= HRTOS_DEBUG_TASK_MAX)
{
    return 0;
}

当前有效任务ID范围:

0 ~ 15

如果ID超出范围,则直接返回0。

随后读取任务控制块:

if (OS_TASK[task_id].wait_type == WAIT_DELAY)
{
    return 1;
}

这样就可以非常直接地获取指定任务的Delay状态。


五、获取任务剩余等待时间

知道任务正在Delay还不够。

在实际调试过程中,一个非常重要的信息是:

这个任务还需要等待多久?

HRTOS任务控制块中保存了:

wait_tick

用于记录等待相关的Tick信息。

Debug模块提供:

unsigned int HRTOS_DEBUG_DelayWait_GetTick(
    unsigned char task_id)

读取指定任务剩余等待时间片。

核心代码:

if (OS_TASK[task_id].wait_type != WAIT_DELAY)
{
    return 0;
}

return OS_TASK[task_id].wait_tick;

这里有一个重要的判断:

只有 WAIT_DELAY 状态
        │
        ▼
wait_tick
才作为 Delay 信息解释

如果任务当前并不是Delay状态,则返回0。

这样可以避免将其他等待类型下的wait_tick错误解释为Delay剩余时间。


六、为什么剩余Tick很重要

假设Debug显示:

Task 3
State      : Delay
Wait Tick  : 500

说明Task 3当前并没有参与正常运行,而是在等待相应的Tick时间到达。

如果进一步观察:

Task 3
Wait Tick : 500

随后:

Task 3
Wait Tick : 300

再到:

Task 3
Wait Tick : 100

就可以直观看到任务的等待过程。

最终:

Wait Tick : 0

任务退出Delay等待状态,重新进入后续调度流程。

因此,wait_tick比单纯的任务状态信息更加具体。


七、统计当前Delay阻塞任务数量

除了分析单个任务,Debug还提供:

unsigned char HRTOS_DEBUG_DelayWait_GetCount(void)

用于统计当前所有普通任务中处于Delay状态的任务数量。

核心实现:

unsigned char i;
unsigned char count;

count = 0;

for (i = 0; i < HRTOS_DEBUG_TASK_MAX; i++)
{
    if (OS_TASK[i].wait_type == WAIT_DELAY)
    {
        count++;
    }
}

它会扫描全部16个普通任务:

Task 0  → 检查
Task 1  → 检查
Task 2  → 检查
...
Task 15 → 检查

最终得到当前Delay阻塞任务数量。

例如:

Delay Wait Count : 7

表示当前16个普通任务中,有7个处于Delay阻塞状态。


八、获取第一个Delay阻塞任务

如果Debug界面只需要快速判断:

当前有没有任务处于Delay状态?

可以使用:

unsigned char HRTOS_DEBUG_DelayWait_GetFirst(void)

函数从Task 0开始扫描:

for (i = 0; i < HRTOS_DEBUG_TASK_MAX; i++)
{
    if (OS_TASK[i].wait_type == WAIT_DELAY)
    {
        return i;
    }
}

找到第一个Delay任务后,直接返回对应任务ID。

如果没有找到:

return 0xFF;

因此返回值定义为:

0 ~ 15
    表示找到对应Task

0xFF
    表示当前没有Delay阻塞任务

这种接口适合Shell或简单状态监控。


九、三个接口的关系

Delay Wait模块主要提供三个层次的信息。

第一层:判断状态

HRTOS_DEBUG_DelayWait_IsWaiting(task_id)

回答:

这个任务是不是Delay状态?


第二层:获取剩余Tick

HRTOS_DEBUG_DelayWait_GetTick(task_id)

回答:

这个任务还要等待多少Tick?


第三层:统计整体状态

HRTOS_DEBUG_DelayWait_GetCount()

回答:

当前系统有多少任务处于Delay阻塞?

此外:

HRTOS_DEBUG_DelayWait_GetFirst()

可以快速找到第一个Delay任务。

因此整个模块可以概括为:

              Delay Wait
                  │
       ┌──────────┼──────────┐
       │          │          │
      Is        GetTick    GetCount
       │          │          │
       ▼          ▼          ▼
     状态       剩余时间     总数量

十、与Task Monitor结合

Delay Wait模块单独使用已经能够提供任务时间阻塞信息,但与Task Monitor结合后会更加有价值。

例如Task Monitor发现:

Task 5
State : WAIT

这时可以进一步查询Delay Wait:

Task 5
Delay : YES
Wait Tick : 120

这样就可以确定:

Task 5
   │
   ▼
处于等待状态
   │
   ▼
WAIT_DELAY
   │
   ▼
剩余120 Tick

从而把“任务没有运行”进一步解释成:

任务正在进行时间等待。

这对于系统运行状态分析非常重要。


十一、与Resource Debug结合

HRTOS Debug还可以分析Resource状态。

因此,当任务处于WAIT状态时,可以进一步区分:

Task
 │
 ▼
WAIT
 │
 ├── WAIT_DELAY
 │       │
 │       └── 时间阻塞
 │
 └── Resource Wait
         │
         └── 等待资源

这就形成了不同等待类型之间的区分。

例如:

Task 3
WAIT
 │
 └── Delay
      └── 200 Tick

而另一个任务:

Task 5
WAIT
 │
 └── Resource
      ├── Resource 2
      └── Wait Mask

通过不同Debug模块,可以逐步分析任务为什么没有运行。


十二、8051平台下的实现特点

HRTOS Debug Delay Wait模块并没有引入复杂的数据结构。

它直接读取:

OS_TASK[]

中的已有任务控制块信息。

核心数据主要包括:

wait_type
wait_tick

因此Debug模块本身不需要维护另一份任务状态。

这种设计可以避免:

内核任务状态
       +
Debug任务状态

形成两套数据源。

而是直接采用:

HRTOS Kernel
     │
     ▼
OS_TASK[]
     │
     ▼
HRTOS Debug

这样Debug显示的信息来源就是HRTOS当前实际运行状态。


十三、为什么采用只读方式

与Resource Debug一样,Delay Wait模块不会修改:

OS_TASK[]

中的任务状态。

Debug模块只进行:

读取
 ↓
判断
 ↓
统计
 ↓
输出

不会进行:

修改任务状态
修改等待Tick
强制唤醒任务
改变调度状态

这样可以尽量降低Debug功能对被测系统运行状态的影响。

Debug的职责是:

观察系统,而不是控制系统。


十四、完整核心代码

Delay Wait模块完整实现如下:

#include "HRTOS_Debug.h"

#define HRTOS_DEBUG_TASK_MAX    16
#define WAIT_DELAY              1

extern OS_TCB xdata OS_TASK[HRTOS_DEBUG_TASK_MAX];

unsigned char HRTOS_DEBUG_DelayWait_IsWaiting(
    unsigned char task_id)
{
    if (task_id >= HRTOS_DEBUG_TASK_MAX)
    {
        return 0;
    }

    if (OS_TASK[task_id].wait_type == WAIT_DELAY)
    {
        return 1;
    }

    return 0;
}

unsigned int HRTOS_DEBUG_DelayWait_GetTick(
    unsigned char task_id)
{
    if (task_id >= HRTOS_DEBUG_TASK_MAX)
    {
        return 0;
    }

    if (OS_TASK[task_id].wait_type != WAIT_DELAY)
    {
        return 0;
    }

    return OS_TASK[task_id].wait_tick;
}

unsigned char HRTOS_DEBUG_DelayWait_GetCount(void)
{
    unsigned char i;
    unsigned char count;

    count = 0;

    for (i = 0; i < HRTOS_DEBUG_TASK_MAX; i++)
    {
        if (OS_TASK[i].wait_type == WAIT_DELAY)
        {
            count++;
        }
    }

    return count;
}

unsigned char HRTOS_DEBUG_DelayWait_GetFirst(void)
{
    unsigned char i;

    for (i = 0; i < HRTOS_DEBUG_TASK_MAX; i++)
    {
        if (OS_TASK[i].wait_type == WAIT_DELAY)
        {
            return i;
        }
    }

    return 0xFF;
}

十五、总结

HRTOS Debug Delay Wait模块直接利用HRTOS任务控制块中的状态信息,对普通任务的时间阻塞状态进行分析。

目前提供:

IsWaiting()
    判断指定任务是否处于Delay状态

GetTick()
    获取Delay剩余等待Tick

GetCount()
    统计当前Delay阻塞任务数量

GetFirst()
    获取第一个Delay阻塞任务

核心判断依据为:

OS_TASK[id].wait_type == WAIT_DELAY

而剩余等待时间则通过:

OS_TASK[id].wait_tick

获取。

整个模块的工作方式可以概括为:

OS_TASK[]
    │
    ├── wait_type
    │      └── 判断是否Delay
    │
    └── wait_tick
           └── 获取剩余等待时间
                  │
                  ▼
             HRTOS Debug

对于实时系统来说,任务处于等待状态本身并不是异常。

真正重要的是能够知道:

任务为什么等待,以及还需要等待多久。

通过Task Monitor、Delay Wait和Resource Monitor等Debug功能,可以逐步把HRTOS内部的任务运行状态、时间等待状态以及资源等待状态展示出来,为8051实时系统的调试和运行分析提供更加完整的信息。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐