;关于自旋锁,但我仍然感到困惑。我认为这是因为问题和答案假设了不同的设置,或者当他们询问或回答时,没有清楚地解释是SMP还是抢占内核的设置(一些旧信息也混合在一起)。
我的第一个问题是:(Q1)在SMP情况下,schedule()
是否在每个处理器上同时运行(我知道调度是通过jiffies定时器中断开始的)?我会在下面的问题中假设是。如果有人能简单地向我解释一下进程在调度过程中是如何在处理器核心之间移动的,我将不胜感激。
我试图了解spin_lock/unlock_irqsave
是如何使用的,为什么使用。这是我的问题。
假设有一个代码调用spin_lock_irqsave
,并且在调用spin_lock_irqsave()
时中断状态(enable)为"disable"。这个代码可能是在中断上下文中运行的吗?可能不会,因为如果中断在相应的本地处理器中被禁用,ISR一开始就不应该启动。因此,调用spin_lock_irqsave
的代码必须在进程上下文中。好的,中断之前已经被禁用,但一个进程正在尝试用spin_lock_irqsave
锁定。
在什么情况下可以禁用中断?我认为有两种情况。
情况1:先前的中断例程已被此进程(调用此spin_lock_irqsave
)抢占。这很奇怪,因为ISR不能被抢占。(Q2)顺便问一下,在抢占内核中,ISR可以被进程抢占吗?(Q3)我想因为preempt_count()
和(current_thread_info()->preempt_count)
一样是#define
d,所以preempt_disable
只用于进程而不用于中断。中断是否也有当前线程信息?
情况2:先前的正常进程已获得具有spin_lock_irq
(或irqsave
)的锁。但这也很奇怪,因为在锁定之前,spin_lock_irq
(或irqsave
)禁用任务的抢占和中断,告诉调度器在调度器定时器中断后不要切换到其他任务。所以这种情况不可能是真的。
我知道我必须进一步研究SMP和内核抢占的进程调度,也许我误解了什么。有人能澄清一下我的问题吗?非常感谢您的阅读。
有很多问答;关于自旋锁,但我仍然感到困惑。我认为这是因为问题和答案假设了不同的设置,或者当他们询问或回答时,没有清楚地解释是SMP还是抢占内核的设置(一些旧信息也混合在一起)。
我只能同意。Spinlocks虽然本质上很简单,但当包含在现代Linux内核的上下文中时,它根本不是一个简单的主题。我不认为仅仅通过阅读随机和特定于大小写的Stack Overflow答案就可以很好地理解spinlock。
我强烈建议您阅读Linux设备驱动程序一书的第5章:并发和种族条件,该书在网上免费提供。特别是,第5章的"Spinlocks"部分非常有助于理解Spinlocks在不同情况下是如何有用的。
(Q1)在SMP情况下,
schedule()
是否在每个处理器上同时运行?[…]如果有人能向我简要解释一下进程在调度过程中是如何移动处理器核心的,我将不胜感激。
是的,如果你喜欢,你可以这样看。每个CPU(即每个处理器内核)都有自己的定时器,当在给定的CPU上引发定时器中断时,该CPU执行内核注册的定时器中断处理程序,内核调用调度器,调度器重新调度进程。
系统中的每个CPU都有自己的运行队列,其中包含处于可运行状态的任务。任何任务最多可以包含在一个运行队列中,并且不能同时在多个不同的CPU上运行。
任务的CPU相关性决定了任务可以在哪些CPU上运行。默认的"正常"相关性允许任务在任何CPU上运行(特殊配置除外)。根据它们的相关性,任务可以由调度程序从一个运行队列移动到另一个,也可以通过sched_setaffinity
系统调用(这里有一个相关的答案解释了如何移动)(如果需要的话)。
建议阅读:Linux进程调度的完整指南。
假设有一个代码调用
spin_lock_irqsave
,并且在调用spin_lock_irqsave()
时中断状态(enable)为"disable"。这个代码可能是在中断上下文中运行的吗?可能不会。
为什么不呢?这是可能的。该代码可以在中断上下文中运行,但不能由不同的中断调用。请看我答案的底部。
情况1:先前的中断例程已被此进程(调用此
spin_lock_irqsave
)抢占。这很奇怪,因为ISR不能被抢占。
你是对的,这很奇怪。更奇怪的是,这是不可能的。在Linux上,任何时候都可以启用或禁用中断(两者之间没有任何区别)。中断并没有真正的"优先级"(就像任务一样),但我们可以将它们分为两类:
- 不可抢占中断,必须在完全控制CPU的情况下从头到尾运行。这些中断使系统处于"禁用中断"状态,并且不会发生其他中断
- 可抢占中断,可重新进入并允许其他中断发生。如果在为该中断提供服务时发生另一个中断,则进入嵌套中断场景,该场景类似于任务的嵌套信号处理程序的场景
在您的情况下,由于中断之前已被禁用,这意味着如果禁用中断的代码是一个中断,则它是不可抢占的,因此它不可能被抢占。它也可能是一个可抢占中断,它正在执行需要禁用中断的关键代码部分,但情况仍然相同,您不能在另一个中断中。
(Q2)顺便说一句,在抢占内核中,ISR可以被进程抢占吗?
否。说"被进程抢先"是不恰当的。进程并没有真正抢占任何东西,它们被控制的内核抢占。也就是说,理论上,一个可抢占中断可能会被另一个进程注册的中断中断(不幸的是,我不知道这种情况的示例)。不过,我仍然不会称之为"进程抢占",因为整个过程都在内核空间中发生。
(Q3)[…]中断是否也有当前线程信息?
中断处理程序生活在另一个世界,它们不关心运行任务,也不需要访问这些信息。如果你真的想要,你可能会得到current
甚至current_thread_info
,但我怀疑这对任何事情都没有帮助。中断与任何任务都没有关联,中断与某个正在运行的任务之间没有联系。这里的另一个答案可供参考。
情况2:以前的正常进程已获取具有
spin_lock_irq
(或irqsave
)的锁。但这也很奇怪,因为在锁定之前,spin_lock_irq
(或irqsave
)禁用任务的抢占和中断,告诉调度器在调度器定时器中断后不要切换到其他任务。所以这种情况不可能是真的。
是的,你说得对。这是不可能的。
spin_lock_irqsave()
函数的存在是为了在您无法知道中断是否已被禁用的情况下使用,因此您不能使用spin_lock_irq()
后面跟着spin_unlock_irq()
,因为第二个函数将强制重新启用中断。顺便说一句,我在上面链接的Linux设备驱动程序的第5章中也对此进行了解释。
在您描述的场景中,您正在调用spin_lock_irqsave()
,并且中断已经被其他东西禁用。这意味着,任何最终调用当前函数的父调用方函数一定已经以某种方式禁用了中断。
以下情况是可能的:
-
最初的中断禁用是由中断处理程序引起的,现在您正在执行另一段代码作为同一中断处理程序的一部分(即当前函数已由中断处理进程本身直接或间接调用)。在中断处理程序正在调用的函数中,可以很好地调用
spin_lock_irqsave()
。或者甚至只是对local_irqsave()
的调用(例如,kfree()
函数就是这样做的,它肯定可以从中断上下文中调用)。 -
最初的中断禁用是由正常内核代码引起的,现在您正在执行另一段代码,作为同一正常内核代码的一部分(即,在禁用中断后,当前函数已被其他一些内核函数直接或间接调用)。这是完全可能的,事实上这就是
irqsave
变体存在的原因。