Linux中断处理程序框架

设备的中断会打断内核进程的正常调度和运行,系统对更高吞吐率的追求势必要求中断服务程序尽量短小精悍。但是,这个良好的愿望往往与现实不吻合。在多数真实的系统中,当中断到来时,要完成的工作往往并不是短小的,它可能要进行大量的耗时处理。为了在中断执行时间尽量短和中断处理需完成的工作尽量大之间找到一个平衡点,Linux将中断处理程序分解为两个部分:顶半部(Top Half) 和底半部(Botton Half)。

顶半部用于完成尽量少的比较紧急的功能,他往往只是简单地读取寄存器中的中断状态,并在清除中断标志后进行“登记中断”的工作。即将底半部处理程序挂到该设备的底半部执行队列中去。这样,顶半部执行的速度就会很快,从而可以服务更多的中断请求。而中断工作的中心就落在了底半部的头上,需用它来完成中断事件的绝大多数任务。底半部几乎做了中断处理程序所有的事情,而且可以被新的中断打断,这也是底半部和顶半部的最大不同,应为顶半部往往被设计成不可中断。底半部相对来说并不是非常紧急的,而且相对比较耗时,不在硬件中断服务程序中执行。也不是所有的中断处理都分为两部分,如果中断处理的工作本身很少,则完全可以直接在顶半部完成。

Linux下的中断编程

//申请中断irq
int request_irq(unsigned int irq,irq_handler_t handler,unsigned long flags,const char *name void *dev);
//irq 是要申请的硬件中断号,handler是向系统登记的中断处理函数(顶半部),是一个回调函数,中断发生是调用这个函数,
dev参数将被传递给它,flahs是中断处理的属性,可以指定中断的触发方式和处理方式。在触发方式方面,可以是 IRQF_TRIGGER_RISING、
IRQF_TRIGGER_FALLING、IRQF_TRIGGER_HIGH、IRQF_TRIGGER_LOW 等,在处理方面,若设置了IRQF_SHARED,
则表示多个设备共享中断,dev是要传递给中断服务程序的私有数据,一般设置为这个设备的设备结构体或NULL。  函数返回0 表示成功,
返回-EINVAL表示中断号无效或处理函数指针为空,返回-EBUSY表示中断已经被占用且不能共享。


int devm_request_irq(struct device *dev,unsigned int irq,irq_handler_t hamdler,unsigned long irqflags,const char *devname,void *dev_id);
//此函数与上面函数的区别是devm——开头的API 申请的是内核“managed”的资源,一般不需要在出错处理和remove()接口里显式的释放。

//顶半部handler的类型irq_hadler_t定义为
typedef irqreturn_t (*irq_handler_t)(int ,void *);
typedef int irqreturn_t;

 

//释放irq
void free_irq(unsigned int irq,void *dev_id);

//屏蔽和使能一个中断源
void disable_irq(int irq);//会阻塞,等待目前的中断完成后返回
void disable_irq_nosync(int irq);//不会阻塞,立即返回
void enable_irq(int irq);

 中断底半部机制

Linux 实现中断底半部的机制主要有tasklet、工作队列、软中断和线性化irq

tasklet     执行上下文是软中断,执行时机通常是顶半部返回的时候

void my_tasklet_func(unsigned long);//定义一个处理函数
DECLARE_TASKLET(my_tasklet,my_tasklet_func,data);//定义一个tasklet 结构体,与处理函数相关联

//在需要调度tasklet 的时候引用tasklet_shchedule() 函数就能使系统在适当的时候进行调度运行
tasklet_schedule(&my_tasklet);

工作队列  执行上下文是内核线程,因此可以调度和睡眠。

struct work_struct my)wq; //定义一个工作队列
void my_wq_func(struct work_struct *work);//定义一个处理函数


WORK_INIT(&my_wq,&my_wq_func);//初始化工作队列并将其与处理函数绑定

schedule_work(&my_wq); //调度工作队列执行

 软中断  执行时机是顶半部返回的时候,在Linux内核中,用softirq_action 结构体表征一个软中断,这个结构体包含软中断处理函数指针和传递给函数的参数。使用open_softirq() 函数可以注册软中断对应的处理函数,而raise_softirq()函数可以触发一个软中断。  

threaded_irq  在内核中,除了可以通过request_irq()、devm_request_irq() 申请中断以外,还可以使用 request_threaded_irq() 和 devm_reauest_threaded_irq() 申请。

int request_threaded_irq(unsigned int irq, irq_handler_t handler, irq_handler_t thread_fn,unsigned long flags, const char *name, void *dev); 
int devm_request_threaded_irq(struct device *dev, unsigned int irq,irq_handler_t handler, irq_handler_t thread_fn,unsigned long irqflags, const char *devname, void *dev_id);

 由此可见,它们比request_irq()、devm_request_irq() 多了一个参数thread_fn。用这两个api申请中断的时候,内核会为相应的中断号分配一个对用的内核线程。注意这个线程只针对这个中断号,如果其他中断也通过request_thread_irq() 申请,自然会得到新的内核线程。参数handler 对应的函数执行与中断上下文,thread_fn 参数对应的函数测执行与内核线程。如果handler结束的时候,返回值是IRQ_WAKE_THREAD,内核会调用对应线程执行thread_fn对应的函数。可以参考drivers/input/keyboard/gpio_keys.c 的GPIO按键驱动的代码理解中断编程。

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值