中断和中断处理

处理器的速度跟外围硬件设备的速度往往不再一个数量级上,因此,如果内核采取让处理器向硬件发出一个请求。

然后专门等待回应的办法,如果专门等待回应,明显太慢。所以等待期间可以处理其他事务,等待完成了请求操作后,再回来进行处理。

所以内核提供了一种机制,让内核在需要的时候再向内核发出信号。这就是中断机制。

一、中断

硬件中断可以随时产生,因此,内核随时可能因为新到来的中断而被打断。

不同的设备对应的中断不同,而每个中断都通过一个唯一的数字标志。

这些中断值通常被称为中断请求(IRQ)线。每个IRQ线都会被关联一个数值量。

异常:它产生时必须考虑处理器时钟同步,常常也被称为同步中断。

在处理器执行到由于编程失误而导致的错误指令的时候,或者实在执行期间出现特殊情况,必须靠内核来处理的时候,处理器就会产生一个异常。

二、中断处理程序

在响应要给特定中断的时候,内核会执行要给一个函数,该函数叫做中断处理程序中断服务例程(ISR)。

linux中断程序是普通的C函数,不过必须按照特定的方式声明,以便内核识别。

真正的区别在于:中断处理程序是被内核调用来响应中断的,而它们运行于中断上下文的特殊上下文中。

中断上下文偶尔也称为原子上下文,该上下文中的执行代码不可阻塞。

中断程序最好尽快反应,并且执行时间尽可能短。

三、上半部与下半部的对比

一般把中断程序分成两个部分,中断处理程序是上半部(接收到一个中断,就立即执行,但只做有限的工作) 。

稍后完成的工作会推迟到下半部去,在适合的时机,下半部会被开中断执行。

四、注册中断处理程序

驱动程序可以通过request_irq()函数注册终端处理程序,它被声明再文件<linux/interrupt.h>中

/* request_irq:分配一条给定的中断线 */
int request_irq(unsigned int irq,
irq_handler_t handler,
unsigned long flags,
const char *name,
void *dev)
/* 第一个参数irq表示要分配的中断号 */
/* 第二个参数handler时一个指针,指向处理这个中断的实际中断处理程序 */ /* handler原型 */
typedef irqreturn_t (*irq_handler_t)(int, void *);

request_irq原型

4.1 中断处理程序标志

第三个参数flags可以为0,再文件<linux/interrupt.h>中定义了掩码

IRQF_DISABLED:意味着内核在处理中断程序本身期间,要禁止所有的其他中断

IRQF_SAMPLE_RANDOM:此标志表明这个设备产生的中断对内核熵池有贡献

IRQF_TIMER:特别为系统定时器的中断处理而准备的

IRQF_SHARED:可以在多个中断的处理程序之间的贡献中断线

第四个参数name是与中断相关的设备的ASCII文本表示,在/proc/irq和/proc/interrupts

第五个参数dev用于共享中断线,当一个中断处理程序释放时,dev将提供标志信息。如果不需要共享中断线,设空(NULL)就行。

4.2 一个中断例程

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

request_irq()

  • irqn是请求中断线
  • my_interrupt是中断处理程序
  • 设置IRQF_SHARED中断可以共享
  • 设备名为"my_device"
  • 传递my_dev给dev形参

如果调用返回0,则说明成功。必须先初始化程序,然后再注册中断

4.3 释放中断处理程序

调用free_irq注销相应的中断处理程序。

void free_irq(unsigned int irq, void *dev)

free_irq

如果指定的中断线不是共享的,那么函数删除动作的同时会禁用此中断线

如果设置了共享标志,那么函数删除仅仅对dev对应的处理程序,除非删除了最后一个处理程序后,中断线才会被禁用。

五、编写中断处理程序

/* irq:参数已经没有太大意义了 */
/* dev:一个通用指针,它与中断处理程序注册时传递的必须一致 */
static irqreturn_t intr_handler(int irq, void *dev)

irqreturn_t intr_handler

返回值是一个特殊类型:irqreturn_t,可能返回两个特殊的值:IRQ_NONE和IRQ_HANDLED

重入和中断处理程序:中断程序是不需要重入的,程序中断时,其他中断会被屏蔽,而且也不会重复嵌套。

5.1 共享中断处理程序

共享和非共享处理程序比较相似,但是差异主要有三处:

  • request_irq()参数flags必须设置IRQF_SHARED标志
  • 对于每个注册的中断程序,dev参数必须唯一,中断处理程序会用到这个值
  • 中断处理程序必须能够区分它的设备是否真的产生了中断。否则无法判断哪个设备发出的中断请求。

任何一个设备没有按规则进行共享,那么中断就无法共享了。

5.2 中带能处理程序实例

在driver/char/rtc.c中有RTC相关的例子。中断发生时,报警器或定时器就会启动。

当RTC驱动程序装载时,rtc_init()会被调用。

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

注册rtc_interrupt

第一个参数说明RTC位于IRQ8,中断程序为rtc_interrupt,并且设置了共享中断线,程序名为"rtc"

