中断与中断处理(二)

**

书接上回

**

(六):中断上下文

当执行一个中断处理程序的时候,内核处于中断上下文中.中断上下文由于没有后备进程,所以不可以睡眠,同时中断上下文具有严格的时间限制,因为他打断了其他代码.

中断处理程序栈的设置是一个配置选项.原来的时候,中断处理程序共享所中断进程的内核栈,大小是两页的大小,即在32位系统上是8KB,在64位系统上是16KB.现在每一个中断处理程序都有自己的一个中断栈,大小是原来的一半,即在32位机器上是4KB.

(七):中断处理机制的实现

首先我们看一个中断从硬件到内核的路由:

在内核中,中断的旅程开始于预定义入口点,这类似于系统调用通过预定义的异常句柄进入内核.对于每条中断线,处理器都会跳到对应的一个唯一的位置.这样,内核就可以知道所接收中断的IRQ号了 .初始入口点只是在栈中保存这个号,并存放当前寄存器的值(这些值属于被中断的任务);然后,内核调用函数do_IRQ().从这里开始,大多数中断处理代码使用C编写的.

下面是fo_IRQ()的声明:

unsigned int do_IRQ(struct pt_regs regs);

因为C的调用惯例是要把函数参数放在栈的顶部,因此,pt_regs结构包含原始寄存器的值.这些值是以前在汇编入口例程中保存到栈中的.中断的值也得以保存.所以,do_IRQ()以及将它提取出来.

计算出中断号之后,do_IRQ()对所接收的中断进行应答,禁止这条中断线上的中断传递.在普通的PC上,这些操作是由mask_and_ack_8259A()来完成的.

接下来,do_IRQ()需要确保在这条中断线上有一个有效的处理程序,而且这个程序已经启动,但是当前并没有执行.如果是这样的话,do_IRQ()就会调用handle_IRQ_event()来运行为这条中断线所安装的中断处理程序.下面我们来看一下handle_IRQ_event()函数的定义,定义在kernel/irq/handler.c文件中.

/**
 * handle_IRQ_event - irq action chain handler
 * @irq:    the interrupt number
 * @action: the interrupt action chain for this irq
 *
 * Handles the action chain of an irq event
 *
 * handle_IRQ_event - irq操作链处理程序
 * @irq:    中断号
 * @action: 该中断所在的中断操作链
 *
 * 处理一个irq事件的操作链
 */
irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action)
{
    irqreturn_t ret, retval = IRQ_NONE;
    unsigned int status = 0;
    if (!(action->flags & IRQF_DISABLED))
        local_irq_enable_in_hardirq();
    do {
        trace_irq_handler_entry(irq, action);
        ret = action->handler(irq, action->dev_id);
        trace_irq_handler_exit(irq, action, ret);
        switch (ret) {
        case IRQ_WAKE_THREAD:
            /*
             * Set result to handled so the spurious check
             * does not trigger.
             *
             * 将返回值设置为已处理,以便可疑的检查
             * 不再触发.
             */
            ret = IRQ_HANDLED;
            /*
             * Catch drivers which return WAKE_THREAD but
             * did not set up a thread function
             *
             * 捕获返回值是WAKE_THREAD的驱动程序,但是
             * 并不创建一个线程函数
             */
            if (unlikely(!action->thread_fn)) {
                warn_no_thread(irq, action);
                break;
            }
            /*
             * Wake up the handler thread for this
             * action. In case the thread crashed and was
             * killed we just pretend that we handled the
             * interrupt. The hardirq handler above has
             * disabled the device interrupt, so no irq
             * storm is lurking.
             *
             * 为这次中断唤醒处理线程.万一线程崩溃并且被杀死,
             * 我们仅仅假装我们已经处理了该中断.上述的硬中断
             * 已经禁止了设备中断,因此杜绝了irq的产生.
             *
             *
             */
            if (likely(!test_bit(IRQTF_DIED,
                         &action->thread_flags))) {
                set_bit(IRQTF_RUNTHREAD, &action->thread_flags);
                wake_up_process(action->thread);
            }
            /* Fall through to add to randomness */
        case IRQ_HANDLED:
            status |= action->flags;
            break;
        default:
            break;
        }
        retval |= ret;
        action = action->next;
    } while (action);
    if (status & IRQF_SAMPLE_RANDOM)
        add_interrupt_randomness(irq);
    local_irq_disable();
    return retval;
}

