3. VxWorks内核基础
VxWorks 内核是实时操作系统(RTOS)的核心,它决定了系统对外部事件的响应能力。本章将深入剖析 VxWorks 内核的三大支柱:任务调度机制、任务间通信(IPC)以及中断处理与 ISR 设计原则。理解这些基础是编写高效、稳定设备驱动的前提。
3.1 任务调度机制
VxWorks 内核采用基于优先级的抢占式调度,并可选配时间片轮转调度。调度器始终选择最高优先级且处于就绪态的任务来执行。
3.1.1 优先级抢占调度
每个任务被分配一个优先级(0-255,0为最高)。当高优先级任务就绪时,内核立即抢占当前正在运行的低优先级任务。这种机制保证了关键任务(如中断响应、控制环路)的实时性。
关键特性: 优先级继承(Priority Inheritance)协议用于防止优先级反转。当低优先级任务持有高优先级任务所需的资源时,低优先级任务会临时提升到高优先级任务的优先级,直到释放资源。
3.1.2 时间片轮转调度
对于相同优先级的任务,可以启用时间片轮转调度。每个任务运行一个固定的时间片(Tick),时间片耗尽后,调度器将 CPU 交给同一优先级的下一个就绪任务。这确保了同优先级任务间的公平性。
| 调度策略 | 触发条件 | 适用场景 |
|---|---|---|
| 优先级抢占 | 高优先级任务就绪 | 中断响应、实时控制、紧急事件处理 |
| 时间片轮转 | 时间片耗尽 | 后台任务、非实时数据采集、日志记录 |
驱动开发提示: 在 ISR 中唤醒的高优先级任务,应在 ISR 退出后立即获得 CPU,这是 VxWorks 实时性的核心体现。
3.2 任务间通信(IPC)
设备驱动通常需要与多个任务交互。VxWorks 提供了丰富的 IPC 机制,用于同步、互斥和数据交换。
3.2.1 信号量
信号量是 VxWorks 中最核心的同步原语,用于管理共享资源访问和任务同步。
- 二进制信号量: 用于互斥或事件通知,只有 0 和 1 两种状态。
- 计数信号量: 用于管理多个相同资源(如缓冲区池)。
- 互斥信号量: 专为互斥设计,支持优先级继承,防止优先级反转。
3.2.2 消息队列
消息队列用于任务间传递数据块。驱动中常用于将接收到的数据包从 ISR 或中断服务任务传递给上层协议栈。
/* 创建消息队列 */
MSG_Q_ID msgQId = msgQCreate(10, sizeof(MyPacket), MSG_Q_FIFO);
/* 发送消息(可在 ISR 中调用) */
MyPacket pkt;
STATUS status = msgQSend(msgQId, (char *)&pkt, sizeof(pkt), WAIT_FOREVER, MSG_PRI_NORMAL);
/* 接收消息 */
status = msgQReceive(msgQId, (char *)&recvPkt, sizeof(recvPkt), WAIT_FOREVER);
3.2.3 事件
事件机制允许任务等待一组预定义的事件标志。一个任务可以等待多个事件中的任意一个或全部,非常适合处理复杂的异步触发场景。
驱动设计模式: 使用“事件 + 任务”模式替代轮询。例如,网络驱动可以定义“接收完成”、“发送完成”、“链路状态变化”等事件,驱动任务等待这些事件,而不是不断查询硬件状态。
3.3 中断处理与 ISR 设计原则
中断是设备驱动与硬件交互的主要方式。VxWorks 的中断处理分为两部分:中断服务程序(ISR)和中断服务任务(IST)。
3.3.1 ISR 设计原则
ISR 在中断上下文中运行,必须遵循严格的设计约束,以保证系统实时性和稳定性。
- 快速执行: ISR 应尽可能短,只做最必要的操作(如读取硬件状态、清除中断标志)。
- 禁止阻塞调用: 在 ISR 中不能调用可能阻塞的函数(如
semTake()、msgQReceive()、malloc())。 - 使用中断安全 API: 只能调用以
int或ISR为后缀的函数(如semGive()、msgQSend()、logMsg())。 - 避免浮点运算: 中断上下文通常不保存浮点寄存器。
3.3.2 中断服务任务(IST)模式
对于需要复杂处理的中断,推荐使用“ISR + IST”模式:ISR 仅做最小处理,然后通过信号量或消息队列唤醒一个高优先级任务(IST)来完成后续工作。
/* ISR 示例:最小化处理 */
void myDevIntr(void *arg) {
/* 1. 读取硬件状态寄存器 */
UINT32 status = REG_READ(INT_STATUS);
/* 2. 清除中断标志 */
REG_WRITE(INT_CLEAR, status);
/* 3. 发送信号量唤醒 IST */
semGive(gMyDevSem);
/* 4. 可选:记录中断计数(使用中断安全函数) */
logMsg("Interrupt received, status: 0x%x\n", status);
}
/* IST 任务:处理中断数据 */
void myDevIstTask(void) {
while (1) {
semTake(gMyDevSem, WAIT_FOREVER);
/* 执行耗时的数据处理 */
processReceivedData();
}
}
常见错误: 在 ISR 中调用
printf() 或 malloc()。这些函数可能引起任务切换或阻塞,导致系统崩溃。应始终使用 logMsg() 进行调试输出。