中断和中断处理(一)

(一):中断

中断本质上是一种特殊的电信号,由硬件设备发向处理器。处理器在接收到中断后,会马上向操作系统反映此信号的到来,然后就u由操作系统来处理这些新到来的数据。不同的设备对应的中断不同,而每个中断都通过一个唯一的数字标志。这些中断值被称为中断请求线(IRQ)。中断是随时随地发生的,也就是说中断并不考虑与处理器的时钟同步。

异常:异常的产生必须与处理器时钟同步,异常也被成为同步中断。在处理器执行到由于编程失误而导致的错误指令的时候,或者是在执行期间出现特殊情况,必须靠内核来处理的时候,处理器就会产生一个异常。中断是由硬件引起的,异常是由于软件引起的。

(二):中断处理程序

在响应一个特定中断的时候,内核会执行一个函数,该函数叫做中断处理程序或中断服务例程。产生中断的每个设备都有一个相应的中断处理程序。中断处理程序与其他内核函数的真正区别在于,中断处理程序是被内核调用来响应中端的,而他们运行在我们称之为中断上下文的特殊上下文中,中断上下文也被成为原子上下文,该上下文执行的代码不可阻塞。

(三):上半部与下半部的对比

由于中断处理程序既需要运行的快,又需要完成的工作量多,所以将终端处理切分为两部分。中断处理程序是上半部,一旦接收到一个中断,他就立即开始执行,但只做有严格时限的工作。能够被允许稍后执行的工作会推迟到下半部去。

(四):注册中断处理程序

中断处理程序是管理硬件的驱动程序的组成部分。每一个设备都有相关的驱动程序,如果设备使用中断,那么相应的驱动程序就注册一个中断处理程序。

驱动程序可以通过request_irp()函数注册一个中断处理程序,该函数被定义在linux/interrupt.h文件中,并且激活给定的中断线,以处理中断。

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

第一个参数表示要分配的中断号。对于大多数其他设备来说,中断号要么可以通过探测获取,要么可以通过编程动态确定。

第二个参数handler是一个指针,指向处理这个中断的实际中断处理程序。只要操作系统一接收到中断,该函数就被调用。

handler函数的原型。

typedef irqreturn_t (*irq_handler_t)(int, void *);

1:中断处理程序标志

第三个参数flags可以为0,也可能是下列一个或多个标志的位掩码。定义在linux/interrupt.h文件中。下面列举一下几个比较重要的标志:

/*
 * These flags used only by the kernel as part of the
 * irq handling routines.
 *
 * IRQF_DISABLED - keep irqs disabled when calling the action handler
 * IRQF_SAMPLE_RANDOM - irq is used to feed the random generator
 * IRQF_SHARED - allow sharing the irq among several devices
 * IRQF_PROBE_SHARED - set by callers when they expect sharing mismatches to occur
 * IRQF_TIMER - Flag to mark this interrupt as timer interrupt
 * IRQF_PERCPU - Interrupt is per cpu
 * IRQF_NOBALANCING - Flag to exclude this interrupt from irq balancing
 * IRQF_IRQPOLL - Interrupt is used for polling (only the interrupt that is
 *                registered first in an shared interrupt is considered for
 *                performance reasons)
 * IRQF_ONESHOT - Interrupt is not reenabled after the hardirq handler finished.
 *                Used by threaded interrupts which need to keep the
 *                irq line disabled until the threaded handler has been run.
 */
#define IRQF_DISABLED       0x00000020
#define IRQF_SAMPLE_RANDOM  0x00000040
#define IRQF_SHARED     0x00000080
#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

IRQF_DISABLE - 该标志被设置后,意味着内核在处理中断处理程序本身期间,要禁止所有的其他中断。如果不设置,中断处理程序可以与除本身以外的其他任何中断同时运行。

IRQF_SAMPLE_RANDOM - 此标志表明这个设备产生的中断对内核熵池(entroy pool)有贡献。内核熵池负责提供从各种随机事件导出真正的随机数。如果指定了该标志,那么来自该设备的中断间隔时间就会作为熵填充到熵池。

