第7篇 zephyr kernel之工作队列Workqueue

目录

摘要

1 工作队列线程 Workqueue Threads

2 工作项生命周期Work Item Lifecycle

3 延时工作队列 Delayed Work

4 触发工作项Triggered Work

5 系统工作队列System Workqueue

6 用户定义一个工作队列

7 提交一个工作项

8 提交一个延时工作项

9 参考链接


本学笔记基于zephyr 工程版本 2.2.99,主机环境为ubuntu18.04,开发平台 nrf52840dk_nrf52840

 

摘要

    熟悉Linux的童鞋可能对工作队列比较熟悉,zephyr中的工作队列与Linux的工作队列功能类似,用于实现中断的底半部。也就是说中断ISR中比较耗时的操作,放到工作队列中去执行。zephyr中工作队列是基于线程的,简单来说,就是有一个线程一直在等待工作队列的api发来的工作项,当有工作项时(一个待执行的函数)就处理(把函数调用了),当有多个工作项时就按顺序处理,没有工作项时就休眠。

1 工作队列线程 Workqueue Threads

   工作队列是一个内核对象,用专用的线程以先进先出(first in, first out)的方式去处理被提交的工作元素(work item)。每个被处理的工作项会调用这个工作项指定的函数(通俗来讲,工作项就是一个一个等待调用的函数)。工作队列通常用于ISR或者高优先级线程把比较复杂的,非紧急的事情交给对时间不敏感的低于先级线程去处理。对Linux驱动比较了解的童鞋理解起来来叫简单,使用方式类似于Linux的工作队列和Tasklet。

    工作队列使用之前必须被初始化,并且清空他的队列创建一个工作队列线程(都是初始化函数来完成的)。

2 工作项生命周期Work Item Lifecycle

    一个工作项(或者叫工作元素,我也不知道怎么翻译好,随便叫吧 )使用之前必须被初始化,初始化就是把处理函数赋值给工作项,并且标记工作项是为挂起的,等着工作队列线程去调用。

    一个工作项可以从ISR或者线程中被提交,提交以后就是把工作项追加到工作队列内部的队列中,加入工作队列以后,当工作队列的线程会从他自己的队列中按顺序取出挂起的工作项,然后在执行这个工作项指定的函数。

   工作项中可是使用任何的内核的API,但是注意使用那些导致线程阻塞的API,比如睡眠,获取信号量之类的API,这样会导致工作队列线程睡眠,那么同时处于这个工作队列的其他工作项,也将会被延时执行,因为工作项是按顺序,一个一个被串行处理的。如果ISR或者线程重复提交一个已经在工作队列中已经挂起的工作项,那么这个工作项不会受到影响,同时在工作队列中的位置将不会受到影响。

  被提交的工作在没有被处理之前,是不能被重新初始化的。

3 延时工作队列 Delayed Work

   当一个ISR或者线程提交一个工作项,但是不想让工作项立即被执行,想让工作项等待一段时间在执行。这时可以使用延时工作项。简单理解就是,一个工作项提交的时候不是直接提交给工作队列,而是指定一个超时,当超时发生的时候,再由内核将这个工作项提交给工作队列,并保持工作项为挂起状态等着工作队列线程去处理。

4 触发工作项Triggered Work

  触发工作项是和POLL机制相关的工作项,用的比较少。POLL机制开发Linux应用的童鞋比较了解,在Linux中POLL机制是在一个线程中等待多个未就绪的文件描述有效,而zephyr是在一个线程中等待多个内核对象有效,比如等待信号量,FIFO等。

5 系统工作队列System Workqueue

   在文中要区分工作队列和工作项的区别,工作项(work item)只是一个被提交到工作队列(work queue)的一个元素。如果内核使能了工作队列的功能,内核会自动创建的一个被称为system workqueuede 的工作队列。这个系统工作队列的线程的优先级可以通过menuconfig去配置。相比于系统通过队列,用户也可以创建自己的工作队列。比如有比较复杂然后特别耗时的事情需要做,那么用户可以自己创建一个工作队列,然后把时间不敏感,不着急处理的事扔给自己创建的工作队列。但是zephyr官方不推荐自己创建工作队列,因为工作队列会消耗大量资源(RAM, flash),毕竟创建一个工作队列就会创建一个线程,一个线程就会包括何种内核对象,私有栈空间等,一般zephyr是跑在资源受限的MCU上的,所以不太推荐。

6 用户定义一个工作队列

 工作队列使用struct k_work_q类型去定义,工作队列需要使用自己定义的栈,然后调用k_work_q_start()去初始化:

#define MY_STACK_SIZE 512
#define MY_PRIORITY 5

K_THREAD_STACK_DEFINE(my_stack_area, MY_STACK_SIZE);

struct k_work_q my_work_q;

k_work_q_start(&my_work_q, my_stack_area,
               K_THREAD_STACK_SIZEOF(my_stack_area), MY_PRIORITY);

7 提交一个工作项

    一个工作项在被提交之前需要调用k_work_init()去初始化,然后一个被初始化的工作项可以使用 k_work_submit()函数把工作项提交到系统工作队列,也可以使用k_work_submit_to_queue()函数提交到用户自己定义的工作队列。

   下面代码示例,一个ISR把打印信息提交给系统工作队列去执行:

struct device_info {
    struct k_work work;
    char name[16]
} my_device;

void my_isr(void *arg)
{
    ...
    if (error detected) {
        k_work_submit(&my_device.work);
    }
    ...
}

void print_error(struct k_work *item)
{
    struct device_info *the_device =
        CONTAINER_OF(item, struct device_info, work);
    printk("Got error on device %s\n", the_device->name);
}

/* initialize name info for a device */
strcpy(my_device.name, "FOO_dev");

/* initialize work item for printing device's error messages */
k_work_init(&my_device.work, print_error);

/* install my_isr() as interrupt handler for the device (not shown) */
...

8 提交一个延时工作项

   一个延时工作项通过struct k_delayed_work去定义,并通过 k_delayed_work_init()去初始化,通过调用k_delayed_work_submit()把延时工作项提交给系统工作队列,通过k_delayed_work_submit_to_queue()把延时工作项提交给用户自己定义的工作队列。当想取消一个超时工作项,可以使用 k_delayed_work_cancel()函数,但是需注意,取消只能在工作项指定的超时没发生之前,否则不能被取消。

9 参考链接

https://docs.zephyrproject.org/latest/reference/kernel/threads/workqueue.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值