较好的帖子 https://blog.csdn.net/zqixiao_09/article/details/50876866
tasklet是一种软中断延迟机制,是可延迟函数的首选方法
一、
1、为什么要有Tasklets 机制?它的作用是什么?
Tasklets 机制是linux中断处理机制中的软中断延迟机制。通常用于减少中断处理的时间,将本应该是在中断服务程序中完成的任务转化成软中断完成。
为了最大程度的避免中断处理时间过长而导致中断丢失,有时候我们需要把一些在中断处理中不是非常紧急的任务放在后面执行,而让中断处理程序尽快返回。
2、Tasklets 解决什么问题?
a -- tasklet是I/O驱动程序中实现可延迟函数的首选方法;
b -- tasklet和工作队列是延期执行工作的机制,其实现基于软中断,但他们更易于使用,因而更适合与设备驱动程序...tasklet是“小进程”,执行一些迷你任务,对这些人物使用全功能进程可能比较浪费。
c -- tasklet是并行可执行(但是是锁密集型的)软件中断和旧下半区的一种混合体,这里既谈不上并行性,也谈不上性能。引入tasklet是为了替代原来的下半区。
软中断是将操作推迟到未来时刻执行的最有效的方法。但该延期机制处理起来非常复杂。因为多个处理器可以同时且独立的处理软中断,同一个软中断的处理程序可以在几个CPU上同时运行。对软中断的效率来说,这是一个关键,多处理器系统上的网络实现显然受惠于此。但处理程序的设计必须是完全可重入且线程安全的。另外,临界区必须用自旋锁保护(或其他IPC机制),而这需要大量审慎的考虑。
二、tasklet数据结构
tasklet通过软中断实现,软中断中有两种类型属于tasklet,分别是级别最高的HI_SOFTIRQ和TASKLET_SOFTIRQ。
tasklet的核心结构体如下(include/linux/interrupt.h):
- struct tasklet_struct
- {
- struct tasklet_struct *next;
- unsigned long state;
- atomic_t count;
- void (*func)(unsigned long);
- unsigned long data;
- };
各成员的含义如下:
a -- next指针:指向下一个tasklet的指针。
b -- state:定义了这个tasklet的当前状态。这一个32位的无符号长整数,当前只使用了bit[1]和bit[0]两个状态位。其中,bit[1]=1表示这个tasklet当前正在某个CPU上被执行,它仅对SMP系统才有意义,其作用就是为了防止多个CPU同时执行一个tasklet的情形出现;bit[0]=1表示这个tasklet已经被调度去等待执行了
三、tasklet操作接口
tasklet对驱动开放的常用操作包括:
a -- 初始化,tasklet_init(),初始化一个tasklet描述符。
b -- 调度,tasklet_schedule()和tasklet_hi_schedule(),将taslet置位TASKLET_STATE_SCHED,并尝试激活所在的软中断。
c -- 禁用/启动,tasklet_disable_nosync()、tasklet_disable()、task_enable(),通过count计数器实现。
d -- 执行,tasklet_action()和tasklet_hi_action(),具体的执行软中断。
e -- 杀死,tasklet_kill()
------------------------------------程序分析如下------------------------------------
-------------------------------------key_tasklets.c-----------------------------------
#include <linux/module.h>
2 #include <linux/init.h>
3 #include <linux/major.h>
4 #include <linux/cdev.h>
5 #include <linux/device.h>
6 #include <linux/types.h>
7 #include <linux/fs.h>
8 #include <asm/uaccess.h>
9 #include <linux/io.h>
10 #include <linux/sched.h>
11 //#include <asm/irq.h>
12 #include <linux/gpio.h>
13 #include <linux/interrupt.h>
14 #include <linux/input.h>
15
16 #define TAG "tasklets"
17 #define INT_GPIO 91
18 #define MY_KEY_CODE KEY_A //上报字母A
19
20 static int irq;
21 static struct input_dev *kinput;
22 static struct tasklet_struct key_tasklet;
23
24 volatile unsigned long *tlmm_gpio_cfg;
25 volatile unsigned long *tlmm_in_out;
26
27 static void do_key_tasklet(void *data)
28 {
29 int value;
30
31 printk(TAG"%s\n", __func__);
32 value = *tlmm_in_out;
33 value &= 0x1;
34 if (value) {
35 input_report_key(kinput, KEY_RIGHTSHIFT, 0);
36 input_report_key(kinput, MY_KEY_CODE, 0);
37 input_sync(kinput);
38 printk(TAG"key is release\n");
39 } else {
40 input_report_key(kinput, KEY_RIGHTSHIFT, 1);
41 input_report_key(kinput, MY_KEY_CODE, 1);
42 input_sync(kinput);
43 printk(TAG"key is press\n");
44 }
45 }
46
47 static irqreturn_t key_irq_thread(int irq, void *data)
48 {
49 printk(TAG"%s\n", __func__);
50 tasklet_schedule(&key_tasklet); //该函数的参数指向要在当前CPU上被执行的tasklet,函数 tasklet_schedule 用来调度一个 tasklet 运行
51
52 return IRQ_HANDLED;
}
54
55 static int my_key_init(void)
56 {
57 int retval;
58
59 printk(TAG" func:%s line%d\n", __func__, __LINE__);
60
61 kinput = input_allocate_device();
62 if (!kinput)
63 return -ENOMEM;
64 kinput->name = "keyt";
65 __set_bit(EV_KEY, kinput->evbit);
66 __set_bit(MY_KEY_CODE, kinput->keybit);
67 __set_bit(KEY_RIGHTSHIFT, kinput->keybit);
68
69 retval = input_register_device(kinput);
70 if(retval)
71 goto input_register_error;
72
73 tlmm_gpio_cfg = (volatile unsigned long *)ioremap(0x105B000, 8);
74 tlmm_in_out = tlmm_gpio_cfg + 1;
75 *tlmm_gpio_cfg |= 0x3;
76
77 irq = gpio_to_irq(INT_GPIO);
78 printk(TAG"%s irq is %d\n", __func__, irq);
79 retval = request_threaded_irq(irq, NULL, key_irq_thread, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING |IRQF_ONESHOT, "vol_k ey", NULL);
80 printk(TAG"%s ret is %d\n", __func__, retval);
81 /*用来初始化一个指定的tasklet描述符
82 tasklet_init(&key_tasklet, (void (*)(unsigned long))do_key_tasklet, (unsigned long)&kinput);
83
84 return 0;
85
86 input_register_error:
87 input_free_device(kinput);
88 return retval;
89 }
90
91 static void key_exit(void)
92 {
93 printk(TAG" func:%s line%d\n", __func__, __LINE__);
94 tasklet_kill(&key_tasklet);
95 iounmap(tlmm_gpio_cfg);
96 free_irq(irq, NULL);
97 input_unregister_device(kinput);
98 printk(TAG" func:%s line%d\n", __func__, __LINE__);
99 }
100
101 module_init(my_key_init);
102 module_exit(key_exit);
103 MODULE_LICENSE("GPL");