工作队列(workqueue)
概述
zephyr中的工作队列与Linux的工作队列功能类似,用于实现中断的底半部。
也就是说中断ISR中比较耗时的操作,放到工作队列中去执行。zephyr中工作队列是基于线程的,简单来说,就是有一个线程一直在等待工作队列的api发来的工作项,当有工作项时(一个待 执行的函数)就处理(把函数调用了),当有多个工作项时就按顺序处理,没有工作项时就休眠。
工作队列线程
工作队列是一个内核对象,用专用的线程以先进先出(FIFO)的方式去处理被提交的工作元项(work item)。
每个被处理的工作项会调用这个工作项指定的函数(通俗来讲,工作项就是一个一个等待调用的函数)。
使用方式类似于Linux的工作队列和Tasklet。
系统工作队列
内核定义了一个叫做 系统工作队列 的工作队列。所有的应用程序或者内核代码都可以使用该工作队列。系统工作队列是可选的,且只有当应用程序使用时才存在。
注:只有当无法向系统工作队列提交新的工作项时,才去创建额外的工作队列。因为每个新的工作队列都会花费可观的内存占用。如果新工作队列中的工作项无法与系统工作队列中已存在的工作项共存时,可以调整新的工作队列。例如,新的工作项执行了阻塞操作导致其它系统工作队列被延迟到一个不可接受的程序。
结构体
k_work (工作项)
struct k_work {
void *_reserved; /* Used by k_queue implementation. */
k_work_handler_t handler;
atomic_t flags[1];
};
k_work_q (工作队列,本质上讲是一个线程,创建一个新的工作队列,相当于创建一个新的线程)
struct k_work_q {
struct k_queue queue;
struct k_thread thread;
};
API
void k_work_init(struct k_work *work, k_work_handler_t handler)
void k_work_q_start(struct k_work_q *work_q, k_thread_stack_t *stack,size_t stack_size, int prio)
//可以将已初始化的工作项提交到系统工作队列中;
void k_work_submit(struct k_work *work)
//将已初始化的工作项提交到指定的工作队列中
void k_work_submit_to_queue(struct k_work_q *work_q, struct k_work *work)
bool k_work_pending(struct k_work *work)
delayed API
void k_delayed_work_init(struct k_delayed_work *work, k_work_handler_t handler)
int k_delayed_work_submit(struct k_delayed_work *work,s32_t delay)
int k_delayed_work_submit_to_queue(struct k_work_q *work_q,struct k_delayed_work *work,s32_t delay)
int k_delayed_work_cancel(struct k_delayed_work *work)
样例:
定义工作队列
#define MY_STACK_SIZE (K_THREAD_SIZEOF + 500)
#define MY_PRIORITY 5
char __noinit __stack my_stack_area[MY_STACK_SIZE];
struct k_work_q my_work_q;
//工作队列是线程,需要指定线程栈、优先级等
k_work_q_start(&my_work_q, my_stack_area, MY_STACK_SIZE, MY_PRIORITY);
提交工作项
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) */
...