在实时操作系统中,任务调度只是系统运行机制的一部分,任务之间如何使用和等待系统资源,同样直接影响系统的运行状态。

例如,当一个任务长时间处于等待状态时,仅仅查看任务本身的信息,往往很难判断它究竟在等待什么。

如果能够直接查看资源对象的当前状态,就可以进一步回答:

  • 当前资源的值是多少?

  • 当前是否存在资源拥有者?

  • 有多少任务正在等待?

  • 哪些任务正在等待该资源?

  • 是否存在来自ISR的待处理信号?

针对这些问题,HRTOS Debug提供了 Resource状态监控功能

该模块统一分析HRTOS内部的8个Resource对象,并提供资源值、拥有者、等待任务数量、等待任务位图以及ISR Pending Signal等运行状态信息。

需要特别说明的是:

HRTOS Debug Resource模块只读取和分析内核资源状态,不修改内核数据。


一、HRTOS Resource的统一设计

HRTOS内部采用统一的Resource对象管理部分同步和通信相关机制。

Resource对象包含以下主要信息:

Resource
│
├── value
├── owner
├── wait_cnt
├── wait_mask
└── pending_signal

这些Resource可以用于:

Semaphore
Mutex
Event
Message Queue Wait

因此,Debug模块并不需要针对每一种同步机制分别设计一套监控接口,而是直接读取统一的Resource对象。

整体关系可以表示为:

              HRTOS Resource
                    │
       ┌────────────┼────────────┐
       │            │            │
   Semaphore      Mutex        Event
       │                         │
       └──── Message Queue Wait ┘
                    │
                    ▼
              HRTOS Debug
                    │
       ┌────────────┼────────────┐
       │            │            │
     Value        Owner       Waiters
                    │
              Pending Signal

这种设计可以让Debug层与内核资源机制保持一致。


二、Resource对象

Debug模块通过以下方式访问HRTOS内部的Resource数组:

extern volatile OS_RESOURCE xdata OS_RES[
    HRTOS_DEBUG_RESOURCE_MAX];

同时定义:

#define HRTOS_DEBUG_RESOURCE_MAX    8
#define HRTOS_DEBUG_INVALID_ID      0xFF

因此当前Debug模块监控:

Resource 0
Resource 1
Resource 2
Resource 3
Resource 4
Resource 5
Resource 6
Resource 7

共8个Resource对象。

每个Resource都有自己的运行状态。


三、Resource的五项核心信息

HRTOS Debug主要提供五类信息。

1. Resource Value

value

表示当前Resource的资源值。

对于不同类型的Resource,其具体含义由内核使用方式决定。

Debug模块不改变这个值,只负责读取当前状态。


2. Resource Owner

owner

表示当前Resource的拥有者。

这个字段对于Mutex等具有所有者概念的资源尤其有价值。

例如:

Resource 2
Owner : Task 5

可以直接知道当前资源由Task 5持有。

如果返回:

HRTOS_DEBUG_INVALID_ID

则可以表示当前没有有效的Owner。


3. Wait Count

wait_cnt

表示当前正在等待该Resource的任务数量。

例如:

Resource 3
Wait Count : 4

说明当前有4个任务处于该Resource的等待状态。

这对于分析任务阻塞非常有帮助。


4. Wait Mask

除了等待任务数量之外,HRTOS Debug还提供:

wait_mask

用于记录具体哪些任务正在等待该Resource。

HRTOS当前拥有16个普通任务,因此可以使用一个16位整数表示任务等待关系:

bit0  → Task 0
bit1  → Task 1
bit2  → Task 2
...
bit15 → Task 15

例如:

wait_mask = 0x002A

对应的二进制位中,可以直接分析出具体处于等待状态的任务。

这相比单独提供:

Wait Count = 3

提供了更多信息。

因为:

Wait Count告诉我们“有多少任务在等待”,Wait Mask则告诉我们“哪些任务在等待”。


