《【北京迅为】itop-3568开发板驱动开发指南.pdf》 学习笔记
文章目录
- tasklet 简介
- tasklet 结构体
- tasklet 初始化
- 使能 tasklet
- 失能 tasklet
- tasklet 调度函数
- tasklet 取消调度函数
- tasklet 实验
tasklet 简介
Tasklets 机制是linux中断处理机制中的软中断延迟机制。在linux中存在着硬中断和软中断的概念区分。
机制流程:当linux接收到硬件中断之后,通过tasklet函数来设定软中断被执行的优先程度从而导致软中断处理函数被优先执行的差异性。
特点:tasklet的优先级别较低,而且中断处理过程中可以被打断。但被打断之后,还能进行自我恢复,断点续运行。——百度百科
在 Linux 内核中,一般使用 tasklet 机制来实现中断下文,tasklet 任务在同一时间只能在一个 CPU 上运行,所以在多核系统上不会存在并发问题,但正因如此,它的执行优先级会比硬中断低。另外,tasklet 任务函数不能调用任何可能会引起休眠的函数,否则会导致内核异常。
tasklet 结构体
tasklet 结构体定义在内核 include/linux/interrupt.h 头文件中:
struct tasklet_struct
{
struct tasklet_struct *next; // 链表中的下一个 tasklet 结构体节点
unsigned long state; // tasklet 状态
atomic_t count; // 原子类型计数器
void(*func) (unsigned long data); // tasklet 处理函数
unsigned long data; // tasklet 处理函数的参数
}
tasklet 初始化
动态初始化
void tasklet_init(struct tasklet_struct *t, void (*func)(unsigned long), unsigned long data)
功能:动态初始化 tasklet_struct 结构体,将计数器 count 和状态 state 置 0。
参数:
- t: tasklet_struct 结构体指针
- func:tasklet 处理函数
- data:tasklet 处理函数的参数
静态初始化
静态初始和动态初始化功能相同,实际上就是动态初始化的宏函数,
DECLARE_TASKLET(name, func, data);
DECLARE_TASKLET_DISABLED(name, func, data);
name 参数对应 tasklet_struct 结构体名,其他参数与动态初始化相同。
DECLARE_TASKLET 和 DECLARE_TASKLET_DISABLED 的区别在于初始化时 tasklet_struct 结构体变量中 count 的值不同,当 count 为 0 时,表示 tasklet 使能,当 count 为 1 时,表示 tasklet 失能。
这里的 count 值会影响 tasklet 任务的调度,只有 count 为 0 时,tasklet 任务才会被调度。
使能 tasklet
tasklet 使能实际就是把 t->count 减 1。
void tasklet_enable (struct tasklet_struct *t)
失能 tasklet
tasklet 失能实际就是把 t->count 加 1。
void tasklet_disable(struct tasklet_struct *t)
tasklet 调度函数
tasklet_schedule() 会让 t->func() 执行(前提是 t->count 为 0)。
void tasklet_schedule (struct tasklet_struct *t)
由于 tasklet 任务执行优先级并不高,所以连续多次调用调度函数,可能只会执行一次。
tasklet 取消调度函数
tasklet_kill() 会将已经调度的 tasklet 停止调度。
void tasklet_kill(struct tasklet_struct *t)
如果 tasklet 任务正在被调度执行,tasklet_kill() 将会等待其退出,tasklet_kill() 完成前应避免再次调度。另外,不能在 tasklet 处理函数里调用 tasklet_kill()。
tasklet 实验
在上一个中断实验的代码上添加中断下文处理函数,即 tasklet 任务。
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
int irq;
struct tasklet_struct mytasklet;
//tasklet 处理函数
void my_tasklet(unsigned long data)
{
printk("data is %ld.\n", data);
}
//静态初始化 tasklet
DECLARE_TASKLET(mytasklet, my_tasklet, 6);
//中断服务函数
irqreturn_t my_interrupt(int irq, void *args)
{
printk("my interrupt handler.\n");
// tasklet 调度
tasklet_schedule(&mytasklet);
return IRQ_RETVAL(IRQ_HANDLED);
}
static int interrupt_irq_init(void)
{
int ret = 0;
// 获取中断号
irq = gpio_to_irq(101);
printk("irq is %d\n", irq);
// 申请中断
ret = request_irq(irq, my_interrupt, IRQF_TRIGGER_RISING, "inttrupt_test", NULL);
if(ret < 0)
{
printk("request irq error.\n");
return 0;
}
return 0;
}
static void interrupt_irq_exit(void)
{
printk("interrupt irq exit.\n");
// 注销中断
free_irq(irq, NULL);
// 摧毁 tasklet
tasklet_kill(&mytasklet);
}
module_init(interrupt_irq_init);
module_exit(interrupt_irq_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("xiaohui");
实验结果
触碰触摸屏,中断被触发,先执行中断服务函数,然后再执行 tasklet 处理函数。