3. 时间片轮转调度

时间片轮转调度 学习卡片

任务定义

在RTOS中,当多个任务具有相同优先级时,需要一种机制让它们公平共享CPU。本案例基于FreeRTOS,创建两个同优先级任务,通过配置时间片轮转调度,使两个任务按固定时间片交替执行,并通过串口输出观察调度行为。

时间片轮转原理

FreeRTOS的调度器在Tick中断触发时检查当前任务是否用完了其时间片(由configTICK_RATE_HZ和configUSE_TIME_SLICING决定)。若同优先级就绪队列中存在其他任务,则切换到下一个任务。时间片长度等于一个Tick周期。本案例中两个任务优先级均为1,各自在循环中打印执行计数,通过串口输出可观察到两个任务交替打印。

代码实现

// main.c
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"

// 两个任务共享的打印互斥量(可选,用于避免串口输出交错)
static SemaphoreHandle_t xPrintMutex;

// 任务1:打印 'A' 及其执行次数
static void vTaskA(void *pvParameters) {
    uint32_t ulCount = 0;
    for (;;) {
        xSemaphoreTake(xPrintMutex, portMAX_DELAY);
        printf("Task A: %lu\r\n", (unsigned long)ulCount++);
        xSemaphoreGive(xPrintMutex);
        // 空循环,让任务持续占用CPU直到时间片耗尽
        for (volatile uint32_t i = 0; i < 10000; i++);
    }
}

// 任务2:打印 'B' 及其执行次数
static void vTaskB(void *pvParameters) {
    uint32_t ulCount = 0;
    for (;;) {
        xSemaphoreTake(xPrintMutex, portMAX_DELAY);
        printf("Task B: %lu\r\n", (unsigned long)ulCount++);
        xSemaphoreGive(xPrintMutex);
        for (volatile uint32_t i = 0; i < 10000; i++);
    }
}

int main(void) {
    // 硬件初始化(时钟、串口等,由平台相关代码提供)
    prvSetupHardware();

    // 创建打印互斥量
    xPrintMutex = xSemaphoreCreateMutex();

    // 创建两个同优先级任务(优先级均为1)
    xTaskCreate(vTaskA, "TaskA", configMINIMAL_STACK_SIZE, NULL, 1, NULL);
    xTaskCreate(vTaskB, "TaskB", configMINIMAL_STACK_SIZE, NULL, 1, NULL);

    // 启动调度器
    vTaskStartScheduler();

    // 正常不会到达这里
    for (;;);
    return 0;
}

FreeRTOSConfig.h 关键配置

#define configUSE_PREEMPTION    1       // 使能抢占式调度
#define configUSE_TIME_SLICING  1       // 使能时间片轮转(默认开启)
#define configTICK_RATE_HZ      ( 1000 ) // Tick频率1kHz,时间片=1ms
#define configMAX_PRIORITIES    5

时间片调度时序

时间片轮转调度时序 (Tick=1ms) 0ms 1ms 2ms 3ms 4ms Task A Task B Task A Task B Task A (优先级1) Task B (优先级1) 时间片=1ms 时间片=1ms

构建与运行

  1. 依赖:FreeRTOS内核源码(V10.x及以上),平台相关移植文件(如STM32、QEMU等),以及可用的串口打印函数printf重定向。
  2. 编译:将main.c加入工程,确保FreeRTOSConfig.h中configUSE_TIME_SLICING为1,configTICK_RATE_HZ设为1000。
  3. 运行:烧录或启动系统,打开串口终端(波特率与平台匹配,通常115200)。

验证方法与判定标准

验证步骤:

  1. 观察串口输出,应看到Task A和Task B交替打印,且各自计数递增。
  2. 统计连续打印次数:在1秒内(约1000个Tick),Task A和Task B各打印约500次(允许±10%误差,因printf耗时影响)。
  3. 若将configUSE_TIME_SLICING改为0,则两个任务不再交替,一个任务会持续独占CPU(除非被更高优先级任务抢占或阻塞),此时只能看到单个任务持续打印。

判定标准:串口输出中Task A与Task B交替出现,且每个任务打印计数单调递增,无卡死或异常跳变,即证明时间片轮转调度生效。