Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1427712
  • 博文数量: 842
  • 博客积分: 12411
  • 博客等级: 上将
  • 技术积分: 5772
  • 用 户 组: 普通用户
  • 注册时间: 2011-06-14 14:43
文章分类

全部博文(842)

文章存档

2013年(157)

2012年(685)

分类: LINUX

2012-03-05 14:06:53

linux的中断处理
研究linux系统,不管是做驱动、协议栈还是进程调度等等,都离不开中断。
这里我们就一同讨论下linux的中断处理。
这里先列几个新手最容易迷糊,也最想了解的关于中断的几个问题:

本期讨论话题:

1.什么是硬中断,什么是软中断?
2.不同的硬中断是否可以嵌套?相同的硬中断是否可以嵌套?硬中断最多可能嵌套几级?
3.不同的软中断是否可以嵌套?相同的软中断是否可以嵌套?
4.软中断在什么时间点被调度?
1.什么时硬中断,什么是软中断?
        硬中断:是由与系统相连的外设(比如:网卡、硬盘)自动产生的。主要是用来通知操作系统外设状态的变化。比如当网卡收到数据包的时候,就会发出一个中断。
        软中断:我们知道,为了满足实时系统的要求,中断处理应该是越快越好。linux为了实现这个特点,当中断发生的时候,硬中断处理那些短时间就可以完成的工作,而将那些处理时间比较长的工作,放到中断之后来完成,也就是软中断中来完成。
2.不同的硬中断是否可以嵌套?相同的硬中断是否可以嵌套,以及是否按优先级嵌套?硬中断最多可能嵌套几级?
        Linux下硬中断是可以嵌套的,但是没有优先级的概念,也就是说任何一个新的中断都可以打断正在执行的中断,但是同种中断不会打断同种中断的执行。
一、介绍一下线程化中断
二、generic irq抽象出了对控制器的操作与以及对edge or level trigger的处理,那原来__do_IRQ里的哪些代码区别对待了edge-triggered和level-triggered。
三、8259在不能与CPU与交互的情况下是否能锁存住一次edge-triggered中断。
四、举出具体例子,从中断发出方的视度来看中断的整个流程。如果设备是level-triggered,它什么时候撤消中断信号?如果设备是edge-triggered,某一次边沿变化后,它会马上还原为之前的电平信号还是下一次再想中断时才变化?
说到中断,就不得不说电流处理。
这里并没有深入说明不同的电流类型,本身自己对硬件的了解不是非常到位,怕给大家造成误解。这里就简单说一下内核软件这块的处理流程。

linux内核中断电流类型主要有两种(当然还有一些不常见的类型), 边沿触发中断和电平触发中断,对应的处理程序分别是handle_edge_irq和handle_level_irq。
而不过是handle_edge_irq还是handle_level_irq,最终都会调用handle_IRQ_event,而handle_IRQ_event就会调用具体的中断处理函数,比如e100网卡的e100_intr函数。

中断处理的函数调用关系可以简单总结如下:

do_IRQ-->handle_edge_irq(handle_level_irq)-->handle_IRQ_event-->irqhandler
要想理解中断线程化,需要首先了解linux内核的中断处理机制。
linux中断处理是无优先级的嵌套处理,也就说每当有中断到达的时候,不管你在执行什么任务,都需要停下来去执行中断。那么这就出现了一个问题:一个对实时性要求非常高且优先级非常高的任务,可能会被一个不太重要的中断打断,这显然不是开发者希望的结果。

为了解决这个问题,内核从2.6.24之后(具体是在哪个版本添加的,可以去看log,至少在2.6.34中是包含的)引入了中断线程化处理。

中断线程化的处理,可以理解为在中断处理的上半部,又分成了两部分,一部分为简单的处理,一部分为线程处理。对于一个线程化的中断处理流程为:

do_IRQ-->handle_IRQ_event-->handler-->wake_up_thread
也就是说,线程化的中断,将中断的大部分处理动作,放到thread中,减少因为中断处理给实时任务的影响。

总结:从线程化中断的处理可以看出,它可以减少对实时任务的影响,但是它仍然会打断实时任务的运行。所以中断线程化只是治标不治本,要想从根本上解决中断对实时任务的影响,需要引入硬件优先级的概念。

