看了《深入理解linux内核》的中断与异常,简单总结了下,如果有错误,望指正!

一 什么是中断和异常

  异常又叫同步中断,是当指令执行时由cpu控制单元产生的,之所以称之为异常,是因为只有在一条指令结束之后才发出中断(程序执行异常或者系统调用)。

  中断又叫异步中断,是由其他硬件设备依照cpu时钟信号随机产生的。

二 高级可编程中断控制器

APIC

  每个CPU都有一个本地的APIC,通过IIC bus链接到一个I/O APIC,这个I/O APIC负责处理外部IRQS,分发IRQS给本地APIC。

三 中断与异常处理程序嵌套执行

  中断处理程序允许被另一个中断处理程序”中断“,从而引起内核控制路径嵌套执行。但是中断处理程序是不允许发生阻塞,即任务切换的。

  中断可以抢占异常处理程序,但异常处理程序不会抢占中断。因为中断处理程序必定处于内核态,如果发生异常,那只能是BUG了,也就是说内核控制路径中异常处理程序不会超过一个。

四 Linux中断描述符

  Intel把中断描述符分三类:任务门、中断门、陷阱门,而Linux则分成五类:

  1. 中断门:Intel的中断门,DPL = 0,描述中断处理程序,通过set_intr_gate宏设置
  2. 系统门:Intel的陷阱门,DPL = 3,用于系统调用,通过set_system_gate宏设置
  3. 系统中断门:Intel的中断门,DPL = 3,用于向量3的异常处理,通过set_system_intr_gate宏设置
  4. 陷阱门:Intel陷阱门,DPL = 0,大部分的异常处理,通过set_trap_gate宏设置
  5. 任务门:Intel任务门,DPL = 0,对”Double fault“异常处理,通过set_task_gate宏设置

五 异常处理

  当cpu产生异常时,会自动根据产生的异常编号在IDT中找对应的异常处理程序,异常处理程序保存大多数寄存器的值,调用异常处理的高级C函数处理该异常,然后通过调用ret_from_exception从异常处理程序退出。

六 中断处理

  I/O中断处理程序执行的四个基本过程:

  1. 在内核态堆栈中保存IRQ的值和寄存器的内容
  2. 给正在为IRQ线服务的PIC发送一个应答,这将允许该PIC进一步发中断
  3. 执行共享该IRQ的所有设备的中断服务例程(ISR)

七 IRQ数据结构

IRQ数据结构

  hw_irq_controller是对PIC进程控制的一些函数,包括应答PIC什么的。action指向的是一个irqaction链,每个irqaction描述一个设备的服务例程。irq_desc_t中的state字段保证了同一时刻只有一个设备会拥有该IRQ,正在处理该IRQ的CPU会禁用这条IRQ(本地),其它cpu还是可以接受该IRQ的请求,不过由于此时state的状态为IRQ_INPROGRESS,所以新的IRQ请求会在其它的CPU上应答,但不会处理,也就是该新的IRQ处理会被延迟到处理同一个IRQ的前面一个CPU上执行。能这样做是因为IRQ的数据结构是所有CPU所共享的。

 

八 多种类型的内核栈

  如果编译内核设置内核栈为8k,那么进程的内核栈被用于所有类型的内核控制路径。如果内核栈为4k,则内核使用3种类型的内核栈:异常栈,用于处理异常,每个进程一个;硬中断栈,用于处理中断,每个cpu一个;软中断栈,用于处理延迟函数,每个cpu一个。

九 软中断

1 为什么要引进软中断机制,用前面的中断机制不就可以了吗(老版本的linux就没有软中断机制)?

  从前面的中断处理中可以看出,一个中断处理程序的几个中断服务例程(每个设备一个)是串行执行的,如果某个处理例程执行的时间比较长,而后面的例程又很紧急,那么会导致这个紧急的例程会延迟比较久的时间。所以如果能把一些服务例程中不是很紧急但又花费比较长的操作延迟到执行完该IRQ上所有中断服务例程之后执行,那么一些紧急的,花费时间短(一般紧急的操作所需的时间都是比较短的)的例程就可以得到快速的响应。还有就是对于某个设备的中断服务例程,如果它的服务例程服务时间过长,cpu在执行该服务例程时是会中断本地cpu对该设备的中断或者整个本地中断,这样会导致很多中断会得不到快速的响应。所以为了处理这两个问题,内核使用软中断来解决,软中断通常处理中断中比较耗时的操作,并且执行期间允许本地中断。

