中断的分类:
来源:外部,内部
是否可屏蔽:可屏蔽中断,不可屏蔽中断
按中断入口跳转方法的不同:向量和非向量
申请和释放IRQ:
int request_irq(unsigned int irq,//要申请的中断号
irqreturn t(*handler)(int void *,struct pt_regs *),//要安装的中断处理函数指针
unsigned long flags,//0是普通外部中断,SA_INTERRUPT快速中断,SA_SHIRQ共享中断
const char *dev_name,
void *dev_id);//用于共享的中断数据线。用来唯一的表示设备
禁止/使能单个中断
为什么将中断处理程序分成顶半部和底半部?
需求:
不能使中断阻塞的时间过长
完成耗时的任务
解决方案:
顶半部,让中断阻塞尽可能短
底半部,完成耗时的任务
顶半部:
实际响应中断的例程
用request_irq注册的中断例程
在很短的时间内完成
底半部: (不会被执行,而是被调度)
被顶半部调度,并在稍后更安全的时间内执行的例程
实现的机制:
实现底半部的机制:tasklet(任务队列),work queue(工作队列)
二者的区别:tasklet(速度快,优先选择,原子操作,运行于中断上下文)
work queue(高延迟,允许休眠,运行于进程上下文)
void free_irq(unsigned int irq,void *dev_id);//释放
中断处理流程:
产生->跳转到中断向量表入口地址(arch/arm/kernel/entry-armv.S)->asm_do_IRQ(){arch/arm/lkernel/irq.c根据irq编号从已申请的中断(通过request_irq)找到该irq编号对应的中断处理函数}->执行对应的中断 处理函数{执行你自己的中断处理函数}->返回到asm_do_IRQ()->返回到entry-armv.S(恢复到终端前的上下文状态,结束中断处理,继续执行中断发生前的程序)
tasklet的使用:
声明tasklet
declare_tasklet(name,function,data);
name:tasklet的名字
function:执行tasklet时调用的函数
data:一个用来传递给tasklet函数的unsigned long 类型的值
例:DECLARE_TASKLET(xxx_tasklet,xxx_do_tasklet,0)
调度tasklet:
void tasklet_shedule(struct tasklet_struct *t)
创建新的工作队列:
//一个工作队列可对应多个内核线程
struct workqueue_struct *create_workqueue(const char *name)
//一个工作队列对应单个线程
struct workqueue_struct *create_singlethread_workqueue(const char *name);
向工作队列提交任务,首先填充一个work_struct结构
DECLARE_WORK(name,void (*function)(void *),void *data);//申明
init_WORK(struct work_struct *work,void (*function)(void *),void *data);
//不会初始化用来将work_struct结构连接到工作 队列的指针,用于任务已经提交,只是修改了任务时使用PREPARE_WORK
PREPARE_WORK(struct work_struct *work,void (*function)(void *),void *data)
提交任务:
int queue_work(struct workqueue_struct *queue,struct work_struct *work);
//实际的工作至少会在经过指定的jiffies(由delay指定)之后才会被执行
int queue_delayed_work(struct workqueue_struct *queue,struct work_struct *work,unsigned long delay);
取消某个队列的入口项:
int cancel_delayed_work(struct work_struct *work);
!=0;内核会确保不会初始化给定入口项的执行
==0;则说明该入口项已经在其他处理器上运行,因此在cancel_delayed_work返回后可能仍在运行。
为了,更新,所有要强制刷新工作队列
void flush_workqueue(struct workqueue_struct *queue);
销毁工作队列
void destroy_workqueue(struct workqueue_struct *queue);
共享工作队列:
1.在许多情况下,驱动不需要有自己的工作队列,只是偶尔向工作队列添加任务
2.使用内核提供的共享的默认工作队列
3。不应该长期独占该队列,即不能长时间休眠
4.任务可能需要更长时间延迟才能获得处理器时间
//初始化
INIT_WORK(struct work_struct *work,void(*function)(void *),void *data);
//调度
INT schedule_work(struct work_struct *work);
int schedule_delayed_work(struct work_struct *work,unsigned long delay);
//取消共享工作队列的一个入口项(一个工作任务 work)
int cancel_delayed_work(struct work_struct *work);
//刷新共享工作队列
void flush_scheduled_work(void);
中断共享:
linux中断共享:多个设备使用同一个中短线号,同一个中断设备线号的所有处理程序连接成一个链表。
共享中断的多个设备在申请 中断时都应该使用SA_SHIRQ标志
设备结构指针可以作为request_irq(...,void *dev_id)的最后一个参数dev_id传入,dev_id这个参数必须是唯一的,用来表示一个唯一的设备。
在中断到来时,对应链表的所有共享该中断的处理程序都会被执行,它们会测dev_id参数信息,并根据硬件中断寄存器中的信息判断是否是本设备的中断,如果不是,应迅速返回,如果是,则处理完成,如果链表中没有一个是,则说明出现错误。