IRQF_TIMER - 该标志是特别为系统定时器的中断处理准备的。

IRQF_SHARED - 该标志表明可以在多个中断处理程序之间共享中断线。在同一个中断线上注册的每个处理程序必须指定这个标志,否则,在每条线上只能有一个处理程序。

第四个参数name是与中断相关的设备的ASCII文本表示

第五个参数dev用于共享中断线。当一个中断处理程序需要释放的时候,dev将提供唯一的标志信息(cookie),以便从共享中断线的诸多中断处理程序中删除指定的那一个。

request_irq()成功执行会返回0,如果返回非0值,表示有错误发生。其中最常见的错误是-EBUSY,他表示给定的中断线已经在使用。

注意request_irq()函数可能会睡眠,因此,不能在中断上下文或其他不允许阻塞的代码调用该函数。

2:一个中断的例子

在一个驱动程序中请求一个中断线,并在通过request_irq()安装中断处理程序:

request_irq();

if(request_irq(irqn,my_interrupt,IRQF_SHARED,"my_device",my_dev)){
    printk(KERN_ERR "my_device: cannot register IRQ %d
",irqn);
    return -EIO;
}

在编写中断处理函数的时候,初始化硬件和注册中断处理程序的顺序必须正确,以防止中断处理程序在设别初始化之前就开始执行。

3:释放中断处理程序

卸载驱动程序的时候,需要注销中断处理程序,并释放中断线,上述动作需要调用:

void free_irq(unsigned int irq,void *dev)

如果指定的中断线不是共享的,那么该函数删除处理程序的同时,禁用这条中断线.如果中断线是共享的,则仅仅删除dev所对应的中断处理程序,而中断线本身只有在删除了最后一个处理程序时才会被禁用.

(五):编写中断处理程序

一下是一个中断处理程序的声明:

static irqreturn_t intr_handler(int irq,void *dev);

注意,他的类型与request_irq()参数中handler所要求的参数类型相匹配.第一个参数irq就是处理程序要相应的中断的中断号.

第二个参数dev是一个通用指针,他与在中断处理程序注册时传递给request_irq()的参数dev必须一致.

中断处理程序的返回值是一个特殊类型:irqreturn_t.中断处理程序可能返回两个特殊的值:IRQ_NONE和IRQ_HANDLED.当中断处理程序检测到一个中断,但该中断对应的设备并不是在注册处理函数期间指定的产生源时,返回IRQ_NONE.当中断处理程序被正确调用,并且确实是他对应的设备产生了中断的时候,返回IRQ_HANDLED

注意:

Linux中的中断处理程序是无须重入的.当一个给定的中断处理程序正在执行时,相应的中断线在所有的处理器上都会被屏蔽掉.以防止在同一个中断线上接收另一个新的中断.由此可见,同一个中断处理程序绝对不会被同时调用以处理嵌套中断.

1:共享的中断处理程序

共享的中断处理程序和非共享的中断处理程序有一下几个差异:

1):request_irq()的参数flags必须设置为IRQF_SHARED标志

2):对于每一个注册的中断处理程序来说,dev参数必须唯一.指向任一设备结构的指针就可以满足这一要求.不能给中断处理程序传递 NULL值.

3):中断处理程序必须能够区分他的设备是否真正的产生了中断.这既需要硬件的支持,也需要处理程序中有相关的处理逻辑.

还有,指定IRQF_SHARED标志以调用 request_irq()的时候,只有在一下两种情况下才可能成功:中断线当前未被注册,或者在该线上的所有以注册处理程序都指定了IRQF_SHARED.

2:中断处理程序实例

下面我们看一下RTC(real-time clock)的中断处理程序.该程序位于drivers/char/rtc.c 文件中.该设备用于系统时钟,提供报警器或周期性的定时器.

首先,在rtc初始化的时候注册中断处理程序.我们来看一下.

函数rtc_init(void):