2 软中断使用的关键数据结构

  softirq_vec数组,每个数组元素类型为softirq_action,该数组总共有32个元素,目前只用了前面六个。softirq_action数据结构包含两个字段:指向软中断处理函数的action指针和指向软中断函数需要的通用数据结构的data指针,这是cpu共享的。

  每个进程描述的thread_info字段中的preempt_count字段,该字段被编码来表示三个不同的计数器和一个标志。0-7位表示是否允许抢占内核,8-15表示是否正在处理软中断,16-27表示硬件中断控制路径嵌套数,28为是PREEMPT_ACTIVE标志。每个进程有一个。

  另一个是每个cpu都有的32位掩码,存放在irq_cpustat_t数据结构中的__softirq_pending字段,32位,每一位表示softirq_vec数组中的对应的软中断函数是否已激活。irq_cpustat_t存放在irq_stat数组中,每个cpu对于数组中的一个irq_cpustat_t。每个cpu一个。

 

3 软中断可延迟函数的四个操作

  • 初始化,定义一个新的可延迟函数,并加入到softirq_vec数组中,所有cpu共享该softirq_vec。
  • 激活,标记一个可延迟函数为”挂起“,通过前面描述的__softirq_pending字段。
  • 屏蔽,有选择地屏蔽一个可延迟函数,即使它被激活。它是通过前面的preempt_count字段或者关闭本地中断(延迟函数一般是通过中断处理程序激活的,如果没有中断处理程序执行,自然也就不会有延迟函数的激活)来实现的。
  • 执行,执行一个挂起的可延迟函数和同类型的其它挂起的可延迟函数。通过do_softirq实现。

  激活和执行可延迟函数必须要在同一个cpu上,从前面激活和执行可以看出这一点。因为__softirq_pending是每个cpu一个,所有在特定cpu激活的延迟函数,只有在该cpu上的__softirq_pending标记被激活,而其它cpu是不知道该函数被激活的,也就不会去执行该函数了。

4 linux现有的六种软中断

  处理高级优先级的tasklet软中断HI_SOFTIRQ,在softirq_vec数组的下标为0;和时钟中断关联的tasklet软中断TIMER_SOFTIRQ,在softirq_vec数组的下标为1;把数据包传送到网卡软中断NET_TX_SOFTIRQ,在softirq_vec数组的下标为2;从网卡接收数据包的软中断NET_RX_SOFTIRQ,在softirq_vec数组的下标为3;SCSI命令中断处理的软中断SCSI_SOFTIRQ,在softirq_vec数组的下标为4;处理常规tasklet软中断TASKLET_SOFTIRQ,在softirq_vec数组的下标为5;它们的优先级是从高到低的。

5 检查是否有软中断挂起(也叫激活)的时机

  • 内核调用local_bh_enable激活本地软中断
  • 当do_IRQ完成了I/O中断的处理或调用irq_exit时
  • smp_apic_timer_interrupt处理完本地定时中断
  • cpu处理CALL_FUNCTION_VECTOR中断处理
  • 当一个特殊的ksoftirq/n内核线程被唤醒。

  ksoftirq/n内核线程是专门用来处理已经激活的软中断的,每个cpu有一个。