使用自旋锁,即使当前进程获取不到锁,也不会让出CPU,而是忙等;而使用信号量则不同,在无法获取信号量的时候,会调度其他进程,提高了并发度
1.在ULK3的P138中提到:可以有选择地禁止IRQ线,但禁止的中断是丢失不了的,它们一旦激活,PIC就由将它们发送到CPU。请问PIC或内核是如何做到这一点的?
2.请问内核是如何保证同种中断串行化执行?
一般发生一个中断,响应的中断控制器会自动屏蔽该中断,一般在中断处理结束后打开中断
对于有些中断来说,如果允许中断被打断,也就说 IRQF_DISABLED没有置位,那么在中断处理开启之前就会,打开中断。如果设置了IRQF_DISABLED,那么就会在执行完后才开启中断
内核通过两个标志位IRQ_DISABLED和IRQ_PENDING来找回丢失的中断。
当一个中断到来,但是该IRQ线被禁用,那么就会职位IRQ_PENDING,然后返回。当内核再次enable那条中断线的时候,会判断对应的irq是否有IRQ_PENDING标志位,如果有,说明丢失了中断,会强制PIC再产生一次中断。

2.请问内核是如何保证同种中断串行化执行?
内核通过IRQ_INPROGRESS标志位来确保中断执行的串行化。每当中断执行的时候,都会置位IRQ_INPROGRESS;而每当一个新的中断将要执行的时候,都会先检查对应的IRQ_INPROGRESS是否被置位,如果IRQ_INPROGESS被置位,说明该中断类型正在被处理,那么新的中断就会置位IRQ_PENDING,然后返回。保证串行化执行

1.什么是硬中断,什么是软中断?
硬中断指的是外部中断引脚引起的中断,是对CPU产生的中断,是硬件级别的。无论有无/任何操作系统均存在的。软中断在Linux内核中指的是softirq,处理一些相对于对硬件中断响应来说比较次要操作。

2.不同的硬中断是否可以嵌套?相同的硬中断是否可以嵌套?硬中断最多可能嵌套几级?
我认为,不同的硬件中断可以嵌套的,例如在第一个中断的ISR中打开中断,那么就可以允许别的中断进来处理。
我认为相同硬中断应该是不可以嵌套的,印象中一般发生一个中断,响应的中断控制器会自动屏蔽该中断,一般在中断处理结束后打开中断。不对请指正
最多可能嵌套多少级?这个我觉得应该是可以一直嵌套下去吧,只要栈允许。当然如果相同中断不允许嵌套的话,中断源也不会超过 preempt_count()字段中的HARDIRQ_MASK吧。
3.不同的软中断是否可以嵌套?相同的软中断是否可以嵌套?
在进入软中断处理前,do_softirq会判断
        if (in_interrupt())
                return;
如果当前正在执行中断,那么就直接返回。
也就是说,在执行软中断时,发生了一个硬件中断,在ISR执行完以后,会去检查是不是有软中断:
        if (!in_interrupt() && local_softirq_pending())
                invoke_softirq();
如果当前正处于中断处理上下文中(hardirq/softirq)那么就不会执行软中断。
但是一个软中断的处理中(h->action(h);)也可以调用__local_bh_enable(SOFTIRQ_OFFSET); 打开软中断。
那么从软中断的实现上来分析分析,在一个软中断处理的时候是通过一个pending存下当前存在的软中断,在根据该变量中的每一个位逐一处理。
如下代码段,如果软中断被嵌套了,那么假设被中断的是a,中断者是b。a处理了其中的一个软中断,b中断了a后,处理所有的软中断。那么返回a后,a仍旧以为还需要处理剩下的软中断,那么就重复处理了。
由此看来,软中断是不能嵌套的。

如果中断是可以打断的(IRQF_DISABLED没有置位),就可以嵌套。但是由于同一种中断是串行处理的,所以个人认为嵌套的层数最多就是运行被中断的中断类型数。

handle_irq_event是怎么被调用的基本弄清了。原来现在irq的handle都是设置成了相应的电平处理函数:

在cat /proc/interrupts可以看出相应的电平处理

这里再列出后面两个问题的个人理解:
3.不同的软中断是否可以嵌套?相同的软中断是否可以嵌套?
        软中断的调用,不管是在什么地方被调用(中断或者线程),最后都是通过do_softirq()来激活的,在do_softirq中会监测当前是否处在中断或者软中断环境中(in_interrupt),如果是,则直接返回。而且在软中断执行的过程中,会禁止软中断调用。所以不管是否是同种类型的软中断,多不可以嵌套执行。但是不同的CPU上,可以同时运行相同类型的软中断。
