中断处理函数_中断处理函数里面可以加锁吗 - CSDN
精华内容
参与话题
  • 中断函数和中断处理函数

    千次阅读 2014-10-22 13:52:40
    (1)当你在做一个计算机(嵌入式)系统时,在为系统做初始化时往往会有设置中断向量的操作。 当你设置好某个特定的外部事件(比如定时器超时)的中断向量后,当你允许(使能)了该设备(定时器),那么等到特定...
    (1)当你在做一个计算机(嵌入式)系统时,在为系统做初始化时往往会有设置中断向量的操作。
    当你设置好某个特定的外部事件(比如定时器超时)的中断向量后,当你允许(使能)了该设备(定时器),那么等到特定时刻(定时器超时),
    外设(定时器)会向你CPU核心发送外部中断请求,如果此时没有对它进行任何屏蔽的话,并且也没有比它优先级更高的中断事件处于未决状态的话,
    那么此时该事件的中断发生。
      为了能够对该事件处理,必须要有一个处理该中断事件的函数入口地址,称该地址为中断向量
      因此中断向量位置一般是由CPU(硬件)来决定的,而里面对于特定中断事件的处理可以由开发人员进行自由地软件编程。
    但是,在处理完该中断事件后,也就是要返回该中断向量函数,必须要使用特殊的中断返回指令(在X86系统中为IRET;在Blackfin指令集中为rti)。

    问题1:为什么说中断向量函数不需要有返回值?
    回答: 因为中断事件是异步发生的。当系统在接受一个中断事件,对它进行处理,完了之后,如果有返回值,那么由谁来处理这个返回值呢?
         况且一个中断可能会发生在不同的线程中,或者在上下文切换之际发生(这种情况可能会比较少,有些系统在做上下文切换时,往往会屏蔽所有可屏蔽中断。但是上下文切换也往往存在临界去【Critical Region】和非临界区)。因此对于中断向量函数的返回值是无法进行处理的。而如果说要以调用普通函数的方式为它传递参数就更不可能了。你只能为中断向量函数提供某种区域(其所能访问的)的相对全局变量,对该变量设定值,然后在该中断向量函数中进行处理;或者是通过写某个相对全局变量的值,以此作为返回。
    比如:
    C/C++ code
    volatile int argument = 0, retValue = 0; // the key word "volatile" here is necessary // The following pragma is pseudo, that only describes the effect of the interrupt vector function#pragmainterrupt_vector(TIMER) // @0x00001000 the address of the vector for timer eventvoidtimer_vector_handler(void) { save_context(); // Save the context(almost all the general registers)retValue = toggle_led(LED1, &argument); // According to argument, If argument is 0, turn it off; Otherwise, turn it on. // Then, the value of argument is toggled. // retValue is a return vbalue that indicates whether the operation is successful or not. reset_timer(); // clear the timer interrupt status and reset the timer. restore_context(); // resotre the context saved before //iret(); // Because the function is predeclared as #pragma interrupt_vector, // the compiler may automatically generate "iret" instruction at the end of the fucntion}
    
    



    问题2:那么一些带有参数或返回值的中断处理函数又是什么呢?
    回答: 这些中断处理函数一般是基于操作系统下的用户自定义处理函数。比如说,在Windows系统下,应用程序编写者可以通过使用一个定时器来处理很多事情。
    但是应用程序用户别指望直接使用内核提供的中断向量函数对定时器进行操作,相反,操作系统对于定时器有个定时器事件的管理。
    比如说定时器1可以观察64个状态值,一旦某个值处于超时状态,就会调用其相应的由应用程序编写者先前所注册的中断处理函数进行回调。而这些用户自定义函数可以有返回值或带有参数。
    比如,用于进行Windows桌面程序编程的MFC库中的SetTimer函数:
    C/C++ code
    UINT SetTimer( UINT nIDEvent, UINT nElapse, void ( CALLBACK* lpfnTimer )(HWND, UINT, UINT, DWORD)= NULL ) throw();
    
    


    上面第三个参数就是指向用户定时中断处理函数的指针,并且带有4个参数。

     

    from:http://topic.csdn.net/u/20080704/10/9f105765-5cd1-4def-badc-3d067f0704e3.html

    展开全文
  • 中断处理函数的注意事项

    千次阅读 2017-06-12 18:58:30
    中断处理函数的注意事项

    中断又叫异步中断,由硬件触发。而异常又称为同步中断,由软件触发。
    中断服务程序(中断处理函数)是一种处理中断响应的函数,它是一种遵循特定原型声明的C函数,它运行在中断上下文中,也称为原子上下文,代码运行在此上下文中是不能被阻塞的。中断服务程序必须运行非常快,它最基本的工作就是告诉硬件已经收到了它发出的中断,但通常还执行大量其他的工作。为此,一般中断服务程序分为两半,一半是中数据恢复处理函数,称为上半部,它只执行那些可以很快执行的代码,如向硬件确认已经收到中断号等,其他的工作要延迟到下半部去执行。
    执行在中断上下文中的代码需要注意的一些事项:

    中断上下文中的代码不能进入休眠。
    不能使用mutex,只能使用自旋锁,且仅当必须时。
    中断处理函数不能直接与用户空间进行数据交换。
    中断处理程序应该尽快结束。
    中断处理程序不需要是可重入的,因为相同的中断处理函数不能同时在多个处理器上运行。

    中断处理程序可能被一个优先级更高的中断处理程序所中断。为了避免这种情况,可以要求内核将中断处理程序标记为一个快速中断处理程序(将本地CPU上的所有中断禁用),不过在采取这个动作前要慎重考虑对系统的影响。


    今天在调试STM32的时候在中断触发的函数中添加了业务处理,导致主函数异常,单步调试发现是在中断处理时耗时过多, 修改方案:

    在中断函数中仅赋值 flag,业务处理在主函数中对flag判断进而再做不同的处理。
    

    修改后BUG消除, debug over !

    // 2017. 6 . 12 pm

    展开全文
  • 回调函数的原理是使用函数指针实现类似“软中断”的概念。 比如在上层的两个函数A和B,把自己的函数指针传给了C,C通过调用A和B的函数指针达到“当做了什么,通知上层来调用A或者B”的目的。 从更...

    1,回调函数。
    回调函数的原理是使用函数指针实现类似“软中断”的概念。
    比如在上层的两个函数A和B,把自己的函数指针传给了C,C通过调用A和B的函数指针达到“当做了什么,通知上层来调用A或者B”的目的。
    从更底层的角度上,代码之间都是在一段程序里面或者可以理解为一致代码段的跳转。通过标准的call ret就可以实现的。

         A
                                     C
         B
    |--upper layer--|--lower layer--|

    2,中断处理函数。
    首先,要了解CPU的底层处理机制。CPU对中断,错误的处理有三种:
    1,错误(fault),这种处理方式会跳到错误的处理程序中,当从错误的处理程序返回,会重新执行当前的指令(再执行一遍出错的那条指令)
    2,陷阱(trap),也会跳到陷阱的处理函数中,当从陷阱函数中返回,执行下一条指令。
    3,异常(abort),异常终止当前程序。

    其实对于中断,类似于trap的过程。表面看来,他和回调函数都是一样的概念,都是,
    发生中断--->跳到中断处理函数里面--->回到中断点下一条--->清中断,

    但是中断与回调的区别和联系是,
    1,中断可能实现不同优先级代码的跳转。比如我发生了软中断,比如接到一个信号,我就要跳到信号处理函数里面执行,但是本身实现信号处理函数跳转的,是一个内核级的代码段。
    2,有些中断,是通过回调实现的,比如windows的视频采集,就是一个帧中断,但是你注册给上层的是一个回调,
    3,关于中断的可重入,这个和回调不同,具体这又是一个话题了。
    4,应用场合不同。

    展开全文
  • Linux 中断之中断处理浅析

    千次阅读 2019-01-17 14:15:09
    1. 中断的概念 中断是指在CPU正常运行期间,由于内外部事件或由程序预先安排的事件引起的 CPU 暂时停止正在运行的程序,转而为该内部或外部事件或预先安排的事件服务的程序中去,服务完毕后再返回去继续运行被暂时...

    1. 中断的概念

    中断是指在CPU正常运行期间,由于内外部事件或由程序预先安排的事件引起的 CPU 暂时停止正在运行的程序,转而为该内部或外部事件或预先安排的事件服务的程序中去,服务完毕后再返回去继续运行被暂时中断的程序。Linux中通常分为外部中断(又叫硬件中断)和内部中断(又叫异常)。

    软件对硬件进行配置后,软件期望等待硬件的某种状态(比如,收到了数据),这里有两种方式,一种是轮询(polling): CPU 不断的去读硬件状态。另一种是当硬件完成某种事件后,给 CPU 一个中断,让 CPU 停下手上的事情,去处理这个中断。很显然,中断的交互方式提高了系统的吞吐。

    当 CPU 收到一个中断 (IRQ)的时候,会去执行该中断对应的处理函数(ISR)。普通情况下,会有一个中断向量表,向量表中定义了 CPU 对应的每一个外设资源的中断处理程序的入口,当发生对应的中断的时候, CPU 直接跳转到这个入口执行程序。也就是中断上下文。(注意:中断上下文中,不可阻塞睡眠)。

     

    2. Linux 中断 top/bottom

    玩过 MCU 的人都知道,中断服务程序的设计最好是快速完成任务并退出,因为此刻系统处于被中断中。但是在 ISR 中又有一些必须完成的事情,比如:清中断标志,读/写数据,寄存器操作等。

    在 Linux 中,同样也是这个要求,希望尽快的完成 ISR。但事与愿违,有些 ISR 中任务繁重,会消耗很多时间,导致响应速度变差。Linux 中针对这种情况,将中断分为了两部分:

    1. 上半部(top half):收到一个中断,立即执行,有严格的时间限制,只做一些必要的工作,比如:应答,复位等。这些工作都是在所有中断被禁止的情况下完成的

    2. 底半部(bottom half):能够被推迟到后面完成的任务会在底半部进行。在适合的时机,下半部会被开中断执行。(具体的机制在下一篇文章分析)

     

    3. 中断处理程序

    驱动程序可以使用接口:

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

    像系统申请注册一个中断处理程序。

    其中的参数:

    参数 含义
    irq 表了该中断的中断号,一般 CPU 的中断号都会事先定义好。
    handler 中断发生后的 ISR
    flags 中断标志( IRQF_DISABLED / IRQFSAMPLE_RANDOM / IRQF_TIMER / IRQF_SHARED)
    name  中断相关的设备 ASCII 文本,例如 "keyboard",这些名字会在 /proc/irq 和 /proc/interrupts 文件使用
    dev 用于共享中断线,传递驱动程序的设备结构。非共享类型的中断,直接设置成为 NULL

     

     

     

     

     

     

     

    中断标志 flag 的含义:

    标志 含义
    IRQF_DISABLED 设置这个标志的话,意味着内核在处理这个 ISR 期间,要禁止其他中断(多数情况不使用这个)
    IRQFSAMPLE_RANDOM 表明这个设备产生的中断对内核熵池有贡献
    IRQF_TIMER 为系统定时器准备的标志
    IRQF_SHARED 表明多个中断处理程序之间共享中断线。同一个给定的线上注册每个处理程序,必须设置这个

     

     

     

     

     

     

    调用 request _irq 成功执行返回 0。常见错误是 -EBUSY,表示给定的中断线已经在使用(或者没有指定 IRQF_SHARED)

    注意:request_irq 函数可能引起睡眠,所以不允许在中断上下文或者不允许睡眠的代码中调用

     

    释放中断:

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

    用于释放中断处理函数。

    注意:Linux 中的中断处理程序是无须重入的。当给定的中断处理程序正在执行的时候,其中断线在所有的处理器上都会被屏蔽掉,以防在同一个中断线上又接收到另一个新的中断。通常情况下,除了该中断的其他中断都是打开的,也就是说其他的中断线上的重点都能够被处理,但是当前的中断线总是被禁止的,故,同一个中断处理程序是绝对不会被自己嵌套的

     

    4. 中断上下文

    与进程上下文不一样,内核执行中断服务程序的时候,处于中断上下文。中断处理程序并没有自己的独立的栈,而是使用了内核栈,其大小一般是有限制的(32bit 机器 8KB)。所以其必须短小精悍。同时中断服务程序是打断了正常的程序流程,这一点上也必须保证快速的执行。同时中断上下文中是不允许睡眠,阻塞的。

    中断上下文不能睡眠的原因是

    1、 中断处理的时候,不应该发生进程切换,因为在中断context中,唯一能打断当前中断handler的只有更高优先级的中断,它不会被进程打断,如果在 中断context中休眠,则没有办法唤醒它,因为所有的wake_up_xxx都是针对某个进程而言的,而在中断context中,没有进程的概念,没 有一个task_struct(这点对于softirq和tasklet一样),因此真的休眠了,比如调用了会导致block的例程,内核几乎肯定会死。

    2、schedule()在切换进程时,保存当前的进程上下文(CPU寄存器的值、进程的状态以及堆栈中的内容),以便以后恢复此进程运行。中断发生后,内核会先保存当前被中断的进程上下文(在调用中断处理程序后恢复);

    但在中断处理程序里,CPU寄存器的值肯定已经变化了吧(最重要的程序计数器PC、堆栈SP等),如果此时因为睡眠或阻塞操作调用了schedule(),则保存的进程上下文就不是当前的进程context了.所以不可以在中断处理程序中调用schedule()。

    3、内核中schedule()函数本身在进来的时候判断是否处于中断上下文:

    if(unlikely(in_interrupt()))

    BUG();

    因此,强行调用schedule()的结果就是内核BUG。

    4、中断handler会使用被中断的进程内核堆栈,但不会对它有任何影响,因为handler使用完后会完全清除它使用的那部分堆栈,恢复被中断前的原貌。

    5、处于中断context时候,内核是不可抢占的。因此,如果休眠,则内核一定挂起。

     

    5. 举例

    比如 RTC 驱动程序 (drivers/char/rtc.c)。在 RTC 驱动的初始化阶段,会调用到 rtc_init 函数:

    module_init(rtc_init);

    在这个初始化函数中调用到了 request_irq 用于申请中断资源,并注册服务程序:

    static int __init rtc_init(void)
    {
    ...
        rtc_int_handler_ptr = rtc_interrupt;
    ...
        request_irq(RTC_IRQ, rtc_int_handler_ptr, 0, "rtc", NULL)
    ...
    }

    RTC_IRQ 是中断号,和处理器绑定。

    rtc_interrupt 是中断处理程序:

    static irqreturn_t rtc_interrupt(int irq, void *dev_id)
    {
    	/*
    	 *	Can be an alarm interrupt, update complete interrupt,
    	 *	or a periodic interrupt. We store the status in the
    	 *	low byte and the number of interrupts received since
    	 *	the last read in the remainder of rtc_irq_data.
    	 */
    
    	spin_lock(&rtc_lock);
    	rtc_irq_data += 0x100;
    	rtc_irq_data &= ~0xff;
    	if (is_hpet_enabled()) {
    		/*
    		 * In this case it is HPET RTC interrupt handler
    		 * calling us, with the interrupt information
    		 * passed as arg1, instead of irq.
    		 */
    		rtc_irq_data |= (unsigned long)irq & 0xF0;
    	} else {
    		rtc_irq_data |= (CMOS_READ(RTC_INTR_FLAGS) & 0xF0);
    	}
    
    	if (rtc_status & RTC_TIMER_ON)
    		mod_timer(&rtc_irq_timer, jiffies + HZ/rtc_freq + 2*HZ/100);
    
    	spin_unlock(&rtc_lock);
    
    	wake_up_interruptible(&rtc_wait);
    
    	kill_fasync(&rtc_async_queue, SIGIO, POLL_IN);
    
    	return IRQ_HANDLED;
    }
    

    每次收到 RTC 中断,就会调用进这个函数。

     

    6. 中断处理流程

    发生中断时,CPU执行异常向量vector_irq的代码, 即异常向量表中的中断异常的代码,它是一个跳转指令,跳去执行真正的中断处理程序,在vector_irq里面,最终会调用中断处理的总入口函数。

    C 语言的入口为 : 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 为中断号。

    asm_do_IRQ -> handle_IRQ

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

    handle_IRQ -> __handle_domain_irq

    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;
    }

    这里请注意:

    先调用了 irq_enter 标记进入了硬件中断:

    irq_enter是更新一些系统的统计信息,同时在__irq_enter宏中禁止了进程的抢占。虽然在产生IRQ时,ARM会自动把CPSR中的I位置位,禁止新的IRQ请求,直到中断控制转到相应的流控层后才通过local_irq_enable()打开。那为何还要禁止抢占?这是因为要考虑中断嵌套的问题,一旦流控层或驱动程序主动通过local_irq_enable打开了IRQ,而此时该中断还没处理完成,新的irq请求到达,这时代码会再次进入irq_enter,在本次嵌套中断返回时,内核不希望进行抢占调度,而是要等到最外层的中断处理完成后才做出调度动作,所以才有了禁止抢占这一处理

    再调用 generic_handle_irq

    最后调用 irq_exit 删除进入硬件中断的标记

    __handle_domain_irq -> generic_handle_irq

    int generic_handle_irq(unsigned int irq)
    {
    	struct irq_desc *desc = irq_to_desc(irq);
    
    	if (!desc)
    		return -EINVAL;
    	generic_handle_irq_desc(desc);
    	return 0;
    }
    EXPORT_SYMBOL_GPL(generic_handle_irq);

    首先在函数 irq_to_desc 中根据发生中断的中断号,去取出它的 irq_desc 中断描述结构,然后调用 generic_handle_irq_desc

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

    这里调用了 handle_irq 函数。

    所以,在上述流程中,还需要分析 irq_to_desc  流程:

    struct irq_desc *irq_to_desc(unsigned int irq)
    {
    	return (irq < NR_IRQS) ? irq_desc + irq : NULL;
    }
    EXPORT_SYMBOL(irq_to_desc);

    NR_IRQS 是支持的总的中断个数,当然,irq 不能够大于这个数目。所以返回 irq_desc + 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),
    	}
    };

    这里是这个数组的初始化的地方。所有的 handle_irq 函数都被初始化成为了 handle_bad_irq。

    细心的观众可能发现了,调用这个 desc->handle_irq(desc) 函数,并不是咱们注册进去的中断处理函数啊,因为两个函数的原型定义都不一样。这个 handle_irq irq_flow_handler_t 类型,而我们注册进去的服务程序是 irq_handler_t,这两个明显不是同一个东西,所以这里我们还需要继续分析。

     

    6.1 中断相关的数据结构

    Linux 中断相关的数据结构有 3 个

    结构名称 作用
    irq_desc IRQ 的软件层面上的资源描述
    irqaction IRQ 的通用操作
    irq_chip 对应每个芯片的具体实现

     

     

     

     

     

    irq_desc 结构如下

    struct irq_desc {
    	struct irq_common_data	irq_common_data;
    	struct irq_data		irq_data;
    	unsigned int __percpu	*kstat_irqs;
    	irq_flow_handler_t	handle_irq;
    #ifdef CONFIG_IRQ_PREFLOW_FASTEOI
    	irq_preflow_handler_t	preflow_handler;
    #endif
    	struct irqaction	*action;	/* IRQ action list */
    	unsigned int		status_use_accessors;
    	unsigned int		core_internal_state__do_not_mess_with_it;
    	unsigned int		depth;		/* nested irq disables */
    	unsigned int		wake_depth;	/* nested wake enables */
    	unsigned int		irq_count;	/* For detecting broken IRQs */
    	unsigned long		last_unhandled;	/* Aging timer for unhandled count */
    	unsigned int		irqs_unhandled;
    	atomic_t		threads_handled;
    	int			threads_handled_last;
    	raw_spinlock_t		lock;
    	struct cpumask		*percpu_enabled;
    	const struct cpumask	*percpu_affinity;
    #ifdef CONFIG_SMP
    	const struct cpumask	*affinity_hint;
    	struct irq_affinity_notify *affinity_notify;
    #ifdef CONFIG_GENERIC_PENDING_IRQ
    	cpumask_var_t		pending_mask;
    #endif
    #endif
    	unsigned long		threads_oneshot;
    	atomic_t		threads_active;
    	wait_queue_head_t       wait_for_threads;
    #ifdef CONFIG_PM_SLEEP
    	unsigned int		nr_actions;
    	unsigned int		no_suspend_depth;
    	unsigned int		cond_suspend_depth;
    	unsigned int		force_resume_depth;
    #endif
    #ifdef CONFIG_PROC_FS
    	struct proc_dir_entry	*dir;
    #endif
    #ifdef CONFIG_GENERIC_IRQ_DEBUGFS
    	struct dentry		*debugfs_file;
    	const char		*dev_name;
    #endif
    #ifdef CONFIG_SPARSE_IRQ
    	struct rcu_head		rcu;
    	struct kobject		kobj;
    #endif
    	struct mutex		request_mutex;
    	int			parent_irq;
    	struct module		*owner;
    	const char		*name;
    } ____cacheline_internodealigned_in_smp;

     

    irqaction 结构如下:

    /**
     * struct irqaction - per interrupt action descriptor
     * @handler:	interrupt handler function
     * @name:	name of the device
     * @dev_id:	cookie to identify the device
     * @percpu_dev_id:	cookie to identify the device
     * @next:	pointer to the next irqaction for shared interrupts
     * @irq:	interrupt number
     * @flags:	flags (see IRQF_* above)
     * @thread_fn:	interrupt handler function for threaded interrupts
     * @thread:	thread pointer for threaded interrupts
     * @secondary:	pointer to secondary irqaction (force threading)
     * @thread_flags:	flags related to @thread
     * @thread_mask:	bitmask for keeping track of @thread activity
     * @dir:	pointer to the proc/irq/NN/name entry
     */
    struct irqaction {
    	irq_handler_t		handler;
    	void			*dev_id;
    	void __percpu		*percpu_dev_id;
    	struct irqaction	*next;
    	irq_handler_t		thread_fn;
    	struct task_struct	*thread;
    	struct irqaction	*secondary;
    	unsigned int		irq;
    	unsigned int		flags;
    	unsigned long		thread_flags;
    	unsigned long		thread_mask;
    	const char		*name;
    	struct proc_dir_entry	*dir;
    } ____cacheline_internodealigned_in_smp;
    

     

    irq_chip 描述如下:

    /**
     * struct irq_chip - hardware interrupt chip descriptor
     *
     * @parent_device:	pointer to parent device for irqchip
     * @name:		name for /proc/interrupts
     * @irq_startup:	start up the interrupt (defaults to ->enable if NULL)
     * @irq_shutdown:	shut down the interrupt (defaults to ->disable if NULL)
     * @irq_enable:		enable the interrupt (defaults to chip->unmask if NULL)
     * @irq_disable:	disable the interrupt
     * @irq_ack:		start of a new interrupt
     * @irq_mask:		mask an interrupt source
     * @irq_mask_ack:	ack and mask an interrupt source
     * @irq_unmask:		unmask an interrupt source
     * @irq_eoi:		end of interrupt
     * @irq_set_affinity:	Set the CPU affinity on SMP machines. If the force
     *			argument is true, it tells the driver to
     *			unconditionally apply the affinity setting. Sanity
     *			checks against the supplied affinity mask are not
     *			required. This is used for CPU hotplug where the
     *			target CPU is not yet set in the cpu_online_mask.
     * @irq_retrigger:	resend an IRQ to the CPU
     * @irq_set_type:	set the flow type (IRQ_TYPE_LEVEL/etc.) of an IRQ
     * @irq_set_wake:	enable/disable power-management wake-on of an IRQ
     * @irq_bus_lock:	function to lock access to slow bus (i2c) chips
     * @irq_bus_sync_unlock:function to sync and unlock slow bus (i2c) chips
     * @irq_cpu_online:	configure an interrupt source for a secondary CPU
     * @irq_cpu_offline:	un-configure an interrupt source for a secondary CPU
     * @irq_suspend:	function called from core code on suspend once per
     *			chip, when one or more interrupts are installed
     * @irq_resume:		function called from core code on resume once per chip,
     *			when one ore more interrupts are installed
     * @irq_pm_shutdown:	function called from core code on shutdown once per chip
     * @irq_calc_mask:	Optional function to set irq_data.mask for special cases
     * @irq_print_chip:	optional to print special chip info in show_interrupts
     * @irq_request_resources:	optional to request resources before calling
     *				any other callback related to this irq
     * @irq_release_resources:	optional to release resources acquired with
     *				irq_request_resources
     * @irq_compose_msi_msg:	optional to compose message content for MSI
     * @irq_write_msi_msg:	optional to write message content for MSI
     * @irq_get_irqchip_state:	return the internal state of an interrupt
     * @irq_set_irqchip_state:	set the internal state of a interrupt
     * @irq_set_vcpu_affinity:	optional to target a vCPU in a virtual machine
     * @ipi_send_single:	send a single IPI to destination cpus
     * @ipi_send_mask:	send an IPI to destination cpus in cpumask
     * @flags:		chip specific flags
     */
    struct irq_chip {
    	struct device	*parent_device;
    	const char	*name;
    	unsigned int	(*irq_startup)(struct irq_data *data);
    	void		(*irq_shutdown)(struct irq_data *data);
    	void		(*irq_enable)(struct irq_data *data);
    	void		(*irq_disable)(struct irq_data *data);
    
    	void		(*irq_ack)(struct irq_data *data);
    	void		(*irq_mask)(struct irq_data *data);
    	void		(*irq_mask_ack)(struct irq_data *data);
    	void		(*irq_unmask)(struct irq_data *data);
    	void		(*irq_eoi)(struct irq_data *data);
    
    	int		(*irq_set_affinity)(struct irq_data *data, const struct cpumask *dest, bool force);
    	int		(*irq_retrigger)(struct irq_data *data);
    	int		(*irq_set_type)(struct irq_data *data, unsigned int flow_type);
    	int		(*irq_set_wake)(struct irq_data *data, unsigned int on);
    
    	void		(*irq_bus_lock)(struct irq_data *data);
    	void		(*irq_bus_sync_unlock)(struct irq_data *data);
    
    	void		(*irq_cpu_online)(struct irq_data *data);
    	void		(*irq_cpu_offline)(struct irq_data *data);
    
    	void		(*irq_suspend)(struct irq_data *data);
    	void		(*irq_resume)(struct irq_data *data);
    	void		(*irq_pm_shutdown)(struct irq_data *data);
    
    	void		(*irq_calc_mask)(struct irq_data *data);
    
    	void		(*irq_print_chip)(struct irq_data *data, struct seq_file *p);
    	int		(*irq_request_resources)(struct irq_data *data);
    	void		(*irq_release_resources)(struct irq_data *data);
    
    	void		(*irq_compose_msi_msg)(struct irq_data *data, struct msi_msg *msg);
    	void		(*irq_write_msi_msg)(struct irq_data *data, struct msi_msg *msg);
    
    	int		(*irq_get_irqchip_state)(struct irq_data *data, enum irqchip_irq_state which, bool *state);
    	int		(*irq_set_irqchip_state)(struct irq_data *data, enum irqchip_irq_state which, bool state);
    
    	int		(*irq_set_vcpu_affinity)(struct irq_data *data, void *vcpu_info);
    
    	void		(*ipi_send_single)(struct irq_data *data, unsigned int cpu);
    	void		(*ipi_send_mask)(struct irq_data *data, const struct cpumask *dest);
    
    	unsigned long	flags;
    };

    irq_chip 是一串和芯片相关的函数指针,这里定义的非常的全面,基本上和 IRQ 相关的可能出现的操作都全部定义进去了,具体根据不同的芯片,需要在不同的芯片的地方去初始化这个结构,然后这个结构会嵌入到通用的 IRQ 处理软件中去使用,使得软件处理逻辑和芯片逻辑完全的分开。

    好,我们接下来继续前进。

     

    6.2 初始化 Chip 相关的 IRQ

    众所周知,启动的时候,C 语言从 start_kernel 开始,在这里面,调用了和 machine 相关的 IRQ 的初始化 init_IRQ()

    asmlinkage __visible void __init start_kernel(void)
    {
    	char *command_line;
    	char *after_dashes;
    
    .....
    
        early_irq_init();
        init_IRQ();
    
    .....
    
    }

    在 init_IRQ 中,调用了 machine_desc->init_irq()

    void __init init_IRQ(void)
    {
    	int ret;
    
    	if (IS_ENABLED(CONFIG_OF) && !machine_desc->init_irq)
    		irqchip_init();
    	else
    		machine_desc->init_irq();
    
    	if (IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_CACHE_L2X0) &&
    	    (machine_desc->l2c_aux_mask || machine_desc->l2c_aux_val)) {
    		if (!outer_cache.write_sec)
    			outer_cache.write_sec = machine_desc->l2c_write_sec;
    		ret = l2x0_of_init(machine_desc->l2c_aux_val,
    				   machine_desc->l2c_aux_mask);
    		if (ret && ret != -ENODEV)
    			pr_err("L2C: failed to init: %d\n", ret);
    	}
    
    	uniphier_cache_init();
    }
    

    machine_desc->init_irq() 完成对中断控制器的初始化,为每个irq_desc结构安装合适的流控handler,为每个irq_desc结构安装irq_chip指针,使他指向正确的中断控制器所对应的irq_chip结构的实例,同时,如果该平台中的中断线有多路复用(多个中断公用一个irq中断线)的情况,还应该初始化irq_desc中相应的字段和标志,以便实现中断控制器的级联。

    这里初始化的时候回调用到具体的芯片相关的中断初始化的地方。

    例如:

    int __init s5p_init_irq_eint(void)
    {
    	int irq;
     
    	for (irq = IRQ_EINT(0); irq <= IRQ_EINT(15); irq++)
    		irq_set_chip(irq, &s5p_irq_vic_eint);
     
    	for (irq = IRQ_EINT(16); irq <= IRQ_EINT(31); irq++) {
    		irq_set_chip_and_handler(irq, &s5p_irq_eint, handle_level_irq);
    		set_irq_flags(irq, IRQF_VALID);
    	}
     
    	irq_set_chained_handler(IRQ_EINT16_31, s5p_irq_demux_eint16_31);
    	return 0;
    }

    而在这些里面,都回去调用类似于:

    void
    irq_set_chip_and_handler_name(unsigned int irq, struct irq_chip *chip,
    			      irq_flow_handler_t handle, const char *name);
    
    irq_set_handler(unsigned int irq, irq_flow_handler_t handle)
    {
    	__irq_set_handler(irq, handle, 0, NULL);
    }
    
    static inline void
    irq_set_chained_handler(unsigned int irq, irq_flow_handler_t handle)
    {
    	__irq_set_handler(irq, handle, 1, NULL);
    }
    
    void
    irq_set_chained_handler_and_data(unsigned int irq, irq_flow_handler_t handle,
    				 void *data);
    
    

    这些函数定义在 include/linux/irq.h 文件。是对芯片初始化的时候可见的 APIs,用于指定中断“流控”中的 :

    irq_flow_handler_t handle

    也就是中断来的时候,最后那个函数调用。

    中断流控函数,分几种,电平触发的中断,边沿触发的,等:

    /*
     * Built-in IRQ handlers for various IRQ types,
     * callable via desc->handle_irq()
     */
    extern void handle_level_irq(struct irq_desc *desc);
    extern void handle_fasteoi_irq(struct irq_desc *desc);
    extern void handle_edge_irq(struct irq_desc *desc);
    extern void handle_edge_eoi_irq(struct irq_desc *desc);
    extern void handle_simple_irq(struct irq_desc *desc);
    extern void handle_untracked_irq(struct irq_desc *desc);
    extern void handle_percpu_irq(struct irq_desc *desc);
    extern void handle_percpu_devid_irq(struct irq_desc *desc);
    extern void handle_bad_irq(struct irq_desc *desc);
    extern void handle_nested_irq(unsigned int irq);
    

    而在这些处理函数里,会去调用到 : handle_irq_event 

    比如:

    /**
     *	handle_level_irq - Level type irq handler
     *	@desc:	the interrupt description structure for this irq
     *
     *	Level type interrupts are active as long as the hardware line has
     *	the active level. This may require to mask the interrupt and unmask
     *	it after the associated handler has acknowledged the device, so the
     *	interrupt line is back to inactive.
     */
    void handle_level_irq(struct irq_desc *desc)
    {
    	raw_spin_lock(&desc->lock);
    	mask_ack_irq(desc);
    
    	if (!irq_may_run(desc))
    		goto out_unlock;
    
    	desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
    
    	/*
    	 * If its disabled or no action available
    	 * keep it masked and get out of here
    	 */
    	if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) {
    		desc->istate |= IRQS_PENDING;
    		goto out_unlock;
    	}
    
    	kstat_incr_irqs_this_cpu(desc);
    	handle_irq_event(desc);
    
    	cond_unmask_irq(desc);
    
    out_unlock:
    	raw_spin_unlock(&desc->lock);
    }
    

    而这个 handle_irq_event 则是调用了处理,handle_irq_event_percpu

    irqreturn_t handle_irq_event(struct irq_desc *desc)
    {
    	irqreturn_t ret;
    
    	desc->istate &= ~IRQS_PENDING;
    	irqd_set(&desc->irq_data, IRQD_IRQ_INPROGRESS);
    	raw_spin_unlock(&desc->lock);
    
    	ret = handle_irq_event_percpu(desc);
    
    	raw_spin_lock(&desc->lock);
    	irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS);
    	return ret;
    }
    

    handle_irq_event_percpu->__handle_irq_event_percpu-> 【action->handler()

    这里终于看到了调用 的地方了,就是咱们通过 request_irq 注册进去的函数


     

    7. /proc/interrupts

    这个 proc 下放置了对应中断号的中断次数和对应的 dev-name

     

    展开全文
  • 中断服务函数由硬件触发,因此不能获得参数,也无法返回值;另一方面,在中断服务函数中使用不可重入的函数,往往会导致问题。
  • 中断处理函数

    千次阅读 2013-08-18 18:12:58
    以下是一个统计中断时间间隔的中断服务程序: irqreturn_t short_interrupt(int irq, void *dev_id, struct pt_regs *regs) { static long mytime=0; static int i=0; struct net_device *dev=(struct net_device *)...
  • 回调函数和中断处理函数

    万次阅读 2012-10-10 13:59:37
    ==================================================================================== ...回调函数的原理是使用函数指针实现类似“软中断”的概念。 比如在上层的两个函数A和B,把自己的函数指针传给了C
  • 概述   从本质上来讲,中断是一种电信号,当设备有某种事件发生时,它就会...跳到中断处理程序的入口点,进行中断处理。 (1) 硬中断 由与系统相连的外设(比如网卡、硬盘)自动产生的。主要是用来通知操作系统系统
  • 因为中断可能发生在执行任务中,因为任务可能是在“malloc”的函数调用中,如果ISR调用此相同的不可重入函数,由此产生的行为可能是灾难性的。 不可重入函数是指这样的一类函数,不可以在它还没有返回就再次被...
  • 处理器于是立即停止自己正在做的事,跳到中断处理程序的入口点,进行中断处理。(1) 硬中断 由与系统相连的外设(比如网卡、硬盘)自动产生的。主要是用来通知操作系统系统外设状态的变化。比如当网卡收到数据包的时候...
  • 51单片机之中断的实现过程

    万次阅读 多人点赞 2017-05-08 16:39:16
    中断是指cpu在执行某一过程中由于外界原因必须暂停现在的事情,处理别的事情,处理完了再回去执行暂停的事情。 中断的优点? 1.分时操作。 2.实时响应 3.可靠性高 中断中用到的寄存器: IE寄存器: 1.单路开关:EX...
  • STM32是如何进入中断函数

    万次阅读 2018-07-19 15:49:02
    一开始接触到的一般都是软中断,软中断就是中断程序包含在主程序里面,当中断条件满足时,直接跳转到中断函数执行,然后再返回。就相当于判断语句。   刚开始接触STM32的小伙伴可能会发现main...
  • 今天在看stm32的中断,一时间不理解stm32主函数是如何进入中断函数的,按C编程的理解,会有个特定的入口之类的,但是看demo过程中没有发现入口。 以串口中断服务函数void USART1_IRQHandler(void) 为例,首先...
  • 中断服务函数的写法

    千次阅读 2016-12-05 09:54:30
    编写中断服务函数时,经常使用到2个函数: 第一个函数是判断某个中断是否发生 另一个是清除某个中断标志位。 在这里需要注意的一点就是:中断事件发生以后,CPU的主权交给了中断控制器,进入中断执行中断服务函数,...
  • 程序, 中断, 调用, 函数 在《微机原理》和《计算机组成》等课程[1-4]教学中(本文以MCS-51单片机为例),中断过程既是教学难点又是教学重点,它与主程序调用子程序过程有一定相似性,但又有很大区别...
  • msp430学习笔记之中断处理函数

    千次阅读 2015-07-10 09:27:59
    //本平台 msp430f149 #include void select_xt2(void){ unsigned char i; /*------选择系统主时钟为8MHz-------*/ BCSCTL1 &= ~XT2OFF; //打开XT2高频晶体振荡噿 do{ IFG1 &= ~OFIFG;
  • Arduino常用函数总汇——中断函数

    千次阅读 2018-11-06 22:33:07
    1、Interrupt():打开总中断 2、noInterrupts():关闭总中断 3、attachInterrupt(interruptnum,function,mode):... function:中断处理函数的名字  mode:中断触发模式(LOW:低电平触发;CHANGE:...
  • 1,中断处理程序中不能使用有睡眠功能的函数,如ioremap,kmalloc,msleep等,理由是中断程序并不是进程,没有进程的概念,...2,中断处理程序中的延时可以用忙等待函数来代替,如ndelay,udelay,mdelay等,这些函数
  • stm32的中断号根据不同内核和型号,ST公司给的官方库中对...STM32会内部会根据配置的中断号在启动文件中寻找相应的中断函数的相应的入口函数例如在配置串口的中断接收打赢函数时,在NVIC的配置中配置串口1的中断函数
  • 驱动程序 基本知识

    万次阅读 2014-10-04 22:21:16
    什么是驱动程序 驱动程序就是使硬件工作的一种特殊软件,它运行在操作系统...2.应用程序可使用标准的库函数,而驱动程序连接到内核,只能使用内核导出的符号和函数。 3.应用程序错误,一般只会杀死进程,而驱动程序错误
1 2 3 4 5 ... 20
收藏数 251,161
精华内容 100,464
关键字:

中断处理函数