/* 对rtc_irq 注册 rtc_interrupt */
if (request_irq(rtc_irq, rtc_interrupt, IRQF_SHARED, "rtc",
            (void *)&rtc_port)) {
        rtc_has_irq = 0;
        printk(KERN_ERR "rtc: cannot register IRQ %d
", rtc_irq);
        return -EIO;
    }

从中我们看出,中断号由rtc_irq提供.这个变量用于为给定体系结构指定RTC中断.第二个参数是我们的中断处理程序rtc_interrupt—他将于其他中断处理程序共享中断线,因为他设置了IRQF_SHARED标志.第四个参数,可以得出驱动程序的名称为”rtc”,因为这个设备允许共享中断线,所以他给dev型参传递了一个面向每个设备的实参值.

下面我们看一下具体的中断处理函数:

#ifdef RTC_IRQ
/*
 *  A very tiny interrupt handler. It runs with IRQF_DISABLED set,
 *  but there is possibility of conflicting with the set_rtc_mmss()
 *  call (the rtc irq and the timer irq can easily run at the same
 *  time in two different CPUs). So we need to serialize
 *  accesses to the chip with the rtc_lock spinlock that each
 *  architecture should implement in the timer code.
 *  (See ./arch/XXXX/kernel/time.c for the set_rtc_mmss() function.)
 *
 *  一个非常轻型的中断处理函数.他是和IRQF_DISABLED集一起运行的,
 *  但是很有可能和set_rtc_mmss()调用发生冲突(rtc 中断和timer中断很容易
 *  同时在两个不同的CPU上运行).所以我们需要使用rtc_lock自旋锁来序列化
 *  对芯片的访问,使得每一个架构都应该在timer代码中实现.
 *
 */
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.
     *
     *  可以是alarm报警中断,更新完成的中断,或者是周期性中断.
     *  我们把这些状态保存在rtc_irq_data的低字节中,而把最后一次读取的中断
     *  号保存到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.
         *
         * 在这种情况下,是HPET RTC中断处理函数调用我们,
         * 伴随着是中断信息作为参数1而不是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);
    /* Now do the rest of the actions */
    /* 现在执行其他的操作 */
    spin_lock(&rtc_task_lock);
    if (rtc_callback)
        rtc_callback->func(rtc_callback->private_data);
    spin_unlock(&rtc_task_lock);
    wake_up_interruptible(&rtc_wait);
    kill_fasync(&rtc_async_queue, SIGIO, POLL_IN);
    return IRQ_HANDLED;
}
#endif

只要计算机一接收到RTC中断,就会调用这个函数.首先要注意的是使用了自旋锁–第一次调用是为了保证rtc_irq_data不会被SMP机器上的其他处理器同时访问,第二次调用是为了避免rtc_callback出现相同的情况.

程序后面会执行一个回调函数,RTC驱动程序允许注册一个回调函数,并在每个RTC中断到来时执行.

最后返回IRQ_HANDLED.

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-08 06:23:24

中断和中断处理(一)的相关文章

中断及中断处理

第七章 中断及中断处理 本章主要描叙Linux核心的中断处理过程.尽管核心提供通用机制与接口来进行中断处理,大多数中断处理细节都是CPU体系结构相关的.     图7.1 中断路由的逻辑图 Linux通过使用多种不同硬件来执行许多不同任务.包括驱动显示器的视频设备.驱动硬盘的IDE设备等. 我们可以同步驱动这些设备,即我们可以发送一个请求执行一组操作(比如说将一块内存数据写入到磁盘)然后等待到执行完毕.这种方式虽然可以工作,但是效率很低,因为操作系统必须等待每个操作的完成,所以操作系统将花费大量

把握linux内核设计(二):硬中断及中断处理

[版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet,文章仅供学习交流,请勿用于商业用途] 操作系统负责管理硬件设备,为了使系统和硬件设备的协同工作不降低机器性能,系统和硬件的通信使用中断的机制,也就是让硬件在需要的时候向内核发出信号,这样使得内核不用去轮询设备而导致做很多无用功. 中断使得硬件可以发出通知给处理器,硬件设备生成中断的时候并不考虑与处理器的时钟同步,中断可以随时产生.也就是说,内核随时可能因为新到来的中断而被打断.当接收到一个中断后,中断控制器会给处

中断与中断处理(二)

