linux kernel 中断处理函数里不能进程调度的原因

来源:互联网 发布:亿晟软件技术有限公司 编辑:程序博客网 时间:2024/06/06 02:23

内核在编译的时候设置了THREAD_SIZE的值为8K的话, 那么每个进程的内核栈的大小就为8K, 此时如果发生中断时, 那么进程的寄存器等值就会保存到它的8K的内核栈中. 但是如果设置了THREAD_SIZE的大小为4K的话, 内核就会使用3种类型的内核栈, 异常栈, 硬件中断请求栈以及软中断请求栈( When using 4K stacks, interrupts get their own stack instead of using the currently active kernel stack.)

   * 异常栈:每个进程一个。

   * 硬中断请求栈:每个CPU一个,每个占用一个单独的页框。do_IRQ()函数内部通过调用execute_on_irq_stack负责切换到该栈中来。

   * 软中断请求栈:每个CPU一个,每个占用一个单独的页框。

关于切换到中断栈的方法,请看下面的代码:

view plaincopy to clipboardprint?
  1. /* 
  2.  * do_IRQ handles all normal device IRQ's (the special 
  3.  * SMP cross-CPU interrupts have their own specific 
  4.  * handlers). 
  5.  */  
  6. unsigned int do_IRQ(struct pt_regs *regs)  
  7. {  
  8.     struct pt_regs *old_regs;  
  9.     /* high bit used in ret_from_ code */  
  10.     int overflow;  
  11.     unsigned vector = ~regs->orig_ax;  
  12.     struct irq_desc *desc;  
  13.     unsigned irq;  
  14.     old_regs = set_irq_regs(regs);  
  15.     irq_enter();  
  16.     irq = __get_cpu_var(vector_irq)[vector];  
  17.     overflow = check_stack_overflow();  
  18.     desc = irq_to_desc(irq);  
  19.     if (unlikely(!desc)) {  
  20.         printk(KERN_EMERG "%s: cannot handle IRQ %d vector %#x cpu %d\n",  
  21.                     __func__, irq, vector, smp_processor_id());  
  22.         BUG();  
  23.     }  
  24.     if (!execute_on_irq_stack(overflow, desc, irq)) {  
  25.         if (unlikely(overflow))  
  26.             print_stack_overflow();  
  27.         desc->handle_irq(irq, desc);  
  28.     }  
  29.     irq_exit();  
  30.     set_irq_regs(old_regs);  
  31.     return 1;  
  32. }  

view plaincopy to clipboardprint?
  1. static inline int  
  2. execute_on_irq_stack(int overflow, struct irq_desc *desc, int irq)  
  3. {  
  4.     union irq_ctx *curctx, *irqctx;  
  5.     u32 *isp, arg1, arg2;  
  6.     curctx = (union irq_ctx *) current_thread_info();  
  7.     irqctx = hardirq_ctx[smp_processor_id()];  
  8.     /* 
  9.      * this is where we switch to the IRQ stack. However, if we are 
  10.      * already using the IRQ stack (because we interrupted a hardirq 
  11.      * handler) we can't do that and just have to keep using the 
  12.      * current stack (which is the irq stack already after all) 
  13.      */  
  14.     if (unlikely(curctx == irqctx))  
  15.         return 0;  
  16.     /* build the stack frame on the IRQ stack */  
  17.     isp = (u32 *) ((char*)irqctx + sizeof(*irqctx));  
  18.     irqctx->tinfo.task = curctx->tinfo.task;  
  19.     irqctx->tinfo.previous_esp = current_stack_pointer;  
  20.     /* 
  21.      * Copy the softirq bits in preempt_count so that the 
  22.      * softirq checks work in the hardirq context. 
  23.      */  
  24.     irqctx->tinfo.preempt_count =  
  25.         (irqctx->tinfo.preempt_count & ~SOFTIRQ_MASK) |  
  26.         (curctx->tinfo.preempt_count & SOFTIRQ_MASK);  
  27.     if (unlikely(overflow))  
  28.         call_on_stack(print_stack_overflow, isp);  
  29.     asm volatile("xchgl %%ebx,%%esp \n"  
  30.              "call  *%%edi      \n"  
  31.              "movl  %%ebx,%%esp \n"  
  32.              : "=a" (arg1), "=d" (arg2), "=b" (isp)  
  33.              :  "0" (irq),   "1" (desc),  "2" (isp),  
  34.             "D" (desc->handle_irq)  
  35.              : "memory""cc""ecx");  
  36.     return 1;  
  37. }  

最后,思考一下标题中的问题:linux内核在中断路径内不能睡眠/调度的原因

Linux是以进程为调度单位的,调度器只看到进程内核栈,而看不到中断栈。在独立中断栈的模式下,如果linux内核在中断路径内发生了调度(从技术上讲,睡眠和调度是一个意思),那么linux将无法找到“回家的路”,未执行完的中断处理代码将再也无法获得执行机会。文章来自:http://www.cnblogs.com/hainange/




先把中断处理流程给出来

1.进入中断处理程序--->2.保存关键上下文---->3.开中断(sti指 令)--->4.进入中断处理程序的handler--->5.关中断(cli指令)---->6.写EOI寄存器(表示中断处理完 成)---->7.开中断。

硬中断:
对应于上图的1、2、3步骤,在这几个步骤中,所有中断是被屏蔽的,如果在这个时候睡眠了,操作系统不会收到任何中断(包括时钟中断),系统就基本处于瘫痪状态(例如调度器依赖的时钟节拍没有等等……)

软中断:
对应上图的4(当然,准确的说应该是4步骤的后面一点)。这个时候不能睡眠的关键是因为上下文。
大家知道操作系统以进程调度为单位,进程的运行在进程的上下文中,以进程描述符作为管理的数据结构。进程可以睡眠的原因是操作系统可以切换不同进程的上下文,进行调度操作,这些操作都以进程描述符为支持。
中断运行在中断上下文,没有一个所谓的中断描述符来描述它,它不是操作系统调度的单位。一旦在中断上下文中睡眠,首先无法切换上下文(因为没有中断描述符,当前上下文的状态得不到保存),其次,没有人来唤醒它,因为它不是操作系统的调度单位。
此外,中断的发生是非常非常频繁的,在一个中断睡眠期间,其它中断发生并睡眠了,那很容易就造成中断栈溢出导致系统崩溃。

如 果上述条件满足了(也就是有中断描述符,并成为调度器的调度单位,栈也不溢出了,理论上是可以做到中断睡眠的),中断是可以睡眠的,但会引起很多问题.例 如,你在时钟中断中睡眠了,那操作系统的时钟就乱了,调度器也失去了依据;例如,你在一个IPI(处理器间中断)中,其它CPU都在死循环等你答复,你确 睡眠了,那其它处理器也不工作了;例如,你在一个DMA中断中睡眠了,上面的进程还在同步的等待I/O的完成,性能就大大降低了……还可以举出很多例子。 所以,中断是一种紧急事务,需要操作系统立即处理,不是不能做到睡眠,是它没有理由睡眠。

0 0