十 tasklet

  tasklet是在软中断的基础上实现的。正如前面说的linux现有的六种软中断,其中HI_SOFTIRQ和TASKLET_SOFTIRQ软中断就是tasklet。

  对于每种tasklet(HI_SOFTIRQ和TASKLET_SOFTIRQ),每个cpu都有一个tasklet_head类型来描述这种tasklet。tasklet_head类型指向了一个由tasklet类型组成的链表,而每个tasklet类型描述了该tasklet要执行的函数和函数需要的数据以及tasklet的状态(状态表示同一个tasklet是否正在其它cpu上运行等)。

  为什么在运行一个tasklet之前要检查这个tasklet是否正在运行呢?当一个tasklet被调度了,并正在运行的时候,这个tasklet又在另一个cpu被调度了,这样很可能出现这个tasklet同时在两个cpu上运行,为了防止这种情况才需要每次运行一个tasklet之前检查该tasklet是否正在运行。

  只要一个tasklet的TASKLET_STATE_SCHED标记被清除了,它就可以被用于调度,即使可能这个tasklet还正在运行。这样做是为了让不相同的tasklet能够同时地执行,这显然比让一个tasklet执行完之后再调度它效率高(通常tasklet执行时间比中断处理程序都是长的)。

  当do_softirq处理软中断时,如果相应的HI_SOFTIRQ和TASKLET_SOFTIRQ软中断被激活,就会调用对应的软中断函数tasklet_hi_action和tasklet_action。而这两个函数处理的数据正是HI_SOFTIRQ和TASKLET_SOFTIRQ类型tasklet所对应的tasklet_head类型指向的tasklet链表。

  为了能够写自己的tasklet函数并加入到对应tasklet类型的tasklet_head指向的链表中,需要调用tasklet_init来初始化新的tasklet和调用tasklet_schedule或tasklet_hi_schedule来把我们自定义的tasklet加入到对应的tasklet_head指向的链表中并激活该tasklet类型所对应的软中断。

  所以说tasklet是在软中断的基础上实现的,但不同的是软中断是静态分配的(linux分配了6个软中断),而tasklet是可以动态加入和删除tasklet的。

十一 工作队列

  每个工作队列,在每个cpu上都有一个cpu_workqueue_struct结构来描述,即对于同一个工作队列,每个cpu都有该队列的一个拷贝。cpu_workqueue_struct中的worklist成员指向了由work_struct结构组成的链表,这个链表描述的是该工作队列被挂起的函数,等待工作者线程去执行。

  通过调用create_workqueue创建工作队列。如果是smp,则每个cpu都会创建一个工作队列和为该工作队列工作的工作者线程。该工作者线程一直阻塞,直到有函数被加入到工作队列中去,工作者线程执行了函数之后就把函数从工作队列中删除。

  可以通过queue_work函数把一个函数加入到工作队列中去。

  由于仅仅为了一个函数就创建一个工作队列开销太大,所以内核预定义了一个叫events的工作队列,为该队列工作的工作者线程叫events/n,每个cpu一个。还有就是专门为块设备使用的kblocked工作队列。

十二 软中断与工作队列的区别

  从上面可以看出,软中断和工作队列十分的相似,都是对服务例程中某些耗时的操延后处理。主要区别在于,软中断的可延迟函数是在中断上下文中执行的,而工作者队列的函数则是在进程上下文中执行的,也就是说软中断的延迟函数执行期间不允许内核被抢占,而工作队列则是可以的。

  

