Chinaunix首页 | 论坛 | 博客
  • 博客访问: 8005405
  • 博文数量: 159
  • 博客积分: 10424
  • 博客等级: 少将
  • 技术积分: 14615
  • 用 户 组: 普通用户
  • 注册时间: 2010-07-14 12:45
个人简介

啦啦啦~~~

文章分类
文章存档

2015年(5)

2014年(1)

2013年(5)

2012年(10)

2011年(116)

2010年(22)

分类: LINUX

2011-05-17 21:41:32

本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。
作者:gfree.wind@gmail.com
博客:linuxfocus.blog.chinaunix.net
    

最近一直对Linux中的中断处理过程一直很纠结,觉得很明确的概念和过程,仔细想起来还是有不明白的地方。比如中断嵌套这一问题,上学的时候,很明确的知道CPU是支持中断嵌套的。可是在看Linux的代码时,对于Linux是否支持中断嵌套产生了疑问。且在和同事的讨论中,更是从Intel的手册上看到了意想不到的说明。

好,闲话少说,开始讨论今天的正文,Linux是否支持中断嵌套!


如果操作系统要想支持中断嵌套,那么底层的平台也就是CPU必须要支持中断嵌套,否则无论操作系统如何努力,也是无法做到的——唉,这就是软件的悲哀。那么我选中的平台是Intel x86平台,这应该是应用最广泛的CPU了。

根据Intel的开发手册volume 3,6.12.1.2,
The only difference between an interrupt gate and a trap gate is the way the 
processor handles the IF flag in the EFLAGS register. When accessing an exception- 
or interrupt-handling procedure through an interrupt gate, the processor clears the 
IF flag to prevent other interrupts from interfering with the current interrupt handler. 
A subsequent IRET instruction restores the IF flag to its value in the saved contents of EFFLAGS register on the stack. Accessing a handler through a trap gate does not affect the IF flag.

这段话说明了在通过interrupt gate去调用中断处理时,CPU会自动清除IF 标志,当中断处理例程结束并返还时,通过IRET指令又将IF标志恢复。而EFLAGS的IF标志使用设置该CPU中断是否enable的标志。看到这里大家可能会有一些奇怪,按照intel的说明,岂不是说intel的CPU不支持中断嵌套。我来说一下我的理解,对于中断的定义,Intel定义的中断与咱们平时说的中断不大一样。咱们平时说的中断,应该说是Intel定义的中断的一种。而trap也被Intel认为是一种中断。这么说吧,Intel眼中的中断就是只要打断了正常的处理流程,都可以称为中断——这是我下的定义,不太准确,但大概意思是这样。所以exception和系统trap都被看完中断。而通过trap gate去调用处理函数时,CPU是不会自动禁掉中断的,这时如果发生高优先级的中断,即发生了中断嵌套。

在Linux kernel中,中断处理使用的时interrupt gate,所以从平台上看,当Linux运行在x86平台时,中断就已经不支持嵌套了——当然,要是中断函数里面非要把中断再次打开,也没办法,此处不考虑这种情况。

现在看Linux kernel本身的代码是否支持中断嵌套。下面查看do_IRQ的实现,因为这个函数是依赖于平台实现的。首先查看x86平台的实现
  1. unsigned int __irq_entry do_IRQ(struct pt_regs *regs)
  2. {
  3.     struct pt_regs *old_regs = set_irq_regs(regs);

  4.     /* high bit used in ret_from_ code */
  5.     unsigned vector = ~regs->orig_ax;
  6.     unsigned irq;

  7.     exit_idle();
  8.     irq_enter();

  9.     irq = __this_cpu_read(vector_irq[vector]);

  10.     if (!handle_irq(irq, regs)) {
  11.         ack_APIC_irq();

  12.         if (printk_ratelimit())
  13.             pr_emerg("%s: %d.%d No irq handler for vector (irq %d)\n",
  14.                 __func__, smp_processor_id(), vector, irq);
  15.     }

  16.     irq_exit();

  17.     set_irq_regs(old_regs);
  18.     return 1;
  19. }
