研究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- 410{
- 411 HI_SOFTIRQ=0,
- 412 TIMER_SOFTIRQ,
- 413 NET_TX_SOFTIRQ,
- 414 NET_RX_SOFTIRQ,
- 415 BLOCK_SOFTIRQ,
- 416 BLOCK_IOPOLL_SOFTIRQ,
- 417 TASKLET_SOFTIRQ,
- 418 SCHED_SOFTIRQ,
- 419 HRTIMER_SOFTIRQ,
- 420 RCU_SOFTIRQ,
- 421
- 422 NR_SOFTIRQS
- 423};里面的HI_SOFRIRQ和TASKLET_SOFTIRQ都是为tasklet准备的。
他们的软中断处理程序就是把一个链表里面的函数一一处理,那么要增加一个tasklet就是往这个链表里加一个元素。
所以这么看起来,软中断比tasklet高级一点,因为他的处理程序就只为自己而设置的
他们都是处于中端上下文,是不可以被抢占的 工作队列就是普通的内核线程,可以被抢占,可以睡眠
有时信号量代替自旋锁可能更好。因为中断处理程序不能被挂起,它们必须用紧循环和down_trylock()函数获得信号量;对这些中断处理程序来说,信号量起的作用本质上与自旋锁一样。另一方面,系统调用服务例程可以在信号量忙时挂起调用进程。对大部分系统调用而言,这是所期望的行为。在这种情况下,信号量比自旋锁更好,因为信号 |
|