5. Pending Signal

Resource还提供:

pending_signal

用于记录来自ISR的待处理信号计数。

这对于中断与任务之间的资源交互分析非常有意义。

例如:

ISR
 │
 ├── 产生信号
 │
 ▼
Resource
 │
 └── pending_signal

Debug模块可以直接读取当前Pending Signal状态。


四、读取单个Resource

最基础的接口是:

unsigned char HRTOS_DEBUG_Resource_Get(
    unsigned char id,
    HRTOS_DEBUG_RESOURCE_INFO xdata *info)

调用时传入Resource编号和输出结构体地址。

首先进行ID检查:

if (id >= HRTOS_DEBUG_RESOURCE_MAX)
{
    return 1;
}

当前有效ID范围:

0 ~ 7

如果超过范围,则直接返回错误。

随后检查输出指针:

if (info == 0)
{
    return 2;
}

避免向无效地址写入数据。


五、Resource信息复制

参数检查完成后,Debug模块直接从内核Resource对象读取状态:

info->id =
    id;

info->value =
    OS_RES[id].value;

info->owner =
    OS_RES[id].owner;

info->wait_cnt =
    OS_RES[id].wait_cnt;

info->wait_mask =
    OS_RES[id].wait_mask;

info->pending_signal =
    OS_RES[id].pending_signal;

整个过程实际上就是:

OS_RES[id]
    │
    ├── value
    ├── owner
    ├── wait_cnt
    ├── wait_mask
    └── pending_signal
          │
          ▼
HRTOS_DEBUG_RESOURCE_INFO

Debug模块只负责读取和复制。


六、获取Resource等待任务数量

如果应用程序只关心当前等待任务数量,可以直接使用:

unsigned char HRTOS_DEBUG_Resource_GetWaitCount(
    unsigned char id)

例如:

unsigned char count;

count = HRTOS_DEBUG_Resource_GetWaitCount(2);

返回:

0 ~ 16

这样就不需要读取完整的Resource结构。


七、获取等待任务位图

如果需要知道具体哪些任务正在等待,可以使用:

unsigned int HRTOS_DEBUG_Resource_GetWaitMask(
    unsigned char id)

例如:

unsigned int mask;

mask = HRTOS_DEBUG_Resource_GetWaitMask(2);

然后根据对应bit判断任务状态。

结构如下:

16 bit Wait Mask

15                  8 7                  0
┌────────────────────┬────────────────────┐
│ Task 15 ... Task 8 │ Task 7 ... Task 0 │
└────────────────────┴────────────────────┘

因此一个16位变量即可表达全部16个普通任务的等待关系。

对于8051这种资源受限的平台,这种设计非常直接。


八、获取Resource Owner

对于需要关注资源所有者的场景,可以使用:

unsigned char HRTOS_DEBUG_Resource_GetOwner(
    unsigned char id)

例如:

unsigned char owner;

owner = HRTOS_DEBUG_Resource_GetOwner(2);

如果Resource存在有效拥有者,则返回对应Task ID。

如果ID无效:

return HRTOS_DEBUG_INVALID_ID;

其中:

#define HRTOS_DEBUG_INVALID_ID 0xFF

用于区分正常Task ID和错误状态。


九、获取Resource Value

如果只需要查询资源当前值,可以使用:

unsigned char HRTOS_DEBUG_Resource_GetValue(
    unsigned char id)

例如:

unsigned char value;

value = HRTOS_DEBUG_Resource_GetValue(0);

这种接口适合Shell或者Debug界面进行单项查询。


十、获取ISR Pending Signal

对于需要分析中断与资源交互的场景,可以使用:

unsigned char HRTOS_DEBUG_Resource_GetPendingSignal(
    unsigned char id)

例如:

unsigned char pending;

pending =
    HRTOS_DEBUG_Resource_GetPendingSignal(1);

通过该值可以观察Resource当前是否存在来自ISR的待处理信号。

