为什么内核抢占只有在preempt_count>时才是安全的0?

时间:2017-11-27 18:27:36

标签: multithreading linux-kernel preemption

Linux内核2.6引入了一个新的每线程字段--- preempt_count ---只要获取/释放锁,就会递增/递减。此字段用于允许内核抢占:"如果设置了need_resched且preempt_count为零,则可以运行更重要的任务,并且可以安全地抢占。"

根据Robert Love的"Linux Kernel Development" book: "那么重新安排什么时候安全?只要内核没有锁定,内核就能够抢占在内核中运行的任务。"

我的问题是:为什么在此任务持有锁定时抢占内核中运行的任务是否安全?

如果安排了另一个任务并尝试抓住锁,它将阻塞(或旋转直到其时间片结束),因此我们不会在同一个临界区内同时获得两个线程。任何人都可以概述一个有问题的场景,以防我们抢占一个在内核模式下锁定的任务吗?

谢谢!

2 个答案:

答案 0 :(得分:1)

在@Tsyvarev的帮助下,我想我现在可以回答我自己的问题,并描述一个有问题的场景,我们在这个场景中抢占了一个在内核模式下锁定的任务。

  1. 线程#1持有自旋锁并被抢占。
  2. 然后安排线程#2,并旋转以抓住旋转锁定。
  3. 现在,如果线程#2是传统的过程,它最终将完成其时间片。在这种情况下,将再次安排线程#1,释放锁定,我们都很好。 但是,如果线程#2是具有更高优先级的实时进程,则线程#1将永远不会再次运行并且我们遇到了死锁。

    another stackoverflow thread引用了FreeBSD documentation

    ,证实了这个答案
      

    虽然锁定可以在抢占的情况下保护大多数数据,但不是全部   内核是抢占安全的。例如,如果一个线程持有   旋转互斥锁被抢占,新线程试图抓住同样的旋转   互斥,新线程可能永远旋转,因为被中断的线程可能   永远不会有机会执行。

    尽管上面的引用没有明确解释为什么"中断的线程可能永远不会有机会执行"试。

答案 1 :(得分:1)

虽然这是一个老问题,但接受的答案不正确。

首先标题是:

为什么仅当preempt_count> 0时内核抢占才是安全的?

这是不正确的,相反。当preempt_count> 0时禁用内核抢占,而当preempt_count == 0时启用内核抢占。

此外,索赔:

如果安排了另一个任务并尝试获取锁,它将阻塞(或旋转直到其时间片结束),

并非总是如此。

假设您获得了自旋锁。抢占已启用。发生进程切换,并且在新进程的上下文中运行了一些softirq。运行softirqs时,抢占被禁用。如果这些softirqs之一试图获取您的锁,则它将永远不会停止旋转,因为禁用了抢占。这样您就陷入僵局。

您无法控制取代您进程的进程是否运行softirqs。禁用softirqs的preempt_count字段是特定于进程的。 Softirq必须在禁用抢占的情况下运行,以保留softirq的按CPU序列化。