[笨叔点滴12]面试必考:如果在中断处理函数里发生了缺页中断会怎样?为什么?

Posted 奔跑吧Linux社区

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[笨叔点滴12]面试必考:如果在中断处理函数里发生了缺页中断会怎样?为什么?相关的知识,希望对你有一定的参考价值。

 小明同学去BAT公司面试。

面试官:叫神马名字

小明:小明

面试官:啥是时钟中断?

小明:就是手表坏了,不跑了

面试官: ???这也行

面试官接着问:如果时钟中断处理程序发生了缺页中断,怎么办?

小明嘟囔:咋手表坏了还会缺液,这啥手表。。。液压手表?要加几号油...

面试官:小明同学,滚。。。


上面是一个段子,但是这是一个面试必考题目,考察大家对中断和异常处理的理解,如果你对中断和异常理解不透彻,很难胜任Linux相关的研发工作的。如果能把这个问题的来龙去脉想出来,去面试BAT或者其他大公司一点问题都木有。


恰巧,今天在笨叔的VIP奔跑群里,有人问了这样一个问题,在中断处理函数ISR中,可以发生缺页中断吗?发生缺页中断会怎么样?


\'[笨叔点滴12]面试必考:如果在中断处理函数里发生了缺页中断会怎样?为什么?_中断处理\'

\'[笨叔点滴12]面试必考:如果在中断处理函数里发生了缺页中断会怎样?为什么?_中断处理_02\'


01 啥是缺页中断?



首先我们先搞清楚两个事情:

  1. 一个是中断
  2. 另外一个是缺页中断

他们是否一样都是中断呢?


答案显然是否定的。中断和缺页中断是兄弟,但是不是同一个人。在大部分的体系结构里,缺页中断其实是缺页异常,英文叫做“Page Fault”,笨叔不知道从哪个教材开始“page fault”都清一色叫做缺页中断,其实翻译成缺页异常会更合适,否则学生还天真的认为缺页中断和普通的外设中断没啥两样。


在ARM32处理器里中断和异常是分的比较清楚的,中断有IRQ和FIQ中断,我们常说的普通的外设中断就是IRQ中断。而异常,分成data abort、undefine abort、预取异常这几种异常。大家从异常向量表就可以看出,当中断和异常发生的时候,他们的处理的入口是不一样的。


\'[笨叔点滴12]面试必考:如果在中断处理函数里发生了缺页中断会怎样?为什么?_中断处理_03\'


当一个外设中断发生的时候,它的处理步骤是这样的:

  1. 跑到IRQ的向量表里
  2. 在vector_IRQ汇编函数,保存当前lr和spsr到中断栈
  3. 跳转到SVC模式
  4. 在SVC模式里,判断中断发生在内核态还是用户态
  5. 保存中断发生那个时间点上下文到进程的内核栈里(SVC模式)
  6. 跳转到do_IRQ函数里去执行中断处理函数
  7. 判断是否需要抢占
  8. 中断返回


上面是笨叔总结的IRQ中断发生,上半部处理要经历的一些事情(我们这里讨论忽略了中断下半部)。这次点滴那个小伙伴问的问题是,当在第6步的时候发生了缺页中断。也就是在do_IRQ函数里,访问的内存不存在,或者页的属性不对,导致发生了一个缺页异常,那怎么办?


经过笨叔上面这么一分析,我们就get到了这个问题的本质了。


02 不可以哟



假设在中断过程的第6步发生了缺页异常,你访问了不该访问的内存,捅了马蜂窝,那会发生什么事情?  (在ARM处理器里大概有两种的缺页异常,一种是进程地址空间的缺页异常do_page_fault,另外一种是vmalloc的缺页异常do_translation_fault。后者比较简单,vmalloc缺页中断仅仅是把init_task进程的页表拷贝到当前进程而已,我们暂时考虑更为复杂的前者)。


首先一点,异常处理的汇编代码部分和中断是差不多的,都是通过异常模式的栈,然后跳转到当前进程的内核栈里面。但是异常模式有一个不一样的地方,就是处理过程是开中断和允许睡眠和调度的。


所以当在外设中断ISR处理过程中发生异常,会出现下面情况。

\'[笨叔点滴12]面试必考:如果在中断处理函数里发生了缺页中断会怎样?为什么?_中断处理_04\'


如图所示,在发生中断的那个current进程的内核栈里,会出现一个一个栈框,最上面的是发生中断那个现场点的上下文的栈框,接着是do_IRQ的栈框,接着是具体硬件外设ISR处理函数的栈框。 接下来就是发生异常时候,保存下来的栈框。


由于在异常处理函数里,它是允许开中断的和睡眠的,那“层层嵌套”,套路太深,猴年马月才能返回到 最初那个中断上下文呢?


\'[笨叔点滴12]面试必考:如果在中断处理函数里发生了缺页中断会怎样?为什么?_linux_05\'


如图所示,在①这个地方发生了中断A,然后在保存了这个现场的上下文。然后在②这个地方发生了异常,也保存了这个现场的上下文,在异常处理过程③的地方,又发生了中断B,这时候保存发生中断B的上下文。


  1. 假设中断B返回的时候发生了调度,那猴年马月能返回到③这个地方呢?
  2. 假设在异常处理的时候发生了调度,也是一样的,猴年马月能返回到③这里呢?


上面场景发生的话,中断A啥时候才能返回呢?因为中断控制器还在等待程序媛给他一个吻,这个吻叫做“EOI”,英文叫做“end of interrupt”。也许望穿秋水了,也等不到这个吻了。。。


上面场景如果发生在时钟中断里的话,大家想想会有啥后果?

所以,为了防止异常处理发生的套路很深的圈套,我们看一下Linux内核做了哪些限制?

\'[笨叔点滴12]面试必考:如果在中断处理函数里发生了缺页中断会怎样?为什么?_linux_06\'


在do_page_fault()函数里,有判断当前上下文是否在中断上下文中,通过in_atomic()函数来判断。如果在中断上下文里,就跑到do_kernel_fault里,通常就打印oops错误了。


那in_atomic()怎么判断当前是否在中断上下文呢?

\'[笨叔点滴12]面试必考:如果在中断处理函数里发生了缺页中断会怎样?为什么?_中断处理_07\'


这里主要是去判断thread_info里面的preempt_count计数。

\'[笨叔点滴12]面试必考:如果在中断处理函数里发生了缺页中断会怎样?为什么?_中断处理_08\'


那中断的处理过程是否设置了preempt_count计数呢?


IRQ的处理过程:irq_handle-> gic_handle_irq()->handle_domain_irq()->irq_enter->_irq_enter

\'[笨叔点滴12]面试必考:如果在中断处理函数里发生了缺页中断会怎样?为什么?_中断处理_09\'

这回我们终于知道了IRQ中断处理会设置preempt_count计数的HARDIRQ_OFFSET这个域了。

以上是关于[笨叔点滴12]面试必考:如果在中断处理函数里发生了缺页中断会怎样?为什么?的主要内容,如果未能解决你的问题,请参考以下文章

[笨叔点滴13]哪些异常处理的事儿

[笨叔点滴10] 中断到“底”来了吗?

[笨叔点滴14] ARMv8里异常处理哪些蛇神牛鬼

[笨叔点滴15]ARMv8里异常处理哪些蛇神牛鬼 2

[笨叔点滴9] GNU GCC扩展2

[笨叔点滴11] malloc惹的祸