当执行用户代码的线程正在等待输入时,调度程序如何知道要中断输入或线程如何知道要调用调度程序,这似乎是因为简单的单线程应用程序的普通程序员不太可能插入sched_yield( )无处不在。编译器是在优化时插入sched_yield()还是线程只是自旋锁定,直到调度程序设置的通用计时器中断触发,或者用户是否必须显式声明wait(),sleep()函数以使上下文切换?
如果调度程序不是抢占式的,则此问题尤其重要,因为在等待吞吐量有效的输入时,它必须调用调度程序,但我不确定它的工作方式这个。
答案 0 :(得分:2)
我不清楚您的问题是理论还是实践。实际上,在每个现代操作系统中,I / O操作都是特权。这意味着,为了使用户进程或线程访问文件,设备等,它必须发出系统调用。 这样内核就有机会做它认为适当的任何事情。例如,它可以检查I / o操作是否将被阻塞,并因此在发出该操作之后切换正在运行的(即“调用”调度程序)进程。 请注意,即使内核没有处理计时器中断,该机制也可以工作。总之,这将取决于您的系统。例如,在没有操作系统(或最少操作系统)的嵌入式系统中,在发出阻止操作之前,用户代码有全部责任来调用调度程序。
答案 1 :(得分:2)
请注意不要将抢占与进程睡眠的能力混淆。即使使用非抢占式调度程序,进程也可以进入睡眠状态。当进程正在等待I / O时,就会发生这种情况。该过程进行系统调用,例如read()
,并且设备确定没有可用数据。然后,它通过更新调度程序使用的数据结构在内部使进程进入休眠状态。然后,调度程序将执行其他进程,直到发生唤醒原始进程的中断或其他事件。然后,醒来的进程将再次有资格进行调度。
另一方面,抢占是体系结构的调度程序在没有其配合的情况下停止执行进程的能力。中断可能发生在程序指令流中的任何位置。控制权返回到调度程序,然后调度程序可以执行其他进程,并稍后返回中断(抢占)进程。大多数调度程序都会分配时间片,在该时间片中,进程可以运行预定的时间,如果优先级更高的进程需要时间片,则在此时间段之前会被抢占。
除非您正在编写驱动程序或内核代码,否则无需过多担心底层机制。编写用户空间应用程序时,关键概念是(1)一些系统调用可能 block ,这意味着您的进程将一直处于睡眠状态,直到事件发生为止;(2)在可抢占系统上(所有主流操作系统),您的程序可能随时被抢占,以便其他进程可以运行。
*请注意,在某些平台(例如Linux)中, thread 实际上只是与另一个进程共享其虚拟地址空间的另一个进程。因此,调度程序将进程和线程完全相同。
答案 2 :(得分:0)
内核可以抢占,而不是调度程序。
第一个sched_yield()
和wait()
是自愿抢占的类型,当进程本身释放CPU时,即使内核是非抢占式的。
如果内核能够在时间段到期或更高优先级的进程变为可运行状态时切换到另一个进程,那么我们所说的是非自愿抢占,即抢占式内核,它可以在不同的地方发生在下面。
区别在于,sched_yield()
进程处于可运行的 TASK_RUNNING 状态,但由于其静态优先级而仅进入运行队列的末尾。进程必须等待才能再次获取CPU。
另一方面,wait()
使进程进入睡眠状态 TASK_(UN)INTERRUPTABLE 状态,在等待队列中,调用schedule()
并等待事件发生。事件发生时,进程将再次移动以运行队列。但这并不意味着他们会立即获得CPU。
此处说明了在唤醒过程后何时可以调用schedule()
的情况:
唤醒实际上并不会导致进入schedule()。他们添加了一个 运行队列的任务就这样。 如果添加到运行队列的新任务优先于当前任务 任务,然后唤醒设置TIF_NEED_RESCHED,然后schedule() 在最可能的情况下致电:
如果内核是可抢占的(CONFIG_PREEMPT = y):
如果内核不可抢占(未设置CONFIG_PREEMPT) 然后在下一个: