中断处理

中断类型

根据中断的来源分为外部中断内部中断,外部中断来源于CPU外部,由外设发出,内部中断就是CPU内部触发(软件中断指令、溢出、除法错误等)。根据中断能否被屏蔽分为可屏蔽中断不可屏蔽中断,可屏蔽中断就是可以被屏蔽,被屏蔽之后中断不再响应。不可屏蔽中断就是不能被屏蔽。

中断的申请/释放函数

申请irq函数

int request_irq(unsigned int irq, irqreturn_t (*handler)(int, void *, struct pt_regs *), unsigned long flags, const char *dev_name, void *dev_id);
/* 
   irq:中断号
   irqreturn_t (*handler):中断处理函数
   flags:中断管理位掩码
   dev_name:请求中断的设备的名称,显示在/proc/interrupts
   dev_id:共享中断时用,一般设置为这个设备的设备结构体或者NULL 
*/

参数flags可以为0,也可能是下列一个或多个标志的位掩码。其定义在<linux/interrupt.h>。

#define IRQF_TRIGGER_NONE	0x00000000
#define IRQF_TRIGGER_RISING	0x00000001 //上升沿触发
#define IRQF_TRIGGER_FALLING	0x00000002 //下降沿触发
#define IRQF_TRIGGER_HIGH	0x00000004 //高电平触发
#define IRQF_TRIGGER_LOW	0x00000008 //低电平触发
#define IRQF_TRIGGER_MASK	(IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)
#define IRQF_TRIGGER_PROBE	0x00000010

#define IRQF_SHARED		0x00000080 //允许在多个设备之间共享IRQ
#define IRQF_PROBE_SHARED	0x00000100 //由呼叫者在期望发生共享不匹配时设置
#define __IRQF_TIMER		0x00000200
#define IRQF_PERCPU		0x00000400
#define IRQF_NOBALANCING	0x00000800
#define IRQF_IRQPOLL		0x00001000
#define IRQF_ONESHOT		0x00002000 //Hardirq处理程序完成后,中断不会重新启用。由需要在线程处理程序运行之前保持IRQ行禁用的线程中断使用
#define IRQF_NO_SUSPEND		0x00004000 //在挂起期间不要禁用此IRQ。不保证此中断将使系统从暂停状态唤醒。
#define IRQF_FORCE_RESUME	0x00008000
#define IRQF_NO_THREAD		0x00010000 //中断不能被线程化
#define IRQF_EARLY_RESUME	0x00020000
#define IRQF_COND_SUSPEND	0x00040000
#define IRQF_TIMER		(__IRQF_TIMER | IRQF_NO_SUSPEND | IRQF_NO_THREAD) //将此中断标记为计时器中断的标志

释放irq函数

void free_irq(unsigned int irq, void *dev_id);

中断处理前/后半部

因为中断可能随时发生,因此中断处理程序也就随时可能执行,所以必须保证中断处理程序能够快速执行,这样才能保证尽可能快的恢复中断你代码的执行。又想中断处理程序运行的快,又想中断处理程序完成的工作量多,为了满足这两个要求,一般将中断处理程序分为两个部分。
中断上半部接收到一个中断,立即执行,只做严格的限时工作,其他耗时操作留到下半部执行。实现下半部可以使用tasklet或者工作队列。

中断线程化

在 Linux 中,中断具有最高的优先级。不论在任何时刻,只要产生中断事件,内核将立即执行相应的中断处理程序,等到所有挂起的中断和软中断处理完毕后才能执行正常的任务,因此有可能造成实时任务得不到及时的处理。中断线程化之后,中断将作为内核线程运行而且被赋予不同的实时优先级,实时任务可以有比中断线程更高的优先级。这样,具有最高优先级的实时任务就能得到优先处理,即使在严重负载下仍有实时性保证。但是,并不是所有的中断都可以被线程化,比如时钟中断,主要用来维护系统时间以及定时器等,其中定时器是操作系统的脉搏,一旦被线程化,就有可能被挂起,这样后果将不堪设想,所以不应当被线程化。
中断线程化使用的函数如下:

request_threaded_irq(unsigned int irq, irq_handler_t handler,
		     irq_handler_t thread_fn,
		     unsigned long flags, const char *name, void *dev);

与request_irq函数的区别就是差一个thread_fn,此处如果传递NULL,表示没有中断线程化。事实上在linux/include/interrupt.h中看request_irq的实现如下:

static inline int __must_check
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
	    const char *name, void *dev)
{
	return request_threaded_irq(irq, handler, NULL, flags, name, dev);
}

request_irq函数就是将request_threaded_irq的thread_fn设置为NULL。

使能和禁止中断

为何要禁止中断?
因为中断的高优先级,一旦触发 了中断,系统就会进入中断处理函数。理想状态下,中断触发的时候,前一次的中断处理函数已经执行完成。如果前面一个中断处理函数没有处理完成,那么就会导致中断嵌套。为了避免出现这种情况,需要在处理中断的时候禁止中断,处理完成之后再使能中断。

什么叫中断嵌套?
是指中断系统正在执行一个中断服务时,有另一个优先级更高的中断提出中断请求,这时会暂时终止当前正在执行的级别较低的中断源的服务程序,去处理级别更高的中断源,待处理完毕,再返回到被中断了的中断服务程序继续执行,这个过程就是中断嵌套。

禁止所有中断
用于禁止当前处理器(仅仅是当前处理器)上的本地中断,随后又激活它们的函数如下,定义在<asm/system.h>

void local_irq_save(unsigned long flags); /* 保存中断状态到flags,flags是值传递,不是通过指针 */
void local_irq_disable(void); /* 关闭本地中断递交而不保存状态 */

void local_irq_restore(unsigned long flags); /* 恢复由local_irq_save存储于flags的中断状态 */
void local_irq_enable(void); /* 无条件的打开中断,不管之前的中断状态如何 */

禁止指定的中断
某些情况下,只需要禁止一条中断线就够了。使用下面的函数:

void disable_irq(unsigned int irq); /* 禁止给定的中断, 还等待一个当前执行的中断处理结束 */
void disable_irq_nosync(unsigned int irq); /* 立刻返回,但是可能使你的设备有竞争情况 */
void enable_irq(unsigned int irq); /* 使能中断 */
int enable_irq_wake(unsigned int irq); /* 使能中断,不仅在系统正常运行状态下产生的IRQ会上报,同时在系统休眠的情况下产生的IRQ会上报 */

disable_irq不能放在中断处理函数中,系统可能会出现死锁状态,disable_irq_nosync可以在中断处理函数中使用。在中断处理函数完成所有的工作之后,要主动调用enable_irq,否则该中断将一直处于禁止状态。

示例

后续补上

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值