共享队列是操作系统内核统一管理的公共任务池,所有程序都可以使用这个共用的队列来处理任务。而自定义队列则是由操作系统或设备驱动专门创建的独立任务池,用来执行特定的定制化操作。
简单总结
- 共享队列:系统自带的公共任务通道,大家都可以用
- 自定义队列:专门定制的任务通道,为特定功能服务
一、自定义工作队列介绍
源代码下载
bash
git clone git@gitee.com:yangxuesong314/linux-driver.git1
- 若之前已 git 拉取代码可以忽略
- 代码位于:
linux-driver/04.中断及异常/03.自定义工作队列的使用
1、工作队列相关结构体
work_struct 工作项
在 Linux 内核中,结构体 struct work_struct 描述的是要延迟执行的工作项,定义在 include/linux/workqueue.h 当中:
c
struct work_struct {
atomic_long_t data;
struct list_head entry;
work_func_t func;
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
主要成员
atomic_long_t data:存储工作项数据的字段,支持安全的多线程访问struct list_head entry:工作项在队列中的链表节点,用于将工作项连接到队列中work_func_t func:工作项执行时调用的处理函数
workqueue_struct 工作队列
内核使用 struct workqueue_struct 结构体描述一个工作队列,定义在 kernel/workqueue.c 中。
2、工作队列相关接口函数
创建工作队列
c
create_workqueue(name)1
- 作用:创建一个工作队列,每个 CPU 都会对应一个独立的线程。
- 参数:
name是队列的名字。
c
create_singlethread_workqueue(name)1
- 作用:创建一个仅单线程的工作队列,不管有多少 CPU,只创建一个线程。
将工作项添加到队列
c
queue_work_on(cpu, wq, work)1
- 作用:把一个工作项立即放入指定 CPU 的队列,等待执行。
- 参数:
cpu:目标 CPU 编号wq:目标队列的指针work:要执行的工作项
取消已调度的工作
c
cancel_work_sync(work)1
强制处理队列中的所有工作
c
flush_workqueue(wq)1
删除工作队列
c
destroy_workqueue(wq)1
使用流程总结
- 创建队列:用
create_workqueue或create_singlethread_workqueue - 添加工作:用
queue_work_on将工作项放入队列 - 取消工作:用
cancel_work_sync终止未执行的工作 - 清理队列:用
flush_workqueue确保所有工作完成,再用destroy_workqueue删除队列
关键注意事项
- 单线程队列:
create_singlethread_workqueue的线程是串行执行的 - 多线程队列:
create_workqueue的每个 CPU 线程独立运行 - 删除前必须清理:删除队列前必须用
flush_workqueue保证所有工作已完成
二、自定义共享队列案例
实验说明
- 硬件 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 workqueue_struct *test_workqueue;
struct work_struct test_workqueue_work;
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");
queue_work(test_workqueue, &test_workqueue_work);
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;
}
test_workqueue = create_workqueue("test_workqueue");
INIT_WORK(&test_workqueue_work, test_work);
return 0;
}
static void interrupt_irq_exit(void)
{
free_irq(irq, NULL);
cancel_work_sync(&test_workqueue_work);
flush_workqueue(test_workqueue);
destroy_workqueue(test_workqueue);
printk("bye bye\n");
}
module_init(interrupt_irq_init);
module_exit(interrupt_irq_exit);
MODULE_LICENSE("GPL");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
53
54
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
53
54
3、实验截图
驱动加载:insmod interrupt.ko

拉高 GPIO:

三、实现
c
static inline bool queue_work(struct workqueue_struct *wq,
struct work_struct *work)
{
return queue_work_on(WORK_CPU_UNBOUND, wq, work);
}1
2
3
4
5
2
3
4
5
c
extern bool queue_work_on(int cpu, struct workqueue_struct *wq,
struct work_struct *work);1
2
2
c
// kernel/workqueue.c
bool queue_work_on(int cpu, struct workqueue_struct *wq,
struct work_struct *work)
{
bool ret = false;
unsigned long flags;
local_irq_save(flags);
if (!test_and_set_bit(WORK_STRUCT_PENDING_BIT, work_data_bits(work))) {
__queue_work(cpu, wq, work);
ret = true;
}
local_irq_restore(flags);
return ret;
}
EXPORT_SYMBOL(queue_work_on);1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18