这样就能够进一步分析:

外部事件
    │
    ▼
ISR
    │
    ▼
Resource
    │
    ▼
Pending Signal
    │
    ▼
任务处理

对于实时系统中的中断—任务协作分析,这类信息具有实际意义。


十一、一次获取全部8个Resource

如果Debug界面需要显示完整的Resource状态,可以使用:

unsigned char HRTOS_DEBUG_Resource_GetAll(
    HRTOS_DEBUG_RESOURCE_INFO xdata *info)

该函数一次性读取全部8个Resource。

核心代码:

for (i = 0; i < HRTOS_DEBUG_RESOURCE_MAX; i++)
{
    info[i].id =
        i;

    info[i].value =
        OS_RES[i].value;

    info[i].owner =
        OS_RES[i].owner;

    info[i].wait_cnt =
        OS_RES[i].wait_cnt;

    info[i].wait_mask =
        OS_RES[i].wait_mask;

    info[i].pending_signal =
        OS_RES[i].pending_signal;
}

调用后,输出数组就保存了完整的Resource状态。

可以进一步用于:

Shell
Debug Monitor
串口输出
系统状态分析
故障定位

十二、为什么采用“只读”设计

Resource Debug模块有一个非常重要的原则:

Debug模块只观察系统,不干预系统。

也就是说,Debug层不会修改:

OS_RES[]

中的任何内核状态。

它只执行:

读取
 ↓
复制
 ↓
分析
 ↓
显示

而不是:

读取
 ↓
修改
 ↓
影响调度

对于实时操作系统来说,这一点非常重要。

Debug功能本身的存在,不应该改变被调试系统的资源状态。

因此,HRTOS Debug与HRTOS内核之间可以保持比较清晰的边界:

┌─────────────────────────┐
│       HRTOS Kernel      │
│                         │
│ Scheduler / Resource    │
│ Task / ISR / IPC        │
└────────────┬────────────┘
             │
          Read Only
             │
             ▼
┌─────────────────────────┐
│      HRTOS Debug        │
│                         │
│ Resource Monitor        │
│ Task Monitor            │
│ CPU Usage               │
│ Stack Monitor           │
└─────────────────────────┘

十三、Resource Debug可以解决什么问题

假设某个任务长期处于等待状态。

仅仅查看任务状态:

Task 5 : WAIT

信息是不完整的。

进一步查看Resource:

Resource 2

Value          : ...
Owner          : Task 3
Wait Count     : 4
Wait Mask      : ...
Pending Signal : ...

就能够进一步判断:

Task 5
   │
   ▼
正在等待 Resource 2
   │
   ├── 当前Owner = Task 3
   ├── 还有其他任务等待
   └── 是否存在Pending Signal

这样,Debug就从“查看任务状态”进一步进入了:

分析任务为什么处于当前状态。

这也是系统级Debug工具的重要价值。


十四、Resource与任务监控结合

如果进一步把Resource Monitor与Task Monitor结合起来,可以形成更加完整的分析链。

例如:

Task Monitor
      │
      ▼
发现 Task 5 WAIT
      │
      ▼
Resource Monitor
      │
      ▼
找到 Resource 2
      │
      ├── Owner
      ├── Wait Count
      ├── Wait Mask
      └── Pending Signal
      │
      ▼
进一步分析阻塞原因

再结合CPU Usage:

CPU Usage
    │
    ▼
系统负载异常
    │
    ▼
Task Monitor
    │
    ▼
Resource Monitor
    │
    ▼
定位任务与资源关系

这样Debug功能就形成了从:

系统
 ↓
任务
 ↓
资源

逐层深入的分析能力。


十五、完整核心代码

Resource Debug模块完整实现如下:

#include "HRTOS_Debug.h"

#define HRTOS_DEBUG_RESOURCE_MAX    8
#define HRTOS_DEBUG_INVALID_ID      0xFF

