工作队列是管理任务的机制,用来把需要处理的任务按顺序排好队,再由多个线程或进程来执行这些任务。它的核心逻辑很简单:新任务来时排到队列末尾,工作线程或进程从队列开头取任务执行。
和 tasklet 的区别
- 能否暂停:tasklet 不能暂停(比如等待磁盘读写),而工作队列可以暂停,适合处理耗时任务。
- 任务类型:tasklet 适合处理短小但需要立即执行的任务,工作队列适合处理需要较长时间或可能卡住的任务。
工作队列的执行方式是:
- 系统启动时会创建一个"后台线程",默认处于等待状态。
- 当有任务要处理时,这个线程会被叫醒执行任务,完成后又进入等待状态。
工作队列分两种:
- 共享队列:多个程序共用同一个任务队列,适合简单任务。
- 自定义队列:自己创建独立队列,能更灵活控制任务执行顺序和优先级。
一、工作队列相关接口函数
- 初始化:先定义
work_struct,再用宏配置任务函数。 - 调度:调用
schedule_work()把任务丢进队列。 - 取消:用
cancel_work_sync()强行终止(会等当前执行结束)。
1、初始化工作项
定义工作项:
c
struct work_struct my_work;1
初始化方式:
方法 1:单独初始化
cINIT_WORK(&my_work, my_work_handler);1方法 2:一步到位
cDECLARE_WORK(my_work, my_work_handler);1
2、调度与取消工作项
安排执行:
c
bool success = schedule_work(&my_work);1
- 作用:把你的任务放进系统的工作队列,等待空闲时自动执行。
- 返回值:
true表示成功加入队列,false表示任务已经在执行中。
取消执行:
c
bool canceled = cancel_work_sync(&my_work);1
- 作用:如果任务还在队列里,立刻移除它;如果任务已经开始执行,会等它执行完再取消。
二、共享工作队列实验源码
源代码下载
bash
git clone git@gitee.com:yangxuesong314/linux-driver.git1
- 若之前已 git 拉取代码可以忽略
- 代码位于:
linux-driver/04.中断及异常/02.共享工作队列的使用
实验说明
- 硬件 IO 表:https://lceda001.feishu.cn/wiki/UN0pw7Y1KixKirkMh0rcP3Tandc
- 操作第七个管脚触发中断,对应 GPIO1_A4
- GPIO1_A4 →
1 * 32 + 0 * 8 + 4 = 36
1、Makefile
makefile
export ARCH=arm64
export CROSS_COMPILE=/home/book/rk/tspi/prebuilts/gcc/linux-x86/aarch64/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-
obj-m += interrupt.o
KDIR := /home/book/rk/tspi/kernel
PWD ?= $(shell pwd)
all:
make -C $(KDIR) M=$(PWD) modules
clean:
make -C $(KDIR) M=$(PWD) clean1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
2、驱动部分代码
c
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
int irq;
struct work_struct test_workqueue;
// 工作项处理函数
void test_work(struct work_struct *work)
{
msleep(1000);
printk("This is test_work\n");
}
// 中断处理函数
irqreturn_t test_interrupt(int irq, void *args)
{
printk("This is test_interrupt\n");
schedule_work(&test_workqueue); // 提交工作项到工作队列
return IRQ_RETVAL(IRQ_HANDLED);
}
static int interrupt_irq_init(void)
{
int ret;
irq = gpio_to_irq(36);
printk("irq is %d\n", irq);
ret = request_irq(irq, test_interrupt, IRQF_TRIGGER_RISING, "test", NULL);
if (ret < 0) {
printk("request_irq is error\n");
return -1;
}
INIT_WORK(&test_workqueue, test_work);
return 0;
}
static void interrupt_irq_exit(void)
{
free_irq(irq, NULL);
printk("bye bye\n");
}
module_init(interrupt_irq_init);
module_exit(interrupt_irq_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("linux");1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
三、实验截图
驱动加载:insmod interrupt.ko

拉高 GPIO1_A4:
