中断和中断处理(一)

简介: 版权声明:您好,转载请留下本人博客的地址,谢谢 https://blog.csdn.net/hongbochen1223/article/details/46857879 (一):中断中断本质上是一种特殊的电信号,由硬件设备发向处理器。
版权声明:您好,转载请留下本人博客的地址,谢谢 https://blog.csdn.net/hongbochen1223/article/details/46857879

(一):中断

中断本质上是一种特殊的电信号,由硬件设备发向处理器。处理器在接收到中断后,会马上向操作系统反映此信号的到来,然后就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.

目录
相关文章
|
1月前
|
传感器
使用中断处理的实例
使用中断处理的实例
12 1
|
2月前
|
资源调度 调度 UED
CPU执行系统调用时发生中断,操作系统还能切回中断前的系统调用继续执行吗?
系统调用服务例程在执行过程中,通常不会被中断。系统调用服务例程的执行是一个原子操作,即在执行期间不会被中断。这是为了确保在系统调用服务例程执行期间对内核数据结构的一致性和完整性。
|
3月前
|
编译器 C语言 芯片
内核里的中断
内核里的中断
34 0
|
8月前
|
调度
中断异常和系统调用
中断异常和系统调用
97 0
|
10月前
|
调度 数据安全/隐私保护
用户态和内核态 中断处理机制
用户态和内核态 中断处理机制
215 0
|
6月前
|
传感器 调度
什么是中断系统?
一、什么是中断系统 中断系统是计算机系统中的一种机制,它允许外部设备和程序请求处理器的注意力,以便进行特定的操作。当一个中断请求被触发时,处理器会暂停当前正在执行的程序,转而执行与中断相关的程序或服务例程。中断系统可以提高计算机系统的效率和响应速度,因为它允许处理器在等待某些事件的同时执行其他任务。常见的中断包括硬件中断(例如键盘输入、鼠标移动、网络数据传输等)和软件中断(例如操作系统调度、系统调用等)。 二、中断系统的特点 中断系统具有以下特点: 1. 实时性:中断系统能够及时响应外部设备的请求,提高计算机系统的响应速度和效率。 2. 可靠性:中断系统能够保证中断请求的可靠性,确保外部设备的
188 0
详解中断系统
本文针对地详解了中断系统
217 0
线程的中断
线程的中断主要涉及三个api interrupt(); isInterrupted(); Thread.interrupted();
非中断方式实现按键中断2
非中断方式实现按键中断(二) 宏定义 初始化 按键处理 延迟 主函数
|
Java
【嵌入式开发】ARM 关闭中断 ( CPRS 中断控制位 | 中断使能寄存器 | 中断屏蔽寄存器 | 关闭中断 | 汇编代码编写 )(二)
【嵌入式开发】ARM 关闭中断 ( CPRS 中断控制位 | 中断使能寄存器 | 中断屏蔽寄存器 | 关闭中断 | 汇编代码编写 )(二)
540 0
【嵌入式开发】ARM 关闭中断 ( CPRS 中断控制位 | 中断使能寄存器 | 中断屏蔽寄存器 | 关闭中断 | 汇编代码编写 )(二)