首先,因为处理器禁止中断,这里要把他们打开,就必须在处理程序注册期间指定IRQF_DISABLED标志.回想一下,IRQF_DISABLED表示处理程序必须在中断禁止的情况下运行.接下来,每个潜在在处理程序在循环中依次执行.如果这条线不是共享的,第一次执行后就退出循环.否则,所有的处理程序都要被执行.之后,如果在注册期间指定了IRQF_SAMPLE_RANDOM标志,则还要调用函数add_interrupt_randomness().这个函数使用中断间隔时间为随机数产生器产生熵.最后,再将中断禁止(do_IRQ()期望中断一直是禁止的),函数返回.回到do_IRQ(),该函数做清理工作并返回到初始入口点,然后再从入口点跳到ret_from_intr()函数.

ret_from_intr()例程类似与初始入口代码,以汇编语言编写.这个例程检查重新调度是否在挂起.如果重新调度正在挂起,而且内核正在返回用户空间(即中断了用户进程),那么,schedule()被调用.如果内核正在返回内核空间(即中断了内核进程),只有在preempt_count为0的候,schedule才会被调用.

(八): /proc/interrupts

orocfs是一个虚拟文件系统,他只存在与内核内存,一把安装于/proc目录.在procfs中读写文件都要调用内核函数,这些函数模拟从真实文件中读或写.于此相关的例子是/proc/interrupts文件,该文件存放的是系统中与中断相关的统计信息.下面我们在终端上输入命令

cat /proc/interrupts

我们看一下输出结果:

对于proc文件系统,我们在我的博客操作系统中已经有了更加详细的介绍.

(九):中断控制

Linux提供了一组接口用于操作机器上的中断状态.这些接口为我们提供了能够禁止当前处理器的中断系统,或屏蔽掉整个机器的一条中断线的能力.这些例程都是和体系结构相关的,可以在

local_irq_disable();
/* 禁止中断 */
local_irq_enable();

这两个函数通常以单个汇编指令来实现.实际上,在X86中,local_irq_disable()仅仅是cli指令,而local_irq_enable()只不过是sti指令.cli和sti分别是clear和set允许中断标志的汇编调用.

如果在调用local_irq_disable()例程之前禁止了中断,那么该例程会带来潜在的危险;同样,相应的local_irq_enable()例程也存在潜在危险,因为他将无条件的激活中断,尽管这些中断在开始时就是关闭的.所以我们需要一种就机制把中断恢复到以前的状态而不是简单的禁止或激活.内核普遍关心这点,是因为,内核中一个给定的代码路径既可以在中断激活的情况下达到,也可以在中断禁止的情况下达到,这取决于具体的调用链.在禁止中断之前保存中断系统的状态会更加安全一些.相反,在准备激活中断时,只需把中断回复到他们原来的状态.

unsigned long flags;
local_irq_save(flags); /* 禁止中断 */
local_irq_restore(flags) ;  /* 中断被恢复到他们原来的状态 */

这些方法至少部分要以宏的形式实现,因此表面上flags参数(这些参数必须定义为 unsigned long类型)是以值传递的.该参数包含具体体系结构的数据,也就是包含中断系统的状态.至少有一种体系结构把栈信息与值结合(SPARC),因此,flags不能传递给另一个函数(特别是它必须驻留在同一栈帧中).基于这个原因,对local_irq_save()和local_irq_restore()的调用必须在同一个函数中进行.

2:禁止指定中断线

在某些情况下,之禁止整个系统中一条特定的中断线就够了.这就是所谓的屏蔽掉(masking out)一条中断线.为此Linux提供了四个接口:

void disable_irq(unsigned int irq);
void disable_irq_nosync(unsigned int irq);
void enable_irq(unsigned int irq);
void synchronize_irq(unsigned int irq);

前两个禁止中断控制器上指定的中断线,即禁止跟定中断向系统中所有处理器的传递.另外,函数只有在当前正在执行的所有处理程序完成之后,disable_irq()才能返回.因此,调用者不仅要确保不在指定线上传递新的中断,同时还要确保已经开始执行的处理程序已全部退出.函数diable_irq_nosygn()不会等待当前中断处理程序执行完毕.

函数sygnchronize_irq()等待一个特定的中断处理程序的退出.如果该处理程序正在执行,那么该函数必须退出后才能返回.

对这些函数的调用可以嵌套.但要记住在一条指定的中断线上,对disable_irq()或disable_irq_nosygn()的每次调用,都需要相应的调用一个enable_irq().只要在对enable_irq()完成最后一次调用后,才真正重新激活了中断线.例如,函数disable_irq()被调用了2次,那么直到第二次调用enable_irq()后,才能真正的激活中断线.

