中断的上半部和下半部
什么是上半部和下半部
对于中断处理函数来说,我们要求尽可能时间短,才不容易出现由于当前中断处理时间过长而导致的其他线程的一些异常,例如获取某些资源失败。
但是可能因为某些原因导致中断的处理无法控制在一个比较短的时间内,因此引入了中断的上半部和下半部,在上半部处理一些简单时间短的任务,例如初始化设备,申请设备节点等,而将那些耗时的操作放在下半部去执行。
操作系统在执行中断时,会先执行中断的上半部,然后中断处理函数就会返回,系统去执行中断之前的任务了,之后让系统调度下半部继续执行即可。
中断的上半部
中断处理函数就是中断的上半部
中断的下半部
实现下半部的方法:
1、softirq:处理比较快,但是是内核级别的机制,需要修改整个内核源码,不常用也不推荐
2、tasklet:内部实现实际调用了softirq
3、workqueue:工作队列
对于tasklet来说,我们是将下半部通过一个结构体去管理(其中包含了下半部的属性和方法),然后将该结构体放入内核提供的一个链表中,让系统去调度我们的方法。
对于workqueue来说,我们是将下半部通过一个结构体去管理(其中包含了下半部的属性和方法),然后将该结构体放入内核提供的一个队列中,让系统去调度我们的方法。
二者的区别在于tasklet是在中断上下文中运行的,其中的方法是不能进行休眠的,而workqueue是在进程上下文中运行的,其中的方法是可以休眠的,也就是可以执行一些阻塞类型的函数
tasklet实现方法
1、初始化
设计一个tasklet_struct结构体,调用tasklet_init函数,
参数1:结构体指针
参数2:下半部的实现逻辑
参数3:要赋给结构体中的data,最终会传入func函数
2、启动-------将下半部放入到上半部中进行启动
顾名思义,需要将下半部处理方法放入上半部也就是中断处理函数中,需要调用tasklet_schedule函数。
3、卸载下半部
tasklet_kill()
workqueue实现
1、初始化
struct work_struct mywork;
INIT_WORK(struct work_struct* work, work_func_t func)
2、启动-------将下半部放入到上半部中进行启动
schedule_work(work_struct* work)
3、卸载模块–从内核中移除work
cancel_work_sync(struct work_struct * work)