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");
}
运行与验证
- 将代码编译链接到VxWorks镜像或作为可下载模块加载到目标机。
- 在shell中依次执行:
inversionTest(0)— 观察无优化时的等待时间
inversionTest(1)— 观察优先级继承效果
inversionTest(2)— 观察天花板协议效果 - 判定标准:无优化模式下,高优先级任务等待时间应接近2秒(低任务持有信号量时间+中任务抢占时间);优先级继承模式下,等待时间应显著缩短(低任务被临时提升优先级,不被中任务抢占);天花板模式下,等待时间应接近低任务实际持有信号量的时间(约2秒,但中任务无法在低任务持锁期间抢占)。
预期输出(示例)
=== 模式: 无优化(普通互斥) ===
[LOW ] 开始执行,尝试获取信号量
[LOW ] 已获取信号量,开始长时间计算...
[MID ] 开始执行,持续占用CPU...
[HIGH] 开始执行,尝试获取信号量
[MID ] 执行完毕
[LOW ] 释放信号量
[HIGH] 已获取信号量,等待时间: 3000 ms
=== 模式: 优先级继承 ===
[LOW ] 开始执行,尝试获取信号量
[LOW ] 已获取信号量,开始长时间计算...
[MID ] 开始执行,持续占用CPU...
[HIGH] 开始执行,尝试获取信号量
[LOW ] 释放信号量
[HIGH] 已获取信号量,等待时间: 2000 ms
注:实际时间取决于系统时钟粒度和任务调度延迟,上述为逻辑推导的预期值,用于验证代码逻辑正确性。