51

我正在阅读 Robert Love 的以下文章

http://www.linuxjournal.com/article/6916

说的是

“......让我们讨论一下工作队列在进程上下文中运行的事实。这与其他所有在中断上下文中运行的下半部分机制形成对比。在中断上下文中运行的代码无法休眠或阻塞,因为中断上下文没有可重新调度的后备进程。因此,由于中断处理程序与进程无关,调度程序没有任何东西可以进入睡眠状态,更重要的是,调度程序没有任何东西可以唤醒......“

我不明白。AFAIK,内核中的调度程序是 O(1),这是通过位图实现的。那么是什么阻止调度程序将中断上下文置于睡眠状态并采取下一个可调度进程并将控制权传递给它呢?

4

11 回答 11

44

那么是什么阻止调度程序将中断上下文置于睡眠状态并采取下一个可调度进程并将控制权传递给它呢?

问题是中断上下文不是一个进程,因此不能进入睡眠状态。

当中断发生时,处理器将寄存器保存到堆栈中并跳转到中断服务程序的开始处。这意味着当中断处理程序运行时,它在中断发生时正在执行的进程的上下文中运行。中断正在该进程的堆栈上执行,当中断处理程序完成时,该进程将继续执行。

如果您尝试在中断处理程序中休眠或阻塞,您不仅会停止中断处理程序,还会停止它中断的进程。这可能很危险,因为中断处理程序无法知道被中断的进程在做什么,或者即使该进程被挂起是安全的。

一个可能出错的简单场景是中断处理程序和它中断的进程之间的死锁。

  1. Process1进入内核模式。
  2. Process1获得LockA
  3. 发生中断。
  4. ISR 使用Process1的堆栈开始执行。
  5. ISR 尝试获取LockA
  6. ISR 调用 sleep 等待LockA被释放。

在这一点上,你有一个死锁。 Process1无法恢复执行,直到 ISR 完成其堆栈。但是 ISR 被阻止等待Process1释放LockA

于 2009-06-27T21:10:49.537 回答
35

我认为这是一个设计理念。

当然,您可以设计一个可以在中断中休眠的系统,但是除了使系统难以理解和复杂(许多情况您必须考虑)之外,这无济于事。所以从设计的角度来看,将中断处理程序声明为不能休眠是非常明确且易于实现的。


来自 Robert Love(内核黑客): http: //permalink.gmane.org/gmane.linux.kernel.kernelnewbies/1791

您不能在中断处理程序中休眠,因为中断没有后备进程上下文,因此没有什么可以重新安排回去。换句话说,中断处理程序与任务无关,因此没有什么可以“进入睡眠”和(更重要的是)“没有什么可以唤醒”。它们必须以原子方式运行。

这与其他操作系统没有什么不同。在大多数操作系统中,中断不是线程化的。然而,下半部分通常是。

页面错误处理程序可以休眠的原因是它仅由在进程上下文中运行的代码调用。因为内核自己的内存是不可分页的,只有用户空间的内存访问会导致页面错误。因此,只有少数几个特定的​​地方(例如对 copy_{to,from}_user() 的调用)会导致内核中的页面错误。这些地方都必须由可以休眠的代码构成(即进程上下文、无锁等)。

于 2009-06-29T05:37:23.437 回答
6

因为此时线程切换基础设施不可用。处理中断时,只有更高优先级的东西才能执行 - 请参阅英特尔软件开发人员手册关于中断、任务和处理器优先级。如果您确实允许另一个线程执行(您在问题中暗示这很容易做到),您将无法让它做任何事情 - 如果它导致页面错误,您将不得不使用服务在处理中断时无法使用的内核中(原因见下文)。

通常,您在中断例程中的唯一目标是让设备停止中断并在较低的中断级别排队(在 unix 中,这通常是非中断级别,但对于 Windows,它是调度、apc 或被动级别)到在您可以访问内核/操作系统的更多功能的地方做繁重的工作。请参阅 -实现处理程序

这是操作系统必须如何工作的特性,而不是 Linux 固有的特性。中断例程可以在任何时候执行,因此您中断的状态是不一致的。如果你中断了线程调度代码,它的状态是不一致的,所以你不能确定你可以“休眠”并切换线程。即使您保护线程切换代码不被中断,线程切换也是 O/S 的一个非常高级的功能,如果您保护了它所依赖的所有内容,那么中断更像是一种建议,而不是其名称所暗示的命令。

于 2009-06-27T20:43:01.433 回答
3

那么是什么阻止调度程序将中断上下文置于睡眠状态并采取下一个可调度进程并将控制权传递给它呢?

调度发生在定时器中断上。基本规则是一次只能打开一个中断,因此如果您在“从设备 X 获取数据”中断中进入睡眠状态,则无法运行定时器中断来调度它。