static irqreturn_t rtc_interrupt(int irq, void *dev)
{
/*
* 可以是报警中孤单、更新完成的中断或周期性中断
* 我们把装填保存在rtc_irq_data的低字节中,
* 而把从最后一次读取之后所接收的中断号保存在其余字节中
*/
spin_lock(&rtc_lick); rtc_irq_data += 0x100;
rtc_irq_data &= ~0xff;
rtc_irq_data |= (CMOS_READ(RTC_INTR_FLAGS) & 0xF0); if(rtc_status & RTC_TIMER_ON)
mod_timer(&rtc_irq_timer, jiffies + HZ/rtq_freq + *HZ/); spin_unlock(&rtc_lock); /*
* 现在执行其余的操作
*/
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;
}

中断处理程序

使用自旋锁,保证不会被其他处理器同时访问。

rtc_irq_data存放RTC有关的信息。

后面部分,则是可能运行预先设置好的回调函数。每个RTC驱动程序允许注册要给回调函数,在中断到来时执行。

最后返回IRQ_HANDLED,表明已经正确完成对此设备的操作。

六、中断上下文

当执行一个中断处理程序时,内核处于中断上下文中。

中断上下问和进程没有什么瓜葛。

中断上下文具有较为严格的时间限制,因为它打断了其他代码。

中断处理程序栈的设置是要给配置选项,32位8KB,64位16KB 。

总而言之,尽量节约内核栈空间。

七、中断处理机制的是实现

中断发生,通过电信号传递给处理器,除非屏蔽了此中断,否则处理器会立即执行。

中断旅程开始于预定义入口,每条中断线,处理器都会跳到对应的一个唯一的位置,这样内核就知道接收的IRQ中断号。

unsigned int do_IRQ(struct pt_regs regs)

do_IRQ()声明