4.软中断在什么时间点被调度?
        (1)内核显示的允许软中断的时候 local_bh_enable
        (2)irq_exit()的时候
        (3)ksoftirqd进程被唤醒的时候
        (4)其他可能的地方(这里没有详细的追究)

中断这一块还存在一些疑问,
例如,定时器中断丢失如何做补偿?
还有前面说的同种中断是串行执行的,这是通过什么保证同种中断串行执行,硬件软件?

1.定时器中断的丢失,一种特殊的中断处理。通过时间差来弥补丢失的中断。也就是说,每当运行时钟中断的时候,如果发现当前的jiffies比上一个jiffies+1还要大,则说明丢失了中断,那么就会一一调用。
随着hrtimer的引入,时钟中断的处理发生了不小的变化。hrtimer下,老的jiffies时钟中断都是模拟的。

2.中断串行化执行,是通过软件实现的。内核通过IRQ_INPROGRESS标志位来确保中断执行的串行化。每当中断执行的时候,都会置位IRQ_INPROGRESS;而每当一个新的中断将要执行的时候,都会先检查对应的IRQ_INPROGRESS是否被置位,如果IRQ_INPROGESS被置位,说明该中断类型正在被处理,那么新的中断就会置位IRQ_PENDING,然后返回。保证串行化执行

在__do_softirq函数中对__softirq_pending进行了清零。难道只能在清零之后产生第二个同类的软中断,并在不懂的CPU上运行?
软中断的执行是循环执行的,也就说如果再执行的过程中,又有新的软中断产生,那么就会循环执行的。当然了,循环次数是有限制的。如果到了一定次数后,还有新的软中断等待处理,那么就会激活ksoftirqd线程来处理未处理的软中断。
软中断可以看成对硬中断的一种可延迟处理。而且软中断一共就几种,一般我们会用得到的就是tasklet,tasklet是基于软中断的。
如果你的驱动中的ISR需要把一些事情放在之后执行,那你会选择什么方法呢?
选择的方案有很多,我想很多人都会选择用工作队列吧
软中断、tasklet和工作队列也有一些区别,看需求了
普通的驱动要求没那么高,就随意用就是了
软中断是在一个32位的变量上(每个CPU都有一个,所以各个cpu之间的软中断不会串扰)实现的。这样本来是可以有32种软中断的,但是linux并没有使用32种软中断。
一般的软中断都是有特定用处的,例如处理网络收发等。
软中断是在一个32位的变量上(每个CPU都有一个,所以各个cpu之间的软中断不会串扰)实现的。这样本来是可以有32种软中断的,但是linux并没有使用32种软中断。
一般的软中断都是有特定用处的,例如处理网络收发等。
enum
  1. 410{
  2. 411        HI_SOFTIRQ=0,
  3. 412        TIMER_SOFTIRQ,
  4. 413        NET_TX_SOFTIRQ,
  5. 414        NET_RX_SOFTIRQ,
  6. 415        BLOCK_SOFTIRQ,
  7. 416        BLOCK_IOPOLL_SOFTIRQ,
  8. 417        TASKLET_SOFTIRQ,
  9. 418        SCHED_SOFTIRQ,
  10. 419        HRTIMER_SOFTIRQ,
  11. 420        RCU_SOFTIRQ,   
  12. 421
  13. 422        NR_SOFTIRQS
  14. 423};里面的HI_SOFRIRQ和TASKLET_SOFTIRQ都是为tasklet准备的。
    他们的软中断处理程序就是把一个链表里面的函数一一处理,那么要增加一个tasklet就是往这个链表里加一个元素。

    所以这么看起来,软中断比tasklet高级一点,因为他的处理程序就只为自己而设置的

    他们都是处于中端上下文,是不可以被抢占的
    工作队列就是普通的内核线程,可以被抢占,可以睡眠

有时信号量代替自旋锁可能更好。因为中断处理程序不能被挂起,它们必须用紧循环和down_trylock()函数获得信号量;对这些中断处理程序来说,信号量起的作用本质上与自旋锁一样。另一方面,系统调用服务例程可以在信号量忙时挂起调用进程。对大部分系统调用而言,这是所期望的行为。在这种情况下,信号量比自旋锁更好,因为信号
阅读(910) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~