调度程序如何知道一个线程被阻塞等待输入?

How does the scheduler know that a thread is blocked waiting for input?

当执行用户代码的线程在等待输入时,调度器如何知道中断它或线程如何知道调用调度器,作为一个简单的单线程应用程序的普通程序员不太可能插入sched_yield() 无处不在。编译器是在优化时插入 sched_yield() 还是线程只是旋转锁定直到调度程序设置的通用定时器中断触发,或者用户是否必须显式声明 wait()、sleep() 函数以便要切换的上下文?

如果调度程序不是抢占式的,这个问题就特别重要,因为它必须在等待输入以使吞吐量生效时调用调度程序,但我不知道它是怎么做到的。

我不清楚你的问题是关于理论还是实践。实际上,在每个现代操作系统中,i/o 操作都是有特权的。这意味着为了让用户进程或线程访问文件、设备等,它必须发出系统调用。 然后内核有机会做任何它认为合适的事情。例如,它可以检查 I/o 操作是否会阻塞,因此在发出操作后切换 运行(即“调用”调度程序)进程。 请注意,即使内核没有处理定时器中断,此机制也可以工作。无论如何,总的来说,这将取决于您的系统。例如,在没有 OS 退出(或最小的)的嵌入式系统中,用户代码可能完全负责在发出阻塞操作之前调用调度程序。

注意不要将抢占与进程休眠的能力混淆。即使使用非抢占式调度程序,进程也可以休眠。这是进程等待 I/O 时发生的情况。该进程进行系统调用,例如 read(),并且设备确定没有可用数据。然后它通过更新调度程序使用的数据结构在内部使进程进入睡眠状态。调度程序然后执行其他进程,直到发生中断或某些其他事件来唤醒原始进程。唤醒的进程然后再次有资格进行调度。

另一方面,抢占 是体系结构的调度程序在没有其合作 的情况下停止进程执行的能力。中断可以发生在程序指令流的任何地方。将 return 控制到调度程序,然后可以执行其他进程,然后将 return 控制到被中断(抢占)的进程。大多数调度程序分配时间片,其中允许进程 运行 最多预定的时间量,之后如果优先级更高的进程需要时间片,它会被抢占。

除非你是写驱动或者内核代码,否则你不需要太担心底层机制。在编写 user-space 应用程序时,关键概念是 (1) 某些系统调用可能 block 这意味着您的进程将进入睡眠状态,直到事件发生,以及 (2)在可抢占系统(所有主流的现代操作系统)上,您的程序可能随时被抢占,以便其他进程可以 运行.

* 请注意,在某些平台中,例如 Linux,线程 实际上只是共享其虚拟地址 [=36] 的另一个进程=] 与另一个过程。因此,进程和线程被调度程序完全相同地对待。

内核可以是抢占式的,不是调度器。

首先sched_yield()wait()自愿抢占的类型,当进程本身发出CPU即使内核是非抢占的.

如果内核有能力在时间片过期或更高优先级进程变得运行可用时切换到另一个进程,那么我们正在谈论非自愿抢占,即抢占内核,它可能发生在下面解释的不同地方。

区别在于insched_yield()进程停留在运行nable TASK_RUNNING状态,只是走到了[=80]的末尾=] 为它的静态优先级排队。进程必须等待再次获取 CPU。

另一方面,wait() 将进程置于睡眠 TASK_(UN)INTERRUPTABLE 状态,在等待队列中,调用 schedule() 并等待一个事件的发生。当事件发生时,进程再次移动到 运行 队列。但这并不意味着他们会立即获得CPU。

这里说明什么时候schedule()可以在进程被唤醒后调用:

唤醒并不会真正导致进入 schedule()。他们加了一个 任务到 运行-queue 就是这样。 如果添加到 运行-queue 的新任务抢占了当前队列 任务,然后唤醒设置 TIF_NEED_RESCHED 和 schedule() 获取 在最近的可能场合打电话:

  • 如果内核是可抢占的(CONFIG_PREEMPT=y):

    • 在系统调用或异常上下文中,在下一个最外面 preempt_enable()。 (这可能是 wake_up() 的 spin_unlock()!)
    • 在 IRQ 上下文中,return 从中断处理程序到 抢占上下文
  • 如果内核不可抢占(CONFIG_PREEMPT未设置) 然后在下一个:

    • cond_resched() 调用
    • 显式 schedule() 调用
    • return 从系统调用或异常到用户-space
    • return 从中断处理程序到用户-space