如果do_IRQ确保在中断线上有一个有效的处理程序,而且程序已经启动。do_IRQ()就调用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
*/
irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action)
{
irqreturn_t ret, retval = IRQ_NONE;
unsigned int status = ; 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:
/*
* 把返回值设置为已处理,以便可疑的检查不再触发
*/
ret = IRQ_HANDLED; /*
* 捕获返回值为WAKE_THREAD的驱动程序,但是并不创建一个线程函数
*/
if(unlikely(!action->thread_fn)) {
warn_no_thread(irq, action);
break;
} /*
* 为这次中断唤醒处理线程。万一线程崩溃且被杀死,我们仅仅假装已经处理了该中断。上述的硬件中断(hardirq)处理程序已经进制设备中断,因此杜绝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;
}

handle_IRQ_event()

八、/proc/interrupts

chen@chen-K42F:~/Downloads//linux-4.14.$ cat /proc/interrupts
CPU0 CPU1
: IO-APIC -edge timer
: IO-APIC -edge i8042
: IO-APIC -fasteoi acpi
: IO-APIC -edge i8042
: IO-APIC -fasteoi ehci_hcd:usb1
: IO-APIC -fasteoi ath9k
: IO-APIC -fasteoi i801_smbus
: IO-APIC -fasteoi ehci_hcd:usb2
: PCI-MSI -edge xhci_hcd

cat /proc/interrupt部分结果

第一列时中断线,第二列时一个接收中断的计数器,第三列时处理这个中断的中断控制器

九、中断控制

相关文件在<asm/system.h> <asm/irq.h>中找到

9.1 禁止和激活中断

用于禁止当前处理器上的本地中断,随后再就激活他们的语句。

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

9.2 进制指定中断线

对中断的状态操作之前禁止设备中断的传递,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);

四种方法

9.3 中断系统状态

通常有必要了解中断系统的状态,或者你当前是否正处于中断上下文的执行状态中。

宏irqs_disable()定义在<asm/system.h>

如果本地处理器上的中断系统被禁止,则返回非0,否则返回0

在<linux/hardirq.h>

local_irq_disable()  禁止本地中断传递

local_irq_enable()  激活本地中断传递

local_irq_save()  保存本地中断传递的当前状态,然后禁止本地中断传递

local_irq_restore()  恢复本地中断传递到给定的状态

disable_irq()    禁止给定中断线,并确保该函数返回之前在该中断线上没有处理程序再运行

disable_irq_nosync()  禁止给定中断线

enable_irq()    激活给定中断线

irqs_disabled()    如果本地中断传递被禁止,则返回非0,否则返回0

in_interrupt()    如果在中断上下文中,则返回非0,如果在进程上下文中,则返回0

in_irq()    如果当前正在执行中断处理程序,则返回非0,否则返回0

Linux内核设计与实现 总结笔记(第七章)中断和中断处理的更多相关文章

  1. Linux内核设计与实现 总结笔记(第二章)

    一.Linux内核中的一些基本概念 内核空间:内核可独立于普通应用程序,它一般处于系统态,拥有受保护的内存空间和访问硬件设备的所有权限.这种系统态和被保护起来的内存空间,称为内核空间. 进程上下文:当 ...

  2. Linux内核设计与实现 总结笔记(第九章)内核同步介绍

    在使用共享内存的应用程序中,程序员必须特别留意保护共享资源,防止共享资源并发访问. 一.临界区和竞争条件 1.1 临界区和竞争条件 所谓临界区就是访问和操作共享数据代码段.多个执行线程并发访问同一个资 ...

  3. 《Linux内核设计与实现》课本第四章自学笔记——20135203齐岳

    <Linux内核设计与实现>课本第四章自学笔记 进程调度 By20135203齐岳 4.1 多任务 多任务操作系统就是能同时并发的交互执行多个进程的操作系统.多任务操作系统使多个进程处于堵 ...

  4. 《Linux内核设计与实现》课本第三章自学笔记——20135203齐岳

    <Linux内核设计与实现>课本第三章自学笔记 进程管理 By20135203齐岳 进程 进程:处于执行期的程序.包括代码段和打开的文件.挂起的信号.内核内部数据.处理器状态一个或多个具有 ...

  5. 《Linux内核设计与实现》课本第五章学习笔记——20135203齐岳

    <Linux内核设计与实现>课本第五章学习笔记 By20135203齐岳 与内核通信 用户空间进程和硬件设备之间通过系统调用来交互,其主要作用有三个. 为用户空间提供了硬件的抽象接口. 保 ...

  6. Linux内核设计与实现 读书笔记 转

    Linux内核设计与实现  读书笔记: http://www.cnblogs.com/wang_yb/tag/linux-kernel/ <深入理解LINUX内存管理> http://bl ...

  7. 《Linux内核设计与实现》第一、二章学习笔记

    <Linux内核设计与实现>第一.二章学习笔记 姓名:王玮怡  学号:20135116 第一章 Linux内核简介 一.关于Unix ——一个支持抢占式多任务.多线程.虚拟内存.换页.动态 ...

  8. Linux内核设计与实现 读书笔记

    第三章 进程管理 1. fork系统调用从内核返回两次: 一次返回到子进程,一次返回到父进程 2. task_struct结构是用slab分配器分配的,2.6以前的是放在内核栈的栈底的:所有进程的ta ...

  9. 初探内核之《Linux内核设计与实现》笔记上

    内核简介  本篇简单介绍内核相关的基本概念. 主要内容: 单内核和微内核 内核版本号 1. 单内核和微内核   原理 优势 劣势 单内核 整个内核都在一个大内核地址空间上运行. 1. 简单.2. 高效 ...

  10. 《LINUX内核设计与实现》第一、二章学习总结

    第一章 Linux内核简介 (一)Unix是一个强大.健壮和稳定的操作系统,特点是: Unix很简洁,仅仅提供几个几百个系统调用并且有一个非常明确的设计目的 在Unix中,所有的东西都被当作文件对待, ...

随机推荐

  1. 运行jar包shell脚本

    #!/bin/sh #该文件必须放在jar包的目录下,因为是以相对路径来运行的.不放jar包目录的话,可以直接在jar_name参数写绝对路径 #start 设置三个参数 #环境 profile=te ...

  2. Vue实现音乐播放器(二)-Vue-cli脚手架安装

  3. servlet过滤器Filter使用之DelegatingFilterProxy类

    正常情况下,我们需要添加一个过滤器,需要实现javax.servlet.Filter接口,再在web.xml中配置filter,如下: package cc.eabour.webapp.securit ...

  4. 2019_Chrome和ChromeDriver对应关系

    Chrome和ChromeDriver对应关系 ChromeDriver下载地址:http://chromedriver.storage.googleapis.com/index.html Chrom ...

  5. 剑指offer--day10

    1.1 题目:二叉搜索树的后序遍历序列:输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果.如果是则输出Yes,否则输出No.假设输入的数组的任意两个数字都互不相同. 1.2 思路: 以{ ...

  6. 应用安全 - Web安全 - 上传漏洞 - 攻防

    客户端绕过 抓包改包(先上传一个gif类型的木马,然后通过burp将其改为asp/php/jsp后缀名即可) 服务端校验 content-type字段校验 文件头检验(常见文件头: () .JPEG; ...

  7. 006/搭建fabric(二)

    准备vmware虚拟机,并安装完ubuntu系统后.继续搭建fabric运行环境... 0.打开终端,切换root身份.目的:后续操作即可不用sudo... 右键->open Terminal- ...

  8. Gradle -- 初体验

    Gradle是一个基于Apache Ant和Apache Maven概念的项目自动化构建开源工具.它使用一种基于Groovy的特定领域语言(DSL)来声明项目设置,抛弃了基于XML的各种繁琐配置. ​ ...

  9. Reactjs 列表优化的一些心得

    前言 在应用开发中,列表是我们使用频率非常高的一种展现形式,在reactjs项目中更是如此.无处不在的使用更是需要我们小心触发性能瓶颈的深水炸弹. 下面就我最近的总结出的几点心得分享给大家,有什么问题 ...

  10. Tornado 的核是什么??

    Tornado 的核心是 ioloop 和 iostream 这两个模块,前者提供了一个高效的 I/O 事件循环,后 者则封装了 一个无阻塞的 socket .通过向 ioloop 中添加网络 I/O ...