3. 优先级反转场景模拟与对策

任务目标

在VxWorks实时系统中,当高优先级任务被低优先级任务持有的互斥信号量阻塞,而中优先级任务又抢占低优先级任务时,高优先级任务的完成时间将被严重延迟。本案例通过三个任务模拟该场景,测量高优先级任务的实际阻塞时间,并分别使用优先级继承(PRI_INHERIT)与优先级天花板(PRI_CEILING)两种互斥信号量属性进行优化,对比优化效果。

环境与依赖

  • VxWorks 7或6.9及以上版本,支持POSIX互斥信号量属性扩展
  • 使用semMCreate创建互斥信号量,通过SEM_INVERSION_SAFE标志启用优先级继承,通过SEM_CEILING_PRIORITY(x)设置天花板优先级
  • 需要目标机串口或控制台输出观察日志

关键完整代码

/* 优先级反转场景模拟与对策 - VxWorks */

#include <vxWorks.h>
#include <semLib.h>
#include <taskLib.h>
#include <stdio.h>
#include <time.h>

/* 任务优先级定义 */
#define PRI_HIGH   100   /* 高优先级任务 */
#define PRI_MID    150   /* 中优先级任务 */
#define PRI_LOW    200   /* 低优先级任务 */

/* 共享互斥信号量 */
static SEM_ID semMutex;

/* 时间戳记录 */
static struct timespec tsStart, tsEnd;

/* 低优先级任务:持有信号量并执行较长时间 */
static void lowTask(void)
{
    printf("[LOW ] 开始执行,尝试获取信号量\n");
    semTake(semMutex, WAIT_FOREVER);
    printf("[LOW ] 已获取信号量,开始长时间计算...\n");
    taskDelay(sysClkRateGet() * 2);  /* 模拟占用2秒 */
    printf("[LOW ] 释放信号量\n");
    semGive(semMutex);
}

/* 中优先级任务:无信号量需求,但会抢占低优先级任务 */
static void midTask(void)
{
    printf("[MID ] 开始执行,持续占用CPU...\n");
    taskDelay(sysClkRateGet() * 3);  /* 模拟运行3秒 */
    printf("[MID ] 执行完毕\n");
}

/* 高优先级任务:等待信号量并记录等待时间 */
static void highTask(void)
{
    printf("[HIGH] 开始执行,尝试获取信号量\n");
    clock_gettime(CLOCK_MONOTONIC, &tsStart);
    semTake(semMutex, WAIT_FOREVER);
    clock_gettime(CLOCK_MONOTONIC, &tsEnd);
    printf("[HIGH] 已获取信号量,等待时间: %ld ms\n",
           (tsEnd.tv_sec - tsStart.tv_sec) * 1000 +
           (tsEnd.tv_nsec - tsStart.tv_nsec) / 1000000);
    semGive(semMutex);
}

/* 测试入口:mode=0 无优化, mode=1 优先级继承, mode=2 天花板 */
void inversionTest(int mode)
{
    /* 创建互斥信号量 */
    if (mode == 0) {
        semMutex = semMCreate(SEM_Q_PRIORITY);
        printf("=== 模式: 无优化(普通互斥) ===\n");
    } else if (mode == 1) {
        semMutex = semMCreate(SEM_Q_PRIORITY | SEM_INVERSION_SAFE);
        printf("=== 模式: 优先级继承 ===\n");
    } else {
        semMutex = semMCreate(SEM_Q_PRIORITY | SEM_CEILING_PRIORITY(PRI_HIGH));
        printf("=== 模式: 优先级天花板(设为%d) ===\n", PRI_HIGH);
    }

    /* 创建任务:先低优先级,再中优先级,最后高优先级 */
    taskSpawn("tLow",  PRI_LOW,  0, 0x2000, (FUNCPTR)lowTask,  0,0,0,0,0,0,0,0,0,0);
    taskDelay(sysClkRateGet() / 10);  /* 确保低任务先获取信号量 */
    taskSpawn("tMid",  PRI_MID,  0, 0x2000, (FUNCPTR)midTask,  0,0,0,0,0,0,0,0,0,0);
    taskDelay(sysClkRateGet() / 10);
    taskSpawn("tHigh", PRI_HIGH, 0, 0x2000, (FUNCPTR)highTask, 0,0,0,0,0,0,0,0,0,0);

    /* 等待所有任务完成 */
    taskDelay(sysClkRateGet() * 4);
    printf("=== 测试结束 ===\n\n");
}

运行与验证

  1. 将代码编译链接到VxWorks镜像或作为可下载模块加载到目标机。
  2. 在shell中依次执行:
    inversionTest(0) — 观察无优化时的等待时间
    inversionTest(1) — 观察优先级继承效果
    inversionTest(2) — 观察天花板协议效果
  3. 判定标准:无优化模式下,高优先级任务等待时间应接近2秒(低任务持有信号量时间+中任务抢占时间);优先级继承模式下,等待时间应显著缩短(低任务被临时提升优先级,不被中任务抢占);天花板模式下,等待时间应接近低任务实际持有信号量的时间(约2秒,但中任务无法在低任务持锁期间抢占)。

预期输出(示例)

=== 模式: 无优化(普通互斥) ===
[LOW ] 开始执行,尝试获取信号量
[LOW ] 已获取信号量,开始长时间计算...
[MID ] 开始执行,持续占用CPU...
[HIGH] 开始执行,尝试获取信号量
[MID ] 执行完毕
[LOW ] 释放信号量
[HIGH] 已获取信号量,等待时间: 3000 ms

=== 模式: 优先级继承 ===
[LOW ] 开始执行,尝试获取信号量
[LOW ] 已获取信号量,开始长时间计算...
[MID ] 开始执行,持续占用CPU...
[HIGH] 开始执行,尝试获取信号量
[LOW ] 释放信号量
[HIGH] 已获取信号量,等待时间: 2000 ms

注:实际时间取决于系统时钟粒度和任务调度延迟,上述为逻辑推导的预期值,用于验证代码逻辑正确性。