1. RTOS 基础概念:任务、调度器、内核对象概述

在深入 RTOS 串口驱动与中断优先级设计之前,必须建立对 RTOS 核心抽象层的清晰认知。实时操作系统(RTOS)的本质是一个确定性任务调度器,它通过内核对象协调多个并发执行流对共享资源(如串口外设)的访问。本节将逐一拆解任务、调度器、信号量、队列与互斥量这五个基础概念,为后续中断服务函数(ISR)与多任务互斥的讨论奠定理论基础。

1.1 任务(Task)—— 并发执行的基本单元

任务是 RTOS 中独立调度的执行线程,每个任务拥有独立的栈空间、优先级和任务控制块(TCB)。与裸机中的超级循环不同,RTOS 任务在逻辑上“同时”运行,由调度器决定哪个任务在哪个时刻获得 CPU 使用权。

关键特征:
  • 优先级: 0~255 级(常见配置),数值越小优先级越高(或相反,取决于内核实现)。
  • 状态机: 运行态、就绪态、阻塞态、挂起态。
  • 上下文切换: 保存/恢复 CPU 寄存器、栈指针,切换时间通常在微秒级。
RTOS 任务状态转换图 运行态 就绪态 阻塞态 挂起态 被抢占 调度 等待资源 资源就绪 vTaskSuspend vTaskResume 说明: 任务在运行态时被更高优先级任务抢占 → 回到就绪态;等待信号量/队列 → 进入阻塞态。 串口 ISR 中释放信号量可让阻塞态任务直接进入就绪态。

1.2 调度器(Scheduler)—— 决定“谁运行”的核心引擎

调度器是 RTOS 内核的心脏,它根据预设的调度策略从就绪任务列表中选择下一个要运行的任务。在嵌入式实时系统中,最常用的调度算法是基于固定优先级的抢占式调度。

常见调度策略对比
调度策略 特点 适用场景
抢占式优先级调度 高优先级任务就绪时立即抢占低优先级任务 硬实时系统,如串口响应、电机控制
时间片轮转(Round-Robin) 同优先级任务轮流获得固定时间片 非实时或软实时,如人机界面
协作式调度 任务主动让出 CPU,无抢占 极简系统,无中断密集型场景
串口驱动中的调度要点: 串口 ISR 通常运行在最高优先级(硬件优先级),但 ISR 中不应调用阻塞型 API。正确的做法是 ISR 释放信号量或向队列发送数据,让高优先级任务在任务上下文中处理数据,从而避免 ISR 长时间占用 CPU。

1.3 内核对象:信号量、队列与互斥量

内核对象是 RTOS 提供的同步与通信机制,用于解决多任务访问共享资源(如串口 TX/RX 缓冲区)时的竞态条件。以下逐一分析三种核心对象。

1.3.1 信号量(Semaphore)—— 事件通知与资源计数

信号量是一个非负整数计数器,支持 take(P 操作)和 give(V 操作)两种原子操作。在串口驱动中,二值信号量常用于 ISR 通知任务“数据已到达”。

// 伪代码:串口 ISR 中释放信号量
void UART_ISR(void) {
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    xSemaphoreGiveFromISR(xRxSemaphore, &xHigherPriorityTaskWoken);
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}

// 任务中等待信号量
void RxTask(void *pvParameters) {
    while (1) {
        xSemaphoreTake(xRxSemaphore, portMAX_DELAY);
        // 处理接收到的数据
        UART_ReadBuffer(rxBuf, len);
    }
}

1.3.2 队列(Queue)—— 结构化数据传递

队列是 RTOS 中最常用的任务间通信方式,支持多个生产者和消费者。队列内部维护一个环形缓冲区,通过拷贝或引用传递数据。在串口驱动中,队列用于将 ISR 接收到的字节传递给处理任务。

队列与信号量在串口驱动中的角色对比
特性 信号量 队列
传递数据 仅通知事件,不携带数据 携带完整数据帧
ISR 安全 API xSemaphoreGiveFromISR xQueueSendFromISR
典型用法 通知任务“有数据可读” 将接收字节直接送入任务缓冲区
多生产者支持 是(计数信号量) 是(多个 ISR 或任务可发送)
串口 ISR 与任务通过信号量+队列协作 UART 外设 串口 ISR 队列 处理任务 中断触发 发送数据 接收数据 流程 1. 外设接收字节 2. ISR 入队 3. 任务出队处理 4. 释放信号量 (可选)

1.3.3 互斥量(Mutex)—— 保护共享资源的“锁”

互斥量是信号量的特例,专用于解决优先级反转问题。它支持优先级继承协议:当低优先级任务持有互斥量时,若高优先级任务请求同一互斥量,低优先级任务会临时继承高优先级任务的优先级,从而避免被中等优先级任务无限抢占。

串口驱动中的互斥量陷阱: 不要在 ISR 中使用互斥量!互斥量的释放操作必须在任务上下文中完成,且持有互斥量的任务不能进入阻塞态。串口驱动中,互斥量通常用于保护共享的打印缓冲区或配置寄存器,而非用于 ISR 与任务之间的同步。

1.4 三种内核对象在串口驱动中的典型分工

信号量、队列、互斥量在串口驱动中的职责矩阵
场景 推荐对象 原因
ISR 通知任务“收到一个字节” 二值信号量 轻量级,仅传递事件,不拷贝数据
ISR 向任务传递多个字节 队列 数据缓冲,支持多个生产者(如 DMA 半满/全满中断)
多任务共享串口打印 互斥量 防止打印内容交错,支持优先级继承
计数资源(如空闲缓冲区数量) 计数信号量 管理有限数量的缓冲区

1.5 小结:从概念到串口驱动设计的映射

理解任务、调度器与内核对象是设计可靠 RTOS 串口驱动的前提:

  • 任务 负责在任务上下文中处理串口数据,避免在 ISR 中执行耗时操作。
  • 调度器 确保高优先级任务(如串口数据处理任务)在数据到达时立即获得 CPU。
  • 信号量 作为 ISR 与任务之间的“门铃”,实现事件驱动。
  • 队列 作为数据管道,解耦 ISR 的生产速度与任务的消费速度。
  • 互斥量 保护串口配置寄存器或共享打印缓冲区,防止竞态条件。