【问题标题】:How does the scheduler know that a thread is blocked waiting for input?调度程序如何知道线程被阻塞等待输入?
【发布时间】:2018-11-17 14:03:08
【问题描述】:

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

如果调度程序不是抢占式的,这个问题尤其重要,因为它必须在等待输入以使吞吐量生效时调用调度程序,但我不确定它是如何做到的这个。

【问题讨论】:

  • 你有抢占式内核吗? (您是否使用具有内核的操作系统...?)
  • 是的。如果它是抢占式的,那么我相信会存在定时器中断。
  • 当程序等待输入时,它会调用操作系统函数来等待输入。在这种情况下,操作系统(以及调度程序)知道您的线程现在处于等待输入的状态。
  • 这是一个令人讨厌的问题,因为几个月前我对操作系统还不太了解

标签: multithreading scheduler scheduling


【解决方案1】:

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

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

除非您正在编写驱动程序或内核代码,否则您无需过多担心底层机制。在编写用户空间应用程序时,关键概念是(1)某些系统调用可能阻塞,这意味着您的进程在事件发生之前处于休眠状态,以及(2)在抢占式系统(所有主流现代操作系统)您的程序可能随时被抢占,以便其他进程可以运行。

* 请注意,在某些平台中,例如 Linux,thread 实际上只是另一个进程,它与另一个进程共享其虚拟地址空间。因此,调度程序对进程和线程的处理完全相同。

【讨论】:

  • 因此,由于 I/O 访问涉及系统调用,内核函数实现可能包含使线程休眠直到 I/O 设备发生特定中断的功能。
  • @Lewis Kelsey 是的,完全正确。在 Linux 中的大多数设备驱动程序中,这是使用 wait queue 完成的,这是内核为此目的提供的一种机制。
【解决方案2】:

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

【讨论】:

    【解决方案3】:

    内核可以是抢占式的,而不是调度程序。

    第一个sched_yield()wait()自愿抢占的类型,当内核是非抢占的情况下,进程本身给出CPU。

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

    不同之处在于 insched_yield() 进程保持在可运行 TASK_RUNNING 状态,但由于它的静态优先级而只是进入运行队列的末尾。进程必须等待再次获得 CPU。

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

    这里解释了当进程被唤醒后schedule()可以被调用:

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

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

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

      • cond_resched() 调用
      • 显式 schedule() 调用
      • 从系统调用或异常返回到用户空间
      • 从中断处理程序返回到用户空间

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-16
      相关资源
      最近更新 更多