3:中断系统的状态

宏irqs_disable()定义在

in_interrupt()
in_irq()

第一个宏最有用:如果内核处于任何类型的中断处理中,他返回非0,说明内核此刻正在执行中断处理程序,或者正在执行下半部处理程序.

宏in_irq()只有在内核确实正在执行中断处理程序时才返回非0.

下面是一个中断控制方法的列表.

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

时间: 2024-10-12 13:24:59

中断与中断处理(二)的相关文章

中断及中断处理

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

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

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

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

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

中断和中断处理(一)

(一):中断 中断本质上是一种特殊的电信号,由硬件设备发向处理器.处理器在接收到中断后,会马上向操作系统反映此信号的到来,然后就u由操作系统来处理这些新到来的数据.不同的设备对应的中断不同,而每个中断都通过一个唯一的数字标志.这些中断值被称为中断请求线(IRQ).中断是随时随地发生的,也就是说中断并不考虑与处理器的时钟同步. 异常:异常的产生必须与处理器时钟同步,异常也被成为同步中断.在处理器执行到由于编程失误而导致的错误指令的时候,或者是在执行期间出现特殊情况,必须靠内核来处理的时候,处理器就

SylixOS中GIC通用中断控制器(二)——GIC实现

1.概述 本篇文档主要介绍IMX6UL平台上基于SylixOS集成开发环境中GIC通用中断控制器的实现流程和方法. 2.GIC控制器基地址获取 GIC控制器基地址通过调用armPrivatePeriphBaseGet函数获得.如图 2.1所示,Ctrl+h局搜索armPrivatePeriphBaseGet函数,搜索结果如图 2.2所示. 图 2.1全局搜索armPrivatePeriphBaseGet函数 图 2.2 armPrivatePeriphBaseGet函数搜索结果 参考DDI046

java中断小记(二)

在上一篇博文中,介绍了成员方法interrupt()的用法,这篇接上上篇继续介绍剩下的两个与中断有关的方法. 2.成员方法new Thread().isInterrupted() 通常使用Thread.currentThread().isInterrupted()方法判断某个线程是否已被发送过中断请求(某一线程被发送过中断请求后并一定会中断),因为它将线程中断标示位设置为true后,不会立刻清除中断标示位,即不会将中断标设置为false.程序示例如下: package com.szw.test;

西门子PLC学习笔记二十一-(中断处理二)

1.循环中断 循环中断组织块用于按一定时间间隔循环执行中断程序,例如周期性地定时执行某一段程序,间隔时间从STOP切换到RUN模式时开始计算. 循环中断组织块OB30~OB38默认的时间间隔和中断优先级如表所示.CPU318只能使用OB32和OB35,其余的S7-300CPU只能使用OB35.S7-400CPU可以使用的循环中断OB的个数与CPU型号有关. 循环中断组织块的时间间隔.优先级.OB号表如下: 示例:在I0.0的上升沿时启动OB35对应的循环中断,在I0.1的上升沿禁止OB35对应的

AVR开发 Arduino方法(二) 中断子系统

在了解ATMega328P的中断子系统之前,首先要了解中断的概念.你正在看书,这时电话响了,你会怎么做呢?相信大多数人会这样:先标记看到的位置,接完电话回来后继续阅读.这就是一个现实生活中中断的例子,我们把"电话响了"成为中断源.ATMega328P拥有26个中断源,如下表所示: 向量号 程序地址 中断源 中断定义 中断服务程序名称 1 0x0000 RESET 外部电平复位,上电复位,掉电检测复位,看门狗复位 2 0x0002 INT0 外部中断请求0 INT0_vect 3 0x0

Linux中断分层技术

一.中断嵌套  当系统正在执行某中断处理函数时,又产生了一个新的中断,这就叫做中断嵌套.当中断为慢速中断时,新的中断会取代当前中断,即当前中断没有执行完就结束 了:当中断为快速中断时,新的终端就不会产生.这两种情况都是我们不愿意看到的情况,所以就有了今天的题目——中断分层. 二.中断分层 中断分层是将中断处理函数分为上下两个部分.上半部是与硬件相关的,下半部是与硬件无关的.与硬件无关的内容我们就可以将它分离出中断处理函数,交给内核在系统空闲的时候处理,这样就缩短了中断处理的时间,从而减小了中断丢