共计 2339 个字符,预计需要花费 6 分钟才能阅读完成。
“小明同学去 BAT 公司面试。
面试官:叫神马名字
小明:小明
面试官:啥是时钟中断?
小明:就是手表坏了,不跑了
面试官:???这也行
面试官接着问:如果时钟中断处理程序产生了缺页中断,怎么办?
小明嘟囔:咋手表坏了还会缺液,这啥手表。。。液压手表?要加几号油 …
面试官:小明同学,滚。。。
”
下面是一个段子,然而这是一个面试必考题目,考查大家对中断和异样解决的了解,如果你对中断和异样了解不透彻,很难胜任 Linux 相干的研发工作的。如果能把这个问题的前因后果想进去,去面试 BAT 或者其余大公司一点问题都木有。
凑巧,明天在笨叔的 VIP 奔跑群里,有人问了这样一个问题,在中断处理函数 ISR 中,能够产生缺页中断吗?产生缺页中断会怎么样?
[如果你也想参加到 VIP 奔跑群里和笨叔进行交换,请点击“浏览原文”订阅旗舰篇]
01 啥是缺页中断?
首先咱们先搞清楚两个事件:
一个是中断
另外一个是缺页中断
他们是否一样都是中断呢?
答案显然是否定的。中断和缺页中断是兄弟,然而不是同一个人。在大部分的体系结构里,缺页中断其实是缺页异样,英文叫做“Page Fault”,笨叔不晓得从哪个教材开始“page fault”都清一色叫做缺页中断,其实翻译成缺页异样会更适合,否则学生还天真的认为缺页中断和一般的外设中断没啥两样。
在 ARM32 处理器里中断和异样是分的比较清楚的,中断有 IRQ 和 FIQ 中断,咱们常说的一般的外设中断就是 IRQ 中断。而异样,分成 data abort、undefine abort、预取异样这几种异样。大家从异样向量表就能够看出,当中断和异样产生的时候,他们的解决的入口是不一样的。
当一个外设中断产生的时候,它的解决步骤是这样的:
跑到 IRQ 的向量表里
在 vector_IRQ 汇编函数,保留以后 lr 和 spsr 到中断栈
跳转到 SVC 模式
在 SVC 模式里,判断中断产生在内核态还是用户态
保留中断产生那个工夫点上下文到过程的内核栈里(SVC 模式)
跳转到 do_IRQ 函数里去执行中断处理函数
判断是否须要抢占
中断返回
下面是笨叔总结的 IRQ 中断产生,上半部解决要经验的一些事件(咱们这里探讨疏忽了中断下半部)。这次点滴那个小伙伴问的问题是,当在第 6 步的时候产生了缺页中断。也就是在 do_IRQ 函数里,拜访的内存不存在,或者页的属性不对,导致产生了一个缺页异样,那怎么办?
通过笨叔下面这么一剖析,咱们就 get 到了这个问题的实质了。
[对于中断解决的全过程,倡议大家去看《奔跑吧 Linux 内核》第 5.1 章内容]
02 不能够哟
假如在中断过程的第 6 步产生了缺页异样,你拜访了不该拜访的内存,捅了马蜂窝,那会产生什么事件?(在 ARM 处理器里大略有两种的缺页异样,一种是过程地址空间的缺页异样 do_page_fault,另外一种是 vmalloc 的缺页异样 do_translation_fault。后者比较简单,vmalloc 缺页中断仅仅是把 init_task 过程的页表拷贝到以后过程而已,咱们临时思考更为简单的前者)。
首先一点,异样解决的汇编代码局部和中断是差不多的,都是通过异样模式的栈,而后跳转到以后过程的内核栈外面。然而异样模式有一个不一样的中央,就是处理过程是开中断和容许睡眠和调度的。
所以当在外设中断 ISR 处理过程中产生异样,会呈现上面状况。
如图所示,在产生中断的那个 current 过程的内核栈里,会呈现一个一个栈框,最下面的是产生中断那个现场点的上下文的栈框,接着是 do_IRQ 的栈框,接着是具体硬件外设 ISR 处理函数的栈框。接下来就是产生异样时候,保留下来的栈框。
因为在异样处理函数里,它是容许开中断的和睡眠的,那“层层嵌套”,套路太深,猴年马月能力返回到 最后那个中断上下文呢?
如图所示,在①这个中央产生了中断 A,而后在保留了这个现场的上下文。而后在②这个中央产生了异样,也保留了这个现场的上下文,在异样处理过程③的中央,又产生了中断 B,这时候保留产生中断 B 的上下文。
假如中断 B 返回的时候产生了调度,那猴年马月能返回到③这个中央呢?
假如在异样解决的时候产生了调度,也是一样的,猴年马月能返回到③这里呢?
下面场景产生的话,中断 A 啥时候能力返回呢?因为中断控制器还在期待程序媛给他一个吻,这个吻叫做“EOI”,英文叫做“end of interrupt”。兴许望眼欲穿了,也等不到这个吻了。。。
下面场景如果产生在时钟中断里的话,大家想想会有啥结果?
所以,为了避免异样解决产生的套路很深的陷阱,咱们看一下 Linux 内核做了哪些限度?
在 do_page_fault() 函数里,有判断以后上下文是否在中断上下文中,通过 in_atomic() 函数来判断。如果在中断上下文里,就跑到 do_kernel_fault 里,通常就打印 oops 谬误了。
那 in_atomic() 怎么判断以后是否在中断上下文呢?
这里次要是去判断 thread_info 外面的 preempt_count 计数。
[这里 preempt_count 计数是啥意思呢?笨叔在这里就不解释了,倡议大家去看《奔跑吧 Linux 内核》第 3 章的图 3.2]
那中断的处理过程是否设置了 preempt_count 计数呢?
IRQ 的处理过程:irq_handle-> gic_handle_irq()->handle_domain_irq()->irq_enter->_irq_enter
这回咱们终于晓得了 IRQ 中断解决会设置 preempt_count 计数的 HARDIRQ_OFFSET 这个域了。
[对于这部分,还不分明的小伙伴能够看《奔跑吧 Linux 内核》第 5.1.5 章节]
奔跑吧 Linux 社区保持原创文章,原汁原味,绝不转载!喜爱的话,记得打赏和转发,谢谢!
订阅奔跑吧 Linux 社区旗舰篇视频,与笨叔一起交换和奔跑!