12.1 概述
Workqueue是Linux内核中的一种机制,用于延迟执行任务。它通过内核线程(worker threads)来执行这些任务,允许任务在进程上下文中运行,因此可以睡眠或阻塞。
12.2 Workqueue的作用
Workqueue的主要目的是提供一种灵活的机制来处理需要延迟执行的任务,特别是那些可能需要睡眠或更复杂处理的任务。它们可以在进程上下文中运行,这使得它们比Tasklet更灵活。
12.3 使用场合
Workqueue适用于以下场景:
• 需要睡眠的任务:例如需要等待某些资源或条件的任务。
• 复杂处理:需要执行较长时间或复杂处理的任务。
• 异步处理:需要在后台执行的任务,而不阻塞当前的执行路径。
12.4 注意事项
1). 上下文:Workqueue运行在进程上下文中,可以睡眠或阻塞。
2). 资源管理:确保正确管理和释放资源,避免内存泄漏。
3). 并发性:Workqueue可以在多个CPU上并发运行,注意并发访问共享资源时的同步问题。
12.5 Workqueue vs Tasklet
| 特性 | Tasklet | Workqueue |
|-----------------|--------------------------|------------------------------|
| 执行上下文 | 软中断上下文 | 进程上下文 |
| 阻塞操作 | 不允许 | 允许 |
| 使用场景 | 快速、非阻塞任务 | 可能需要睡眠或更长时间的任务 |
| 并发性 | 同一类型串行化 | 可能并发执行 |
| 优先级 | 正常或高 | 可配置 |
|-----------------|--------------------------|------------------------------|
12.6 Workqueue驱动实例
创建一个demoqueue.c的驱动源代码文件, 内容如下:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/workqueue.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
#include <linux/slab.h> // for kmalloc and kfree
static struct kobject *demo_kobj;
static void demo_work_handler(struct work_struct *work);
// dynamic create Workqueue
static struct workqueue_struct *demo_wq;
static struct work_struct *demo_work;
static void demo_work_handler(struct work_struct *work)
{
printk(KERN_INFO "Workqueue handler executed\n");
}
static ssize_t trigger_workqueue_store(struct kobject *kobj,
struct kobj_attribute *attr, const char *buf,
size_t count)
{
printk(KERN_INFO "%s, value = %s", __func__, buf);
queue_work(demo_wq, demo_work);
return count;
}
static struct kobj_attribute trigger_workqueue_attr = __ATTR(trigger_workqueue, 0220, NULL, trigger_workqueue_store);
static int __init demo_init(void)
{
int ret;
demo_kobj = kobject_create_and_add("demo_workqueue", kernel_kobj);
if (!demo_kobj)
{
return -ENOMEM;
}
ret = sysfs_create_file(demo_kobj, &trigger_workqueue_attr.attr);
if (ret)
{
kobject_put(demo_kobj);
return ret;
}
// create Workqueue
demo_wq = create_workqueue("demo_queue");
if (!demo_wq)
{
sysfs_remove_file(demo_kobj, &trigger_workqueue_attr.attr);
kobject_put(demo_kobj);
printk(KERN_ERR "Failed to create workqueue\n");
return -ENOMEM;
}
// allocate and init the Work structure.
demo_work = kmalloc(sizeof(struct work_struct), GFP_KERNEL);
if (!demo_work)
{
destroy_workqueue(demo_wq);
sysfs_remove_file(demo_kobj, &trigger_workqueue_attr.attr);
kobject_put(demo_kobj);
printk(KERN_ERR "Failed to allocate memory for work\n");
return -ENOMEM;
}
INIT_WORK(demo_work, demo_work_handler);
return 0;
}
static void __exit demo_exit(void)
{
sysfs_remove_file(demo_kobj, &trigger_workqueue_attr.attr);
kobject_put(demo_kobj);
// clear Workqueue
if (demo_wq)
{
destroy_workqueue(demo_wq);
}
// free Work structure
if (demo_work)
{
kfree(demo_work);
}
}
module_init(demo_init);
module_exit(demo_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Linx zhang");
MODULE_DESCRIPTION("Demo driver with Workqueue");
驱动代码里是动态创建workqueue, 如下:
demo_wq = create_workqueue("demo_queue");
demo_wq 是一个工作队列,它是一个内核机制,用于调度和执行延迟的工作。这样demo_wq 会在内核排队,并在稍后由内核线程处理。
同时, 也需要创建工作项demo_work, 如下:
demo_work = kmalloc(sizeof(struct work_struct), GFP_KERNEL);
并且初始工作项, 将工作项与处理函数(demo_work_handler)关联起来,以便在工作队列中调度时执行。如下:
INIT_WORK(demo_work, demo_work_handler);
最后, 当需要启动Workqueue任务时(demo_work)时, 则把工作项demo_work添加到工作队列 demo_wq 中进行调度和执行。如下:
queue_work(demo_wq, demo_work);
工作队列的内核线程会从队列中取出 demo_work 并调用其处理函数 demo_work_handler。
12.7 Makefile
obj-m += demoqueue.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
12.8 编译及加载
在设备终端, 执行如下命令:
neardi@LPA3588:~/drivers/workqueue$ make
make -C /lib/modules/5.10.110/build M=/home/neardi/drivers/workqueue modules
make[1]: Entering directory '/usr/src/linux-headers-5.10.110'
CC [M] /home/neardi/drivers/workqueue/demoqueue.o
MODPOST /home/neardi/drivers/workqueue/Module.symvers
CC [M] /home/neardi/drivers/workqueue/demoqueue.mod.o
LD [M] /home/neardi/drivers/workqueue/demoqueue.ko
make[1]: Leaving directory '/usr/src/linux-headers-5.10.110'
使用如下命令加载驱动:
neardi@LPA3588:~/drivers/workqueue$ sudo insmod demoqueue.ko
12.9 测试驱动
如下命令测试驱动:
neardi@LPA3588:~/drivers/workqueue$ sudo -i
root@LPA3588:~# echo 1 > /sys/kernel/demo_workqueue/trigger_workqueue
root@LPA3588:~# dmesg
[ 3442.941389] trigger_workqueue_store, value = 1
[ 3442.941922] Workqueue handler executed
Log显示Workqueue运行成功。