Linux内核2.6引入了一个新的每线程字段--- preempt_count ---只要获取/释放锁,就会递增/递减。此字段用于允许内核抢占:"如果设置了need_resched且preempt_count为零,则可以运行更重要的任务,并且可以安全地抢占。"
根据Robert Love的"Linux Kernel Development" book: "那么重新安排什么时候安全?只要内核没有锁定,内核就能够抢占在内核中运行的任务。"
我的问题是:为什么在此任务持有锁定时抢占内核中运行的任务是否安全?
如果安排了另一个任务并尝试抓住锁,它将阻塞(或旋转直到其时间片结束),因此我们不会在同一个临界区内同时获得两个线程。任何人都可以概述一个有问题的场景,以防我们抢占一个在内核模式下锁定的任务吗?
谢谢!
答案 0 :(得分:1)
在@Tsyvarev的帮助下,我想我现在可以回答我自己的问题,并描述一个有问题的场景,我们在这个场景中抢占了一个在内核模式下锁定的任务。
现在,如果线程#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序列化。