extern volatile OS_RESOURCE xdata OS_RES[
    HRTOS_DEBUG_RESOURCE_MAX];

unsigned char HRTOS_DEBUG_Resource_Get(
    unsigned char id,
    HRTOS_DEBUG_RESOURCE_INFO xdata *info)
{
    if (id >= HRTOS_DEBUG_RESOURCE_MAX)
    {
        return 1;
    }

    if (info == 0)
    {
        return 2;
    }

    info->id =
        id;

    info->value =
        OS_RES[id].value;

    info->owner =
        OS_RES[id].owner;

    info->wait_cnt =
        OS_RES[id].wait_cnt;

    info->wait_mask =
        OS_RES[id].wait_mask;

    info->pending_signal =
        OS_RES[id].pending_signal;

    return 0;
}

unsigned char HRTOS_DEBUG_Resource_GetWaitCount(
    unsigned char id)
{
    if (id >= HRTOS_DEBUG_RESOURCE_MAX)
    {
        return 0;
    }

    return OS_RES[id].wait_cnt;
}

unsigned int HRTOS_DEBUG_Resource_GetWaitMask(
    unsigned char id)
{
    if (id >= HRTOS_DEBUG_RESOURCE_MAX)
    {
        return 0;
    }

    return OS_RES[id].wait_mask;
}

unsigned char HRTOS_DEBUG_Resource_GetOwner(
    unsigned char id)
{
    if (id >= HRTOS_DEBUG_RESOURCE_MAX)
    {
        return HRTOS_DEBUG_INVALID_ID;
    }

    return OS_RES[id].owner;
}

unsigned char HRTOS_DEBUG_Resource_GetValue(
    unsigned char id)
{
    if (id >= HRTOS_DEBUG_RESOURCE_MAX)
    {
        return 0;
    }

    return OS_RES[id].value;
}

unsigned char HRTOS_DEBUG_Resource_GetPendingSignal(
    unsigned char id)
{
    if (id >= HRTOS_DEBUG_RESOURCE_MAX)
    {
        return 0;
    }

    return OS_RES[id].pending_signal;
}

unsigned char HRTOS_DEBUG_Resource_GetAll(
    HRTOS_DEBUG_RESOURCE_INFO xdata *info)
{
    unsigned char i;

    if (info == 0)
    {
        return 1;
    }

    for (i = 0; i < HRTOS_DEBUG_RESOURCE_MAX; i++)
    {
        info[i].id =
            i;

        info[i].value =
            OS_RES[i].value;

        info[i].owner =
            OS_RES[i].owner;

        info[i].wait_cnt =
            OS_RES[i].wait_cnt;

        info[i].wait_mask =
            OS_RES[i].wait_mask;

        info[i].pending_signal =
            OS_RES[i].pending_signal;
    }

    return 0;
}

十六、总结

HRTOS Debug Resource模块通过统一的Resource对象,对HRTOS内部资源运行状态进行集中监控。

目前可以获取:

Resource Value
Resource Owner
Wait Count
Wait Mask
Pending Signal

其中:

Wait Count

用于了解当前有多少任务等待资源;

Wait Mask

用于进一步确定具体哪些任务正在等待;

Owner

用于分析资源当前拥有者;

Pending Signal

用于观察ISR与Resource之间的信号状态。

整个模块遵循:

只读内核状态,不修改内核数据。

因此它可以作为HRTOS Debug体系中的一个独立观察层,为任务状态分析、资源等待分析以及中断—任务协作分析提供基础数据。

对于资源有限的8051实时系统而言,Debug并不一定需要复杂的调试器接口。

能够直接看到:

任务在做什么
资源在做什么
谁在等待
谁拥有资源
系统有多忙

就已经能够解决相当一部分实际开发中的定位问题。

这也是HRTOS Debug希望提供的核心能力:

让实时系统内部的运行状态变得可观察、可分析。

Logo

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

更多推荐