Page fault in Interrupt context

5
一个页面错误可以发生在中断处理程序/原子上下文中吗?

这在音频驱动程序和其他使用mmap作为数据源的情况下很常见。页面错误处理程序中有特定的代码来注意先前的IRQ状态。任何copy_from_user()copy_to_user()都可能会出错。内核代码本身是固定的,因此只应发生数据异常数据故障 - artless noise
5个回答

6
它可以做到,但那将是一场灾难。 :-)

@Bandicoot:相信这个人,他是一个Linux内核开发者。 - ninjalj
2
@ninjalj:如果答案有帮助的话,相信起来会更容易些。 - Bandicoot
3
当访问的虚拟地址映射到不存在的页面时,会发生页面错误(Pagefault)。在中断上下文中,这意味着存在内核错误。与用户空间访问不同,那时进程被终止,而终止中断处理程序则是一个疯狂的选择,很可能导致硬件状态不确定。因此,内核会崩溃,CPU会被锁定,需要重新启动系统。 - adobriyan

3
(这是一个有些陈旧的问题。现有的答案包含了正确的事实,但都比较简单。我将尝试更全面地回答这个问题。)
这个问题的答案取决于代码是在内核(监管模式)还是用户模式下运行。原因是这些区域的内存访问规则通常不同。以下是一个简要的事件序列以说明问题(假设内核内存可以分页):
1. 当用户程序执行时,会发生中断(例如按键/磁盘事件)。 2. CPU转换到监管模式并开始执行内核中的处理程序。 3. 中断处理程序开始保存CPU状态(以便稍后正确地恢复用户进程),但在执行此操作时,它会触及一些之前已经分页的存储。 4. 这会引发页面错误异常。 5. 为了处理页面错误异常,内核现在必须保存经历页面缺失的代码的CPU状态。 6. 如果它有一个预先分配的永远不会分页的内存池,它实际上可能能够完成此操作,但这样的内存池大小必然有限。
所以你看,最安全(也是最简单)的解决方案是,内核确保内核拥有的内存根本无法分页。因此,页面错误实际上不应该在内核中发生。它们可能会发生,但正如@adobriyan所指出的那样,这通常表示一个比简单地需要分页一些内存更大的错误。(我相信在Linux中是这种情况。检查你特定的操作系统以确保内核内存是不可分页的。操作系统的体系结构确实有所不同。)
因此,总之,内核内存通常不可分页,并且由于中断通常在内核中处理,因此在服务中断时通常不会发生页面错误。高优先级中断仍然可以打断低优先级中断。只是它们所有的资源都保存在物理内存中。
关于原子上下文的问题不太清楚。如果您指的是硬件支持的原子操作,则在操作的部分完成过程中不会发生中断。如果您指的是类似于临界区域的东西,请记住,临界区域仅模拟原子性。从硬件的角度来看,这样的代码没有什么特别之处,除了进入和退出代码可能使用真正的硬件原子操作。其中间的代码是普通的代码,可以被打断。
希望这对您有用,因为我也曾经对这个问题感到困惑了一段时间。

例如,我认为bitset代码应该是原子的。这些数组可能会在访问时引起页面错误,在没有原子基元的平台上中断掩码。 - artless noise
Linux内核内存不可分页,但通过vmalloc分配的内存仍可能导致页面故障,因此如果在中断中使用了vmalloced内存,则可能会导致页面故障。 - Yusuf Khan

1

是的。

处理程序或关键区域的代码可能跨越两个页面之间的边界。如果第二页不可用,则需要发生页面错误来将其带入。


但是在中断/原子上下文中禁止休眠。调用页面故障处理程序可能会导致进程休眠。因此,在中断上下文中允许页面故障不会导致死锁吗? - Bandicoot
7
正是因为这个原因,Linux内核是不可交换的。 :) - sarnold

1

0

我认为答案是肯定的。 我刚刚检查了在x86_64平台上内核4.15中的页面错误处理程序代码。 以下是一个提示。 no_context是经典的'内核 oops'。

no_context(struct pt_regs *regs, unsigned long error_code,
           unsigned long address, int signal, int si_code)
{

        /* Are we prepared to handle this kernel fault? */
        if (fixup_exception(regs, X86_TRAP_PF)) {
                /*
                 * Any interrupt that takes a fault gets the fixup. This makes
                 * the below recursive fault logic only apply to a faults from
                 * task context.
                 */
                if (in_interrupt())
                        return;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接