这部分代码没有明显处理中断开关的代码,下面看其调用的handle_irq
  1. bool handle_irq(unsigned irq, struct pt_regs *regs)
  2. {
  3.     struct irq_desc *desc;
  4.     int overflow;

  5.     overflow = check_stack_overflow();

  6.     /* 得到中断描述符 */
  7.     desc = irq_to_desc(irq);
  8.     if (unlikely(!desc))
  9.         return false;

  10.     if (!execute_on_irq_stack(overflow, desc, irq)) {
  11.         if (unlikely(overflow))
  12.             print_stack_overflow();
  13.         /* 调用中断处理函数 */
  14.         desc->handle_irq(irq, desc);
  15.     }

  16.     return true;
  17. }
从上面的代码中,可以看出没有直接去对于中断使能位的任何处理。那么在x86平台上,完全依赖于x86的处理。这样的话,可以得出结论,Linux内核在x86平台上不支持中断嵌套。——当然,如果非得在中断处理函数中,enable中断标志,来允许中断嵌套。

我认为这种行为不是好的方法,因为x86既然本身限制了中断嵌套,那么必然有它的限制。如果人为的破坏这个设计,肯定会引发错误。

最后,在引用一段Intel手册中的一段话。
Because IA-32 architecture tasks are not re-entrant, an interrupt-handler task must disable interrupts between the time it completes handling the interrupt and the time it executes the IRET instruction. This action prevents another interrupt from occurring while the interrupt task’s TSS is still marked busy, which would cause a general-protection (#GP) exception.

这里明确说明了Intel的IA-32的架构,不支持中断嵌套(这里指的是可屏蔽中断)。

最后,感谢一下amarant同学,他指出了我最早引用的handler_irq的代码不正确。我之前使用cscope跳到handler_irq的定义时,没有注意到是alpha平台的定义。现已改正博文。

阅读(14925) | 评论(27) | 转发(1) |
给主人留下些什么吧!~~

amarant2011-05-27 14:26:26

GFree_Wind: 不支持中断嵌套,不是Linux本身不支持,而是x86不支持。是x86在进入中断处理时,自动禁止中断,然后在中断返回时使用IRET来恢复标志位。

至于为啥x86这样处理,.....
呵呵 不好意思,看你博文的时候没仔细看这段英文
原来这样啊, 长见识了。

GFree_Wind2011-05-26 22:22:07

amarant: 我认为,
只要保证了中断上下文不被调度,那么即使进入中断嵌套有什么关系呢?为什么要说不支持中断嵌套呢。
虽然有软中断,基本不需要中断嵌套。但是Linux的设.....
不支持中断嵌套,不是Linux本身不支持,而是x86不支持。是x86在进入中断处理时,自动禁止中断,然后在中断返回时使用IRET来恢复标志位。

至于为啥x86这样处理,请看
Because IA-32 architecture tasks are not re-entrant, an interrupt-handler task must disable interrupts between the time it completes handling the interrupt and the time it executes the IRET instruction. This action prevents another

amarant2011-05-26 20:11:09

GFree_Wind: 对啊。这就是为了防止schedule啊。

但是这对于是否支持中断嵌套,没有关系啊。.....
我认为,
只要保证了中断上下文不被调度,那么即使进入中断嵌套有什么关系呢?为什么要说不支持中断嵌套呢。
虽然有软中断,基本不需要中断嵌套。但是Linux的设计就是支持中断嵌套,就算大家不用它,也是支持的。
在ISR中开中断,这样造成的中断嵌套又没有什么恶劣后果。。

GFree_Wind2011-05-26 16:25:49

amarant: 中断嵌套必须要禁用schedule,不然的话就会调度中断上下文了。。
所以给preempt_count的hardirq加一就是表示,在此已经发生过一次中断。不可以调度。
直到所有的.....
对啊。这就是为了防止schedule啊。

但是这对于是否支持中断嵌套,没有关系啊。

amarant2011-05-26 14:24:25

GFree_Wind: 关于add_preempt_count,我认为你的理解不对。
1. add_preempt_count(HARDIRQ_OFFSET),是为hardirq位计数加1。这点是对的。但是不是为了你说的“可以中断嵌套”.....
中断嵌套必须要禁用schedule,不然的话就会调度中断上下文了。。
所以给preempt_count的hardirq加一就是表示,在此已经发生过一次中断。不可以调度。
直到所有的嵌套结束,就是preempt_count等于0才可以调度