一、什么是延迟工作
延迟工作就是把任务推迟到后面再处理的一种方法。简单说就是:如果某个任务需要花很长时间,或者暂时不需要立刻执行,就可以先放一放,等后面再处理。
具体来说就是:
- 把需要延迟的任务先放进一个"任务队列"里
- 让电脑后台的程序自动去处理这些排队的任务
主要好处
- 解决耗时任务:比如发送邮件、处理照片这些需要时间的任务,可以先放到队列里,让用户继续操作其他功能不卡顿
- 定时执行任务:比如每天凌晨自动备份数据,或者设置在特定时间发送提醒
理想型的按键电压变化过程如图:

理想情况下,按键没被按下时显示为 1,按下后立即变为 0。但实际情况中,由于按键是机械结构,加上手指按下的瞬间可能出现抖动,实际电压变化会像图中那样出现波动。

按键按下时,因为机械结构的抖动,电平信号会不稳定跳变。我们通过以下步骤处理:
- 触发检测:当按键第一次按下时,立刻启动一个 20 毫秒的计时器。
- 等待稳定:在这 20 毫秒内,忽略所有按键状态的变化。
- 最终确认:20 毫秒后,重新检查按键状态。如果此时按键仍然处于按下状态,就认定这是一次有效的按键操作。
二、struct delayed_work
在 Linux 内核中,使用 struct delayed_work 来描述延迟工作,定义在 include/linux/workqueue.h 当中:
c
struct delayed_work {
struct work_struct work; // 延迟工作的基本工作结构
struct timer_list timer; // 定时器,用于延迟执行工作
};1
2
3
4
2
3
4
delayed_work 结构体用于实现延迟执行的任务,它包含两个关键部分:
- work:一个工作项结构(
work_struct),用来定义具体要执行的任务内容; - timer:一个定时器结构(
timer_list),用来设定任务的延迟时间和触发时机。
三、延迟工作相关接口函数
1、初始化延迟工作函数
静态定义延迟任务(DECLARE_DELAYED_WORK)
c
DECLARE_DELAYED_WORK(变量名, 处理函数);1
动态初始化延迟任务(INIT_DELAYED_WORK)
c
INIT_DELAYED_WORK(变量名, 处理函数);1
| 场景 | 静态定义(DECLARE) | 动态初始化(INIT) |
|---|---|---|
| 何时使用 | 编译时预先定义变量和初始化 | 运行时动态初始化已声明的变量 |
| 特点 | 一步到位,代码简洁 | 分两步操作,但更灵活 |
示例:
c
// 静态定义
DECLARE_DELAYED_WORK(my_work, handle_my_work);
// 动态初始化
struct delayed_work my_work;
INIT_DELAYED_WORK(&my_work, handle_my_work);1
2
3
4
5
6
2
3
4
5
6
2、调度/取消调度延迟工作函数
使用共享工作队列调度
c
static inline bool schedule_delayed_work(struct delayed_work *dwork, unsigned long delay);1
使用自定义工作队列调度
c
static inline bool queue_delayed_work(struct workqueue_struct *wq,
struct delayed_work *dwork,
unsigned long delay);1
2
3
2
3
取消延迟工作
c
extern bool cancel_delayed_work_sync(struct delayed_work *dwork);1
使用建议
- 如果用系统默认队列:用
schedule_delayed_work - 如果用自定义队列:用
queue_delayed_work,并指定队列wq - 取消任务:用
cancel_delayed_work_sync,确保任务被终止且完全停止
四、实验代码
源代码下载
bash
git clone git@gitee.com:yangxuesong314/linux-driver.git1
- 若之前已 git 拉取代码可以忽略
- 代码位于:
linux-driver/04.中断及异常/04.延迟工作实验
实验说明
- GPIO1_A4 →
1 * 32 + 0 * 8 + 4 = 36
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 delayed_work 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_delayed_work(test_workqueue, &test_workqueue_work, 3 * HZ);
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_DELAYED_WORK(&test_workqueue_work, test_work);
return 0;
}
static void interrupt_irq_exit(void)
{
free_irq(irq, NULL);
cancel_delayed_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
五、实验截图
驱动加载:insmod interrupt.ko

拉高 GPIO:
