返回首页

Zephyr 工作队列学习笔记

概述

工作队列(Work Queue)是 Zephyr 中用于延迟执行和异步处理的重要机制。它将工作项(work item)排队,由专用线程稍后执行。

为什么需要工作队列

典型场景

  1. 中断上下文限制:中断中不能执行耗时操作或阻塞调用
  2. 延迟执行:需要在指定时间后执行任务
  3. 异步处理:避免阻塞主线程
  4. 串行化访问:确保对共享资源的顺序访问

工作队列类型

1. 系统工作队列 (System Work Queue)

// 系统工作队列是内核预定义的
// 通过 k_work_submit() 使用

// 定义工作项
struct k_work my_work;

// 工作处理函数
void work_handler(struct k_work *work)
{
    // 在这里执行工作
    printk("Work executed!\n");
}

// 初始化并提交到系统工作队列
k_work_init(&my_work, work_handler);
k_work_submit(&my_work);

2. 用户自定义工作队列

// 定义工作队列
struct k_work_q my_work_q;

// 工作队列线程栈
K_THREAD_STACK_DEFINE(work_q_stack, 2048);

// 初始化并启动工作队列
void init_my_work_q(void)
{
    k_work_queue_init(&my_work_q);
    k_work_queue_start(&my_work_q, work_q_stack,
                       K_THREAD_STACK_SIZEOF(work_q_stack),
                       K_PRIO_PREEMPT(5), NULL);
}

// 提交工作到自定义队列
void submit_to_my_q(void)
{
    struct k_work my_work;
    k_work_init(&my_work, work_handler);
    k_work_submit_to_queue(&my_work_q, &my_work);
}

工作项类型

1. 普通工作项 (k_work)

struct k_work work;

// 初始化
k_work_init(&work, handler);

// 提交(如果已在队列中,不会重复添加)
k_work_submit(&work);

// 检查是否正在等待执行
if (k_work_is_pending(&work)) {
    // 工作项已在队列中
}

// 取消(如果尚未执行)
int ret = k_work_cancel(&work);

2. 延迟工作项 (k_work_delayable)

struct k_work_delayable dwork;

// 初始化
k_work_init_delayable(&dwork, handler);

// 延迟提交(100ms后执行)
k_work_reschedule(&dwork, K_MSEC(100));

// 延迟提交到指定队列
k_work_reschedule_for_queue(&my_work_q, &dwork, K_MSEC(100));

// 取消延迟工作
int ret = k_work_cancel_delayable(&dwork);

// 检查是否到期
if (k_work_is_pending(&dwork.work)) {
    // 工作项已提交或正在执行
}

// 获取剩余时间
k_timeout_t remaining = k_work_delayable_remaining_get(&dwork);

3. 同步工作项 (k_work_sync)

struct k_work_sync sync;

// 同步取消 - 等待工作完成或从队列移除
bool pending = k_work_cancel_delayable_sync(&dwork, &sync);
// 或
bool pending = k_work_cancel_sync(&work, &sync);

高级用法

周期性任务

struct k_work_delayable periodic_work;

void periodic_handler(struct k_work *work)
{
    // 执行周期性任务
    do_periodic_task();
    
    // 重新调度,实现周期性
    k_work_reschedule(&periodic_work, K_MSEC(1000));
}

void init_periodic(void)
{
    k_work_init_delayable(&periodic_work, periodic_handler);
    // 启动第一次执行
    k_work_reschedule(&periodic_work, K_MSEC(1000));
}

工作队列与中断

// GPIO 中断回调
void gpio_callback(const struct device *dev, struct gpio_callback *cb, uint32_t pins)
{
    // 中断上下文:快速提交工作到队列
    k_work_submit(&irq_work);
}

// 工作处理函数(线程上下文)
void irq_work_handler(struct k_work *work)
{
    // 可以安全地执行阻塞操作
    process_gpio_event();
    k_msleep(10);  // 允许!
}

多个工作项协同

// 工作1:数据采集
struct k_work data_collection_work;
void collect_data(struct k_work *work)
{
    // 采集传感器数据
    // 然后提交处理工作
    k_work_submit(&data_processing_work);
}

// 工作2:数据处理
struct k_work data_processing_work;
void process_data(struct k_work *work)
{
    // 处理数据
    // 然后提交发送工作
    k_work_submit(&data_send_work);
}

// 工作3:数据发送
struct k_work data_send_work;
void send_data(struct k_work *work)
{
    // 通过网络发送
}

工作队列配置

# 系统工作队列
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096
CONFIG_SYSTEM_WORKQUEUE_PRIORITY=-1

# 工作队列统计
CONFIG_WORK_QUEUE_STATS=y

最佳实践

1. 选择合适的队列

2. 工作函数设计

void good_handler(struct k_work *work)
{
    // ✅ 快速执行,避免阻塞
    // ✅ 处理完后立即返回
    // ✅ 可以重新提交自己实现周期性
}

void bad_handler(struct k_work *work)
{
    // ❌ 不要在工作函数中长时间阻塞
    while (1) { }
    
    // ❌ 不要在工作函数中使用无限循环
    for (;;) { }
}

3. 资源管理

struct work_context {
    struct k_work work;
    uint8_t *buffer;
    size_t len;
};

void handler_with_context(struct k_work *work)
{
    struct work_context *ctx = CONTAINER_OF(work, struct work_context, work);
    // 使用 ctx->buffer 和 ctx->len
}

4. 取消工作的正确处理

// 如果需要在取消时知道是否成功
struct k_work_sync sync;
bool was_pending = k_work_cancel_delayable_sync(&dwork, &sync);

if (was_pending) {
    // 工作项被取消
} else {
    // 工作项已经完成或从未提交
}

性能考虑

特性系统队列自定义队列
启动开销无(已启动)需要初始化
内存占用共享栈独立栈
优先级固定(-1)可配置
适用场景简单异步任务复杂或实时任务

常见陷阱

  1. 栈溢出:工作队列栈太小导致崩溃
  2. 优先级反转:低优先级工作阻塞高优先级任务
  3. 竞态条件:工作取消和完成的竞争
  4. 内存泄漏:工作上下文未正确释放

调试技巧

// 启用工作队列调试
CONFIG_WORK_QUEUE_LOG_LEVEL_DBG=y

// 检查系统队列状态
extern struct k_work_q k_sys_work_q;
printk("System work queue pending: %d\n", 
       k_work_busy_get(&k_sys_work_q));

相关 API 总结

函数用途
k_work_init()初始化普通工作项
k_work_submit()提交到系统队列
k_work_submit_to_queue()提交到指定队列
k_work_init_delayable()初始化延迟工作项
k_work_reschedule()延迟调度工作
k_work_cancel()取消工作
k_work_cancel_delayable()取消延迟工作
k_work_cancel_sync()同步取消

参考链接


*学习笔记创建时间: 2026-03-19*