linux中断与异常的更多相关文章

  1. Linux中断技术、门描述符、IDT(中断描述符表)、异常控制技术总结归类

    相关学习资料 <深入理解计算机系统(原书第2版)>.pdf http://zh.wikipedia.org/zh/%E4%B8%AD%E6%96%B7 独辟蹊径品内核:Linux内核源代码 ...

  2. Linux的中断 & 中断和异常的区别

    参考 http://www.yesky.com/20010813/192117.shtml 结构化程序设计思想认为:程序 = 数据结构 + 算法.数据结构体现了整个系统的构架,所以数据结构通常都是代码 ...

  3. Linux内核中断和异常分析(中)

    在linux内核中,每一个能够发出中断请求的硬件设备控制器都有一条名为IRQ的输出线.所有现在存在的IRQ线都与一个名为可编程中断控制器的硬件电路的输入引脚相连,上次讲到单片机的时候,我就讲到了单片机 ...

  4. Linux内核中断和异常分析(上)

    中断,通常被定义为一个事件.打个比方,你烧热水,水沸腾了,这时候你要去关掉烧热水的电磁炉,然后再去办之前手中停不下来的事情.那么热水沸腾就是打断你正常工作的一个信号机制.当然,还有其它的情况,我们以后 ...

  5. 深入理解Linux内核-中断和异常

    Linux内核代码查看 http://androidxref.com/ 中断:被定义位一个事件,它能改变处理器执行指令的顺序.它对应硬件(CPU.其他硬件设备)电路产生的电信号. 同步中断:指令执行时 ...

  6. Linux 2.6 内核阅读笔记 中断和异常

    2014年7月24日 中断门.陷阱门及中断门 中断是能够禁止的,能够通过告诉PIC停止对某个中断的公布.被禁止的中断是不会丢失的,在解除禁止后又会发送到CPU上. 禁止中断和屏蔽(mask)中断的不同 ...

  7. 《深入理解Linux内核》阅读笔记 --- 第四章 中断和异常

    1.中断的作用:中断信号提供了一种方式,使处理器转而去运行正常控制流之外的代码.当一个中断信号到达时,CPU必须停止它当前所做的事,并切换到一个新的活动.为了做到这一点,就要在内核态堆栈保存程序计数器 ...

  8. ASM:《X86汇编语言-从实模式到保护模式》第17章:保护模式下中断和异常的处理与抢占式多任务

    ★PART1:中断和异常概述 1. 中断(Interrupt) 中断包括硬件中断和软中断.硬件中断是由外围设备发出的中断信号引发的,以请求处理器提供服务.当I/O接口发出中断请求的时候,会被像8259 ...

  9. Linux 中断详解 【转】

    转自:http://blog.csdn.net/tiangwan2011/article/details/7891818 原文地址 http://www.yesky.com/20010813/1921 ...

随机推荐

  1. mathlab之floor,ceil,round,int以及fix函数

    建议自己动手敲敲,网上很多人自己都没搞清楚然后好多错的.毕竟自己亲眼看到结果才有说服力. 以下是我亲眼见到的结果. 1.double floor(double)函数 floor()函数是常用的取整函数 ...

  2. bfs codeforces 754B Ilya and tic-tac-toe game

    这题简直把我坑死了 所有的坑都被我中了 题意: 思路:bfs or 模拟 模拟似乎没有什么坑 但是bfs真的是坑 AC代码: #include "iostream" #includ ...

  3. HDu--我要拿走你的蜡烛

    我要拿走你的蜡烛 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Su ...

  4. pythonchallenge 解谜 Level 2

    好吧,赶紧贴一下. #-*- coding:utf-8 -*- #代码版本均为python 3.5.1 #Level 2 import re file = open("Level 2.txt ...

  5. HDU2433 BFS最短路

    Travel Time Limit: 10000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Sub ...

  6. EXT 下拉框事件

    1. <ext:ComboBox ID="cbline" FieldLabel="平台部门来源" runat="server" Dis ...

  7. SQL Server出现错误: 4014

    SQL Server出现错误: 4014 线下的测试机器老是报错,从errorlog里看到大量的4014错误 A fatal error occurred , output error: ). 错误: ...

  8. 皮裤原理和运营微信公众号dotNET跨平台

    经常碰到有同学对.NET跨平台存在各种疑惑和误解,原因是什么呢?当然我是知道.NET的跨平台不是问题,而且微软2014年的努力可圈可点,而且还有很多人对.NET的前景感到困惑.春节期间突然明白了,这就 ...

  9. awk 留底

      序   因为经常做awk编码,而且跨过一段时间就容易忘记,故在此做个留底.便于翻阅.——后期会在这个页面不断补充!   常用常量   属性 描述 NR  已读入的总记录数  ARGIND  当前被 ...

  10. [SDK2.2]SQL Azure (13) Azure的两种关系型数据库服务:SQL Azure与SQL Server VM的不同

    <Windows Azure Platform 系列文章目录> 如果熟悉Windows Azure平台的用户不难发现,对于SQL Server数据库来说,微软提供了两种服务,分别是: -W ...