2. 任务切换时间测量

任务目标

在VxWorks中,任务切换时间直接反映系统实时性。本案例使用系统高精度计时器clock_gettime()测量两个不同优先级任务交替唤醒时的上下文切换开销,并给出可重复的测量方法与判定标准。

测量原理

任务A记录当前时间t1后释放信号量并阻塞;任务B获取信号量后立即记录时间t2。t2 - t1即为一次从A切换到B的完整开销(含调度器决策与上下文恢复)。交替测量多次取平均值,可消除单次抖动。

完整实现代码

/* task_switch_measure.c */
#include <vxWorks.h>
#include <taskLib.h>
#include <semLib.h>
#include <time.h>
#include <stdio.h>

#define MEASURE_COUNT  1000

static SEM_ID semSwitch;
static struct timespec tA, tB;
static volatile int switchCount = 0;
static double totalNsec = 0.0;

/* 高优先级任务:记录时间并释放信号量 */
static void highPrioTask(void)
{
    while (switchCount < MEASURE_COUNT) {
        clock_gettime(CLOCK_MONOTONIC, &tA);
        semGive(semSwitch);          /* 唤醒低优先级任务 */
        taskDelay(0);                /* 主动让出CPU,触发切换 */
    }
}

/* 低优先级任务:记录接收时间,计算差值 */
static void lowPrioTask(void)
{
    while (switchCount < MEASURE_COUNT) {
        semTake(semSwitch, WAIT_FOREVER);
        clock_gettime(CLOCK_MONOTONIC, &tB);

        double diff = (double)(tB.tv_sec - tA.tv_sec) * 1e9 +
                      (double)(tB.tv_nsec - tA.tv_nsec);
        totalNsec += diff;
        switchCount++;
    }
}

void measureTaskSwitch(void)
{
    semSwitch = semBCreate(SEM_Q_PRIORITY, SEM_EMPTY);
    if (semSwitch == NULL) {
        printf("Semaphore creation failed\n");
        return;
    }

    /* 创建低优先级任务(优先级100),先就绪但不运行 */
    taskSpawn("tLow", 100, 0, 0x2000, (FUNCPTR)lowPrioTask, 0,0,0,0,0,0,0,0,0,0);
    taskDelay(1);   /* 确保低优先级任务已阻塞在semTake上 */

    /* 创建高优先级任务(优先级50),开始测量 */
    taskSpawn("tHigh", 50, 0, 0x2000, (FUNCPTR)highPrioTask, 0,0,0,0,0,0,0,0,0,0);

    /* 等待测量完成 */
    while (switchCount < MEASURE_COUNT)
        taskDelay(1);

    double avgNsec = totalNsec / MEASURE_COUNT;
    printf("Task switch time: %.2f ns (avg of %d switches)\n",
           avgNsec, MEASURE_COUNT);
}

运行与验证

  1. 将代码编译为可下载模块,在VxWorks shell中加载。
  2. 调用measureTaskSwitch()启动测量。
  3. 观察串口输出,应显示类似:
    Task switch time: 1850.00 ns (avg of 1000 switches)
    (具体数值取决于CPU型号、主频与VxWorks配置)

判定通过条件:程序正常返回且打印平均切换时间;连续运行两次结果偏差小于20%,说明测量稳定。若输出为0或极大值,检查CLOCK_MONOTONIC是否被BSP支持(可用clock_getres()确认分辨率)。

关键说明

  • 高优先级任务通过taskDelay(0)让出CPU,触发调度器切换到已就绪的低优先级任务,这是测量切换的必要条件。
  • 信号量采用优先级排队(SEM_Q_PRIORITY),确保低优先级任务先阻塞等待,避免优先级反转影响测量。
  • 测量值包含信号量give/take操作开销,但该开销为固定常数,不影响相对比较——如需纯切换开销,可先测量空信号量操作耗时并扣除。
  • 若需分析不同优先级差的影响,可修改taskSpawn中的优先级参数(如将高优先级改为60、70)重新测量对比。