linux内核中断

版权声明:本文为博主原创文章,未经博主允许不得转载。
https://blog.csdn.net/huangweiqing80/article/details/83065404
当一种异常发生的时候,ARM处理器会跳转到对应该异常的固定地址去执行异常处理程序,而这个固定的地址就称为异常向量

在这里插入图片描述

在这里插入图片描述

在裸机代码中,当irq发生时,程序会跳转到0x00000018地址处去,
即ldr pc,_irq.这张异常向量表在linux内核中也是一样的。当irq发生就会调转到0x00000018地址
统一入口
entry-armv.S

	.align	5
__irq_svc:
	svc_entry

#ifdef CONFIG_TRACE_IRQFLAGS
	bl	trace_hardirqs_off
#endif
#ifdef CONFIG_PREEMPT
	get_thread_info tsk
	ldr	r8, [tsk, #TI_PREEMPT]		@ get preempt count
	add	r7, r8, #1			@ increment it
	str	r7, [tsk, #TI_PREEMPT]
#endif

	irq_handler
#ifdef CONFIG_PREEMPT
	str	r8, [tsk, #TI_PREEMPT]		@ restore preempt count
	ldr	r0, [tsk, #TI_FLAGS]		@ get flags
	teq	r8, #0				@ if preempt count != 0
	movne	r0, #0				@ force flags to 0
	tst	r0, #_TIF_NEED_RESCHED
	blne	svc_preempt
#endif
	ldr	r4, [sp, #S_PSR]		@ irqs are already disabled
#ifdef CONFIG_TRACE_IRQFLAGS
	tst	r4, #PSR_I_BIT
	bleq	trace_hardirqs_on
#endif
	svc_exit r4				@ return from exception
 UNWIND(.fnend		)
ENDPROC(__irq_svc)
/*
 * Interrupt handling.  Preserves r7, r8, r9
 */
	.macro	irq_handler
#ifdef CONFIG_MULTI_IRQ_HANDLER
	ldr	r5, =handle_arch_irq
	mov	r0, sp
	ldr	r5, [r5]
	adr	lr, BSYM(9997f)
	teq	r5, #0
	movne	pc, r5
#endif
	arch_irq_handler_default
9997:
	.endm

entry-macro-multi.S

/*
 * Interrupt handling.  Preserves r7, r8, r9
 */
	.macro	arch_irq_handler_default
	get_irqnr_preamble r5, lr
1:	get_irqnr_and_base r0, r6, r5, lr
	movne	r1, sp
	@
	@ routine called with r0 = irq number, r1 = struct pt_regs *
	@
	adrne	lr, BSYM(1b)
	bne	asm_do_IRQ

得到中断号:get_irqnr_and_base

	.macro	get_irqnr_and_base, irqnr, irqstat, base, tmp

		mov	\base, #S3C24XX_VA_IRQ

		@@ try the interrupt offset register, since it is there

		ldr	\irqstat, [ \base, #INTPND ]
		teq	\irqstat, #0
		beq	1002f
		ldr	\irqnr, [ \base, #INTOFFSET ]
		mov	\tmp, #1
		tst	\irqstat, \tmp, lsl \irqnr
		bne	1001f

		@@ the number specified is not a valid irq, so try
		@@ and work it out for ourselves

		mov	\irqnr, #0		@@ start here

		@@ work out which irq (if any) we got

		movs	\tmp, \irqstat, lsl#16
		addeq	\irqnr, \irqnr, #16
		moveq	\irqstat, \irqstat, lsr#16
		tst	\irqstat, #0xff
		addeq	\irqnr, \irqnr, #8
		moveq	\irqstat, \irqstat, lsr#8
		tst	\irqstat, #0xf
		addeq	\irqnr, \irqnr, #4
		moveq	\irqstat, \irqstat, lsr#4
		tst	\irqstat, #0x3
		addeq	\irqnr, \irqnr, #2
		moveq	\irqstat, \irqstat, lsr#2
		tst	\irqstat, #0x1
		addeq	\irqnr, \irqnr, #1

		@@ we have the value

asm_do_IRQ

asmlinkage void __exception_irq_entry
asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
{
	struct pt_regs *old_regs = set_irq_regs(regs);

	irq_enter();

	/*
	 * Some hardware gives randomly wrong interrupts.  Rather
	 * than crashing, do something sensible.
	 */
	if (unlikely(irq >= nr_irqs)) {
		if (printk_ratelimit())
			printk(KERN_WARNING "Bad IRQ%u\n", irq);
		ack_bad_irq(irq);
	} else {
		generic_handle_irq(irq);
	}

	/* AT91 specific workaround */
	irq_finish(irq);

	irq_exit();
	set_irq_regs(old_regs);
}

根据中断号irq得到irq_desc结构,并从irq_desc找到注册好的中断处理函数handle_irq

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

驱动:
实现中断处理函数
注册中断

#include <linux/module.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
#include <linux/io.h>

#define GPFCON 0x56000050

irqreturn_t key_int(int irq, void *dev_id)
{
    //1. 检测是否发生了按键中断
    
    
    //2. 清除已经发生的按键中断
    
    
    //3. 打印按键值
    printk("key down!\n");
    
    return 0;
    	
}

void key_hw_init()
{
    unsigned short data; 
    unsigned int *gpio_config;
    
    gpio_config = ioremap(GPFCON,4);
    data = readw(gpio_config);
    data &= ~0b11;
    data |= 0b10;
    writew(data,gpio_config);
}


int key_open(struct inode *node,struct file *filp)
{
    return 0;	
}

struct file_operations key_fops = 
{
    .open = key_open,	
};

struct miscdevice key_miscdev = {
    .minor = 200,
    .name = "tq2440key",
    .fops = &key_fops,	
};

static int button_init()
{
    misc_register(&key_miscdev);
    
    //按键硬件初始化
    key_hw_init();
    
    //注册中断处理程序
    request_irq(IRQ_EINT0,key_int,IRQF_TRIGGER_FALLING,"tq2440key",0);
    
    return 0;
}


static void button_exit()
{
    misc_deregister(&key_miscdev);	
}


module_init(button_init);
module_exit(button_exit);



那么现在又有一个问题,当我们发生中断嵌套时,比如在慢速中断中,当我们在一个串口中断处理函数中执行时,突然又发生了一个串口中断,那么发生的这个串口中断就会被忽略,又或者在一个快速中断中,当我们在处理着一个串口中断函数时,突然来了一个其他的中断,那么这时发生的这个中断也会被忽略。那么有什么办法解决这些冲突吗?
我们想,我们让前面发生的中断早点结束,也就说中断处理函数执行的时间短一点,这样就能使中断早点结束了,我们想到的办法就是,让中断分层去处理,让与硬件相关的处理我们才把他放到中断处理程序中,我们把这部分叫做上半部;其他的处理我们就把它放到其他的地方进行处理,我们把这部分叫做下半部。

下面我们来开始讲解中断下半部,如果用一个词来形容下半部的功能,就是“延迟执行”,为什么要这样说呢,后面分析过后就会深刻理解这一点了。在中断的上半部,即中断处理程序结束前,当前的中断号在所有的处理器上都会被屏蔽,如果在申请中断号时使用了IRQF_DISABLED,那么情况会更加糟糕,在中断处理程序执行时会禁止所有的本地中断。因此尽可能地缩短中断被屏蔽的时间对系统的响应能力和性能都至关重要。因此,要将耗时较长的任务放到下半部延迟执行。因为下半部并不禁止其他中断上半部的执行(哪怕是自己的中断处理函数)。对于中断底半部的实现方式一共有三种;

  1. 采用软中断的方式
    Linux 之软中断softirq
  2. 采用tasklet微线程
    Linux内核之tasklet
  3. 采用工作队列
    linux内核之工作队列
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值