** 书接上回 ** (六):中断上下文 当执行一个中断处理程序的时候,内核处于中断上下文中.中断上下文由于没有后备进程,所以不可以睡眠,同时中断上下文具有严格的时间限制,因为他打断了其他代码. 中断处理程序栈的设置是一个配置选项.原来的时候,中断处理程序共享所中断进程的内核栈,大小是两页的大小,即在32位系统上是8KB,在64位系统上是16KB.现在每一个中断处理程序都有自己的一个中断栈,大小是原来的一半,即在32位机器上是4KB. (七):中断处理机制的实现 首先我们看一个中断从硬件到内核的

Linux内核实现中断和中断处理(二)

上回说了Linux内核实现中断会把中断分为两部分进行处理,上回讲了上部分,这回讲下部分的设计思路 下半部的实现机制 软中断 tasklet:是通过软中断实现的,但和软中断有所不同 工作队列 讲上面几个实现机制之前先讲一个古老的方法,现在版本的内核虽然已经不再食用了,但是思想还在继续使用 最早的Linux只提供了“bottom half”这种机制实现下半部分,被称为BH,实现简单粗暴,设置一个全局变量(32位整数),表示一个32个节点的链表队列,哪位设置为1证明哪个bottom half就可以执行

Linux 中断处理浅析

最近在研究异步消息处理, 突然想起linux内核的中断处理, 里面由始至终都贯穿着"重要的事马上做, 不重要的事推后做"的异步处理思想. 于是整理一下~ 第一阶段--获取中断号 每个CPU都有响应中断的能力, 每个CPU响应中断时都走相同的流程. 这个流程就是内核提供的中断服务程序. 在进入中断服务程序时, CPU已经自动禁止了本CPU上的中断响应, 因为CPU不能假定中断服务程序是可重入的. 中断处理程序的第一步要做两件事情: 1. 将中断号压入栈中; (不同中断号的中断对应不同的中

SylixOS 中断流程

说明 本文章分析了ARM架构下SylixOS内核中断流程,共包含三部分: 分析SylixOS运用数组和双向链表实现中断服务函数的注册 阐述ARM硬件中断机制 分析SylixOS中断处理流程 SylixOS中断注册 SylixOS中断注册是通过数组和双向链表实现的. 系统中断向量结构索引数组 _K_idescTable [256]是SylixOS内核中的系统中断向量结构索引数组,用于索引注册的中断服务函数,其类型如清单 21所示 清单 21 typedef struct {     LW_LIST

Linux 中断详解 【转】

转自:http://blog.csdn.net/tiangwan2011/article/details/7891818 原文地址 http://www.yesky.com/20010813/192117.shtml 方法之三:以数据结构为基点,触类旁通 结构化程序设计思想认为:程序 =数据结构 +算法.数据结构体现了整个系统的构架,所以数据结构通常都是代码分析的很好的着手点,对Linux内核分析尤其如此.比如,把进程控制块结构分析清楚 了,就对进程有了基本的把握:再比如,把页目录结构和页表结构

《Linux内核设计与实现》读书笔记(七)- 中断处理【转】

转自:http://www.cnblogs.com/wang_yb/archive/2013/04/19/3030345.html 中断处理一般不是纯软件来实现的,需要硬件的支持.通过对中断的学习有助于更深入的了解系统的一些底层原理,特别是驱动程序的开发. 主要内容: 什么是中断 中断类型 中断相关函数 中断处理机制 中断控制方法 总结 1. 什么是中断 为了提高CPU和外围硬件(硬盘,键盘,鼠标等等)之间协同工作的性能,引入了中断的机制. 没有中断的话,CPU和外围设备之间协同工作可能只有轮询

(八)外部中断的设计与应用 02 中断优先级嵌套设计

仿真电路图: 参考代码: 1 #include<reg52.h> 2 #define uchar8 unsigned char 3 #define uint16 unsigned int 4 5 /*****************************/ 6 // 函数名称: DelayMS( ) 7 // 函数功能: 延时 8 // 入口函数: 延时毫秒 9 // 出口函数: 无 10 /************************* *******************/ 11