中断也会发生多次并重叠。如果你让“得到数据”中断休眠,然后得到更多的数据,会发生什么?包罗万象的规则是:不要在中断中休眠。你会做错的。

于 2009-06-27T22:52:24.980 回答
2

即使您可以让 ISR 进入睡眠状态,您也不会想要这样做。您希望 ISR 尽可能快,以降低错过后续中断的风险。

于 2009-07-30T17:56:10.197 回答
2

不允许中断处理程序阻塞是一种设计选择。当设备上有数据时,中断处理程序拦截当前进程,准备数据传输并启用中断;在处理程序启用当前中断之前,设备必须挂起。我们想让我们的 I/O 保持忙碌并且我们的系统响应,那么我们最好不要阻塞中断处理程序。

我不认为“不稳定的状态”是一个本质原因。进程,无论它们处于用户模式还是内核模式,都应该意识到它们可能会被中断中断。如果中断处理程序和当前进程都将访问某些内核模式数据结构,并且存在竞争条件,则当前进程应禁用本地中断,而且对于多处理器架构,应在临界区使用自旋锁.

我也不认为如果中断处理程序被阻塞,它就不能被唤醒。当我们说“阻塞”时,基本上意味着被阻塞的进程正在等待某个事件/资源,因此它将自己链接到该事件/资源的某个等待队列中。每当资源被释放时,释放进程负责唤醒等待进程。

然而,真正令人讨厌的是,被阻塞的进程在阻塞时间内什么都不做;这种惩罚没有错,这是不公平的。并且没有人可以肯定地预测阻塞时间,因此无辜的过程必须等待不明原因和无限时间。

于 2011-01-16T23:26:31.483 回答
1

linux内核有两种分配中断栈的方法。一个在被中断进程的内核堆栈上,另一个是每个 CPU 的专用中断堆栈。如果中断上下文保存在每个 CPU 的专用中断堆栈上,那么实际上中断上下文完全不与任何进程相关联。“当前”宏将产生一个指向当前运行进程的无效指针,因为具有某些体系结构的“当前”宏是使用堆栈指针计算的。中断上下文中的堆栈指针可能指向专用的中断堆栈,而不是某个进程的内核堆栈。

于 2010-08-03T19:25:17.090 回答
1

从本质上讲,问题是在中断处理程序中是否可以获得有效的“当前”(当前进程任务结构的地址),如果是,则可以相应地修改那里的内容以使其进入“睡眠”状态,可以是如果状态以某种方式发生变化,稍后由调度程序返回。答案可能取决于硬件。

但在 ARM 中,这是不可能的,因为“当前”与中断模式下的处理无关。请看下面的代码:

#linux/arch/arm/include/asm/thread_info.h 
94 static inline struct thread_info *current_thread_info(void)
95 {
96  register unsigned long sp asm ("sp");
97  return (struct thread_info *)(sp & ~(THREAD_SIZE - 1));
98 }

USER模式下的sp和SVC模式下的sp是“相同的”(这里的“相同”并不是说它们相等,而是用户模式的sp指向用户空间栈,而svc模式的sp r13_svc指向内核栈,用户进程的task_structure在上一次任务切换时更新,当系统调用发生时,进程再次进入内核空间,当sp(sp_svc)仍然没有改变时,这2个sp是相互关联的,从这个意义上说,它们是相同的'),所以在 SVC 模式下,内核代码可以获得有效的 'current'。但在其他特权模式下,比如中断模式,sp 是“不同的”,指向 cpu_init() 中定义的专用地址。在这些模式下计算的“电流”将与被中断的进程无关,访问它会导致意外的行为。那'

于 2013-09-27T10:38:00.017 回答
0

高级中断处理程序屏蔽所有低优先级中断的操作,包括系统定时器中断的操作。因此,中断处理程序必须避免让自己参与可能导致其休眠的活动。如果处理程序休眠,那么系统可能会挂起,因为定时器被屏蔽并且无法调度休眠线程。这有意义吗?

于 2009-07-30T17:50:36.980 回答
0

如果一个更高级别的中断程序在一段时间后到达它必须做的下一件事情必须发生的地步,那么它需要将一个请求放入定时器队列,要求运行另一个中断程序(以较低的优先级水平)一段时间后。

当该中断例程运行时,它会将优先级提升回原始中断例程的级别,并继续执行。这与睡眠具有相同的效果。

于 2009-07-30T17:57:59.150 回答
0

它只是 Linux 操作系统中的设计/实现选择。这种设计的优点是简单,但它可能不适合实时操作系统要求。

其他操作系统有其他设计/实现。

例如,在 Solaris 中,中断可以有不同的优先级,这允许大多数设备中断在中断线程中调用。中断线程允许睡眠,因为每个中断线程在线程上下文中都有单独的堆栈。中断线程设计适用于应该比中断具有更高优先级的实时线程。

于 2013-01-31T03:47:28.097 回答