linux之中断详解

一、概论

中断分为硬件中断和软件中断,硬件中断是由于外部条件出发后导致CPU的SWI寄存器发送变化后出发的中断,软件中断是由于软件中某一个时间满足时出发的中断,所以在有限的硬件资源中,硬件中断可以说是非常宝贵的,在普通的MCU的rtos或者baremachine中,对这种中断只能一组中断使用一个IO,这样不太便以硬件的灵活性,所以在linux kernel中,引入了共享中断的方式(但是很多的自定义的RTOS项目中,程序员也借鉴了linux的方式实现了共享中断)。
共享中断: 寄存器SWI触发后,会触发软件注册的中断回调函数,我们在回调函数中依次读取属于同一组中断线上的所有IO的状态与软件上设计的是否一致,如果一致则执行这一个IO所注册的回调,这就是共享中断的实现原理。

二、中断详解

2.1、注册一个中断处理

内核中有提供了向内核注册一个中断的函数、
int request_irq(unsigned int irq,irqreturn_t (*handler)(int void *,struct pt_regs *),unsigned long flags,const char *dev_name,void *dev_id);
void free_irq(unsigned int irq, void *dev_id);
返回值:成功返回0 失败则返回一个错误码。
irq表示中断号。

flags 标志设置中断触发条件的mask。
SA_INTERRUPT、当置位了, 这表示一个"快速"中断处理. 快速处理在当前处理器上禁止中断来执行
(这个主题在"快速和慢速处理"一节涉及).
SA_SHIRQ、这个位表示中断可以在设备间共享. 共享的概念在"中断共享"一节中略述
SA_SAMPLE_RANDOM、表示真正随机的时间产生中断

irqreturn_t (*handler)(int void *,struct pt_regs *) 为注册的中断处理函数。
dev_name 注册的设备名。
dev_id 传递到回调函数中的参数。

当注册一个irq成功后,在文件系统的cat /proc/interrupts 可以得到该中断发生的次数,这对于驱动工程师而言是非常方便的。

2.2、上/下半部

中断要求快速响应,所以在中断发生时,避免进行大量运算,处理等消耗CPU时间的动作,所以linux在中断处理上进行拆分,分为上下半部
1、上半部 主要做一些简短,必要性的工作,然后唤醒等待队列(wake_up_interruptible()),唤醒任务schedule_work())等。
2、下半部主要做中断处理的一些比较繁重的计算,硬件操作等。

2.3、中断的具体实现

当SWI被触发时候,CPU的PC将会指向异常向量表(vector_irq),然后去支持响应的中断处理、
入口地址: asm_do_IRQ(unsigned int irq, struct pt_regs *regs)

asmlinkage void __exception_irq_entry
asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
{
	handle_IRQ(irq, regs);
}

irq就是中断号、

void handle_IRQ(unsigned int irq, struct pt_regs *regs)
{
	__handle_domain_irq(NULL, irq, false, regs);
}

int __handle_domain_irq(struct irq_domain *domain, unsigned int hwirq,
			bool lookup, struct pt_regs *regs)
{
	struct pt_regs *old_regs = set_irq_regs(regs);
	unsigned int irq = hwirq;
	int ret = 0;
 
	irq_enter();
 
#ifdef CONFIG_IRQ_DOMAIN
	if (lookup)
		irq = irq_find_mapping(domain, hwirq);
#endif
 
	/*
	 * Some hardware gives randomly wrong interrupts.  Rather
	 * than crashing, do something sensible.
	 */
	if (unlikely(!irq || irq >= nr_irqs)) {
		ack_bad_irq(irq);
		ret = -EINVAL;
	} else {
		generic_handle_irq(irq);
	}
 
	irq_exit();
	set_irq_regs(old_regs);
	return ret;
}

int generic_handle_irq(unsigned int irq)
{
	struct irq_desc *desc = irq_to_desc(irq);//根据终端号取出对应的irq_dest数据
 
	if (!desc)
		return -EINVAL;
	generic_handle_irq_desc(desc);
	return 0;
}
struct irq_desc *irq_to_desc(unsigned int irq)
{
	return (irq < NR_IRQS) ? irq_desc + irq : NULL;
}

/* 我们使用request_irq的时候就会将相关的信息填入到 这个irq_desc[]数组中*/

struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = {
	[0 ... NR_IRQS-1] = {
		.handle_irq	= handle_bad_irq,
		.depth		= 1,
		.lock		= __RAW_SPIN_LOCK_UNLOCKED(irq_desc->lock),
	}
};

//好了,这里就会执行我们注册进来的中断处理函数了。

static inline void generic_handle_irq_desc(unsigned int irq, struct irq_desc *desc)
{
	desc->handle_irq(irq, desc);
}

/**************** 到这里分析完毕 ***************************************/

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值