Chinaunix首页 | 论坛 | 博客
  • 博客访问: 8128213
  • 博文数量: 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平台的定义。现已改正博文。

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

GFree_Wind2011-10-29 13:40:01

tempname2: 你的理解是错的。intel文档那样说是假设每个进程都有一个TSS段,实际上LINUX所有进程共用一个TSS段。IRET也不是故意开中断,而是恢复Flags的时候把IF也恢复了。.....
你的理解是错的。intel文档那样说是假设每个进程都有一个TSS段,实际上LINUX所有进程共用一个TSS段。IRET也不是故意开中断,而是恢复Flags的时候把IF也恢复了。CPU在进入中断后的确会禁止断,但想恢复CPU中断能力,重新打开就行了。
-------------关于其中“但想恢复CPU中断能力,重新打开就行了。”我有一个问题。按照Intel的手册说法,是进入中断时,把中断关闭,返回时重新打开。我也知道可以不是用iret去打开,而人为的去打开中断。但是不明白为什么intel这样设计?如果说intel的CPU是有意为之,那么去人为的打开,是否会引发问题呢?

GFree_Wind2011-10-29 13:34:47

tempname2: 一般的系统都是支持嵌套中断的,而且会有优先级的概念,即高优先级中断可以打断低优先级中断,反过来就不行。LINUX里不仅支持嵌套中断,而特意设计成了无优先级.....
我记得ARM的CPU是支持中断嵌套的,不知道linux的arm上的处理,是否支持嵌套。

tempname22011-10-29 01:06:56

GFree_Wind: 呵呵。。。我说呢。。。怎么感觉有点奇怪呢。。。

没事。也正是跟你的讨论。也让我改正了handler_irq,且理解更深了哈。

讨论出真知!.....
一般的系统都是支持嵌套中断的,而且会有优先级的概念,即高优先级中断可以打断低优先级中断,反过来就不行。LINUX里不仅支持嵌套中断,而特意设计成了无优先级模型,这就是为了追求最大的响应灵敏度。

tempname22011-10-29 01:03:54

GFree_Wind: 呵呵。。。我说呢。。。怎么感觉有点奇怪呢。。。

没事。也正是跟你的讨论。也让我改正了handler_irq,且理解更深了哈。

讨论出真知!.....
你的理解是错的。intel文档那样说是假设每个进程都有一个TSS段,实际上LINUX所有进程共用一个TSS段。IRET也不是故意开中断,而是恢复Flags的时候把IF也恢复了。CPU在进入中断后的确会禁止断,但想恢复CPU中断能力,重新打开就行了。
另一方面,中断控制器在没有进到EOI信号之前也不会再放进新的中断,所以刚进中断的那段时间是双保险,非常安全。handle_irq不是运行注册的ISR,而是generic irq层的抽象层,一般是对应着handle_level_irq,handle_edge_irq之类的。以前所有中断走的是__do_IRQ,现在分开处理了。这部分处理中,如果ISR不要求关闭中断,在真正执行ISR链前就会打开中断。

GFree_Wind2011-05-27 15:14:20

amarant: 呵呵 不好意思,看你博文的时候没仔细看这段英文
原来这样啊, 长见识了。.....
呵呵。。。我说呢。。。怎么感觉有点奇怪呢。。。

没事。也正是跟你的讨论。也让我改正了handler_irq,且理解更深了哈。

讨论出真知!