【问题标题】:Why disabling interrupts disables kernel preemption and how spin lock disables preemption为什么禁用中断会禁用内核抢占以及自旋锁如何禁用抢占
【发布时间】:2014-01-13 05:17:05
【问题描述】:

最近在看Linux Kernel Development,有几个关于禁用抢占的问题。

  1. 在第 7 章的“中断控制”部分,它说:

    此外,禁用中断也会禁用内核抢占。

    我还从书中读到内核抢占可能发生在以下情况:

    当中断处理程序退出时,在返回内核空间之前。
    当内核代码再次变得可抢占时。
    如果内核中的任务显式调用 schedule()
    如果内核中的任务阻塞(导致调用 schedule())

    但我无法将禁用中断与这些情况联系起来。

  2. 据我所知,自旋锁会通过 preempt_disable() 函数禁用抢占。

    帖子What exactly are "spin-locks"? 说:

    在单核机器上,自旋锁只是“禁用中断”或“提高 IRQL”,它完全阻止线程调度。

    preempt_disable() 是否通过禁用中断来禁用抢占?

【问题讨论】:

    标签: linux linux-kernel interrupt spinlock preemption


    【解决方案1】:

    我不是调度专家,但我想解释一下我的看法。 这里有几件事。

    1. preempt_disable() 不禁用 IRQ。它只是增加了一个thread_info->preempt_count 变量。
    2. 禁用中断也会禁用抢占,因为在此之后调度程序不再工作 - 但仅在单 CPU 机器上。在 SMP 上,这还不够,因为当您关闭一个 CPU 上的中断时,另一个/其他 CPU 仍会异步执行/执行某些操作。
    3. 大锁(意味着 - 关闭所有 CPU 上的所有中断)正在显着降低系统速度 - 所以这就是它不再使用的原因。这也是 preempt_disable() 不关闭 IRQ 的原因。

    你可以看到 preempt_disable() 是什么。尝试这个: 1. 获得一个自旋锁。 2.调用schedule()

    在 dmesg 中,您会看到类似“BUG:原子时调度”的内容。当调度程序检测到您的进程处于原子(非抢占式)上下文但它自行调度时,就会发生这种情况。

    祝你好运。

    【讨论】:

    • 感谢您的详细解答。但是我仍然不明白为什么在单CPU机器上禁用中断后调度程序不工作。
    • 无中断 - 无时钟。没有时钟 - 没有计时器。
    • 如果我只禁用抢占(不禁用中断)并同时在系统中发生中断会产生什么影响,禁用中断后又会发生什么?在这种情况下,“preempt_count”变量会更有效。 (研究arm中的中断处理)
    • @Sebastian Mountaniol:“大锁(意味着关闭所有 CPU 上的所有中断)”你能解释一下吗?大锁实际上是一把锁,尽管它是所有 LWP 的全局锁。但这并不等于禁用中断。
    【解决方案2】:

    在我编写的用于监视/分析任务的测试内核模块中,我尝试通过以下方式禁用中断:

    1 - 使用 local_irq_save()

    2 - 使用 spin_lock_irqsave()

    3 - 手动 disable_irq() 到 /proc/interrupts 中的所有 IRQ

    在所有 3 种情况下,我仍然可以使用 hrtimer 来测量时间,即使 IRQ 被禁用(并且我正在监视的任务也被抢占)。

    我觉得这个 veeeeerrrryyyy 很奇怪......我个人期待 Sebastian Mountaniol 指出的 -> 没有中断 - 没有时钟。没有时钟 - 没有计时器...

    Linux kernel 2.6.32 on a single core, single CPU... 谁能有更好的解释?

    【讨论】:

    • 我认为一些 Linux 内核定时器使用了最后一个定时器中断加上 CPU 周期定时器的技巧。所以你会得到准确的时间,直到循环计数器溢出。
    【解决方案3】:
    1. preempt_disable() 不会禁用中断。但是,它会增加抢占计数器的计数。假设您在代码路径中调用了 n 次 preempt_disable(),抢占只会在第 n 次 preempt_enable() 时启用。
    2. 禁用中断以防止抢占:不是一种安全的方法。这无疑会禁用正常的内核抢占,因为scheduler_tick() 不会在系统滴答时被调用(没有调用中断处理程序)。但是,如果程序触发了调度函数,如果preempt_disable()没有被调用,就会发生抢占。
    3. 在 linux 中,raw_spin_lock() 不会禁用可能导致死锁的本地中断。例如,如果调用中断处理程序并尝试锁定已持有的自旋锁,则除非进程本身释放它,否则它将无法锁定,这是不可能的,因为不会发生中断返回。 所以,最好使用raw_spin_lock_irq(),它会禁用中断。

    【讨论】:

      【解决方案4】:

      中断禁用会禁用某些形式的内核抢占,但内核抢占可以通过其他方式发生。因此,禁用中断并不是防止内核抢占的安全方法。

      例如,在禁用中断的情况下,cond_resched() 仍会导致抢占,但如果显式禁用抢占则不会。

      这就是为什么,关于你的第二个问题,自旋锁不使用中断禁用来禁用抢占。它们显式调用 preempt_disable() 来增加 preempt_count,并禁用所有可能发生抢占的方式,除非显式调用 schedule()。

      【讨论】:

        猜你喜欢
        • 2017-03-12
        • 2016-02-25
        • 1970-01-01
        • 1970-01-01
        • 2011-03-23
        • 1970-01-01
        • 2018-09-16
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多