如果我们在 linux 上使用默认调度策略创建 pthreads (pthread_create) 或进程 (fork),调度程序会在调度它们时以相同的优先级对待进程和线程吗?
假设有一个线程的进程 P1 和有 2 个线程的进程 P2 T1 T2
假设只有一个核心..调度是 P1 T1 P1 T2 P1 T1 P1 T2
或者
P1 T1 T2 P1 T1 T2
如果我们在 linux 上使用默认调度策略创建 pthreads (pthread_create) 或进程 (fork),调度程序会在调度它们时以相同的优先级对待进程和线程吗?
假设有一个线程的进程 P1 和有 2 个线程的进程 P2 T1 T2
假设只有一个核心..调度是 P1 T1 P1 T2 P1 T1 P1 T2
或者
P1 T1 T2 P1 T1 T2
Linux 根本不再调度进程。
在内核中,线程被调度。进程的概念现在是一种人为的构造,主要由内核之外的事物看到。显然,内核必须知道线程是如何联系在一起的,但不是为了调度目的。
基本上,内核维护着一大堆线程,每个线程都有一个线程组负责人,这就是在外部看到的进程。一个线程有一个线程 ID 和一个线程组 ID——这很像 PID 和 PPID(进程 ID 和父进程 ID)之间的关系。
当您创建一个常规线程时,内核会为其提供一个全新的线程 ID,但其线程组 ID 设置为与创建它的线程的组 ID 相同。这样,对于外界来说,它就像一个进程中的线程。
当你fork 时,内核会给它一个全新的线程 ID,并将其线程组 ID 设置为与其线程 ID 相同的值。这样,它在外界看来就像一个过程。
大多数报告进程的非内核实用程序实际上只是报告线程 ID 与线程组 ID 相同的线程。
其他方法有一些微妙之处,可能太复杂而无法在这里介绍。我上面写的(希望)是一篇很好的中级论文。
现在,对于您的具体问题,这两种情况都不是,因为P1
只有一个线程(没有P1T2
)。
对于内核,线程是和P1T1
,并且假设它们具有相同的调度属性并且行为相同(a),这就是它们的调度方式。P2T1
P2T2
也可以看看:
了解更多信息。
(a):显然,如果线程开始阻塞 I/O(内核不会在 I/O 可用之前调度它们)或提前释放它们的时间量(内核可能会提高它们的优先级作为表现出色的奖励)但是他们的行为不一样。
确实,Linux 内核(从2.6.23及更高版本开始)调度任务,这些任务要么是线程,要么是(单线程)进程。
除了 paxdiablo 的公认答案之外,我添加这个答案是为了添加一个关于不同线程调度方式的更通用的信息视图。
通常,线程可以是用户空间线程或内核空间线程。用户空间线程通常由库实现。因此,内核对它们几乎一无所知(内核只知道它们所属的进程)并且它们在用户空间中处理。相反,内核线程是由内核实现的,它们完全由内核处理。您可以从下图中获取通用视图。
如您所见,左图显示了一些用户空间线程,其中内核仅拥有有关进程的信息(所谓的进程控制块 - PCB)。所有关于线程的资源信息都保存在进程内部(在线程表中)并由用户空间中的相应线程库处理。在右图中,您可以看到内核线程,其中进程表和线程表都保存在内核中。
内核线程的一个例子是LinuxThreads,它现在已被更现代的NPTL 库所取代。用户空间线程的一个例子是库GNU Portable Threads
现在,就调度而言,正如预期的那样,用户空间线程的调度方式与内核线程不同:
当使用用户空间线程时,调度程序正在调度进程。因此,它选择一个特定的进程并分配允许的时间段。然后,进程内部的线程调度器负责选择如何进行线程之间的调度。由于用户空间线程不会因中断而停止,因此所选线程将倾向于消耗整个进程的量子,直到它完成其任务。因此,在这种情况下,假设的调度将类似于:
P1(T1), P2(T1), P1(T1), P2(T1 - T1 完成任务并产生), P2(T2 - 剩余时间段, P1(T1), P2(T2), P1(T1 ), ...
当使用内核线程时,内核会调度线程。内核对线程属于哪个进程不感兴趣,但它会为每个线程平均分配一个时间片。因此,在这种情况下,假设的调度将是:
P1(T1), P2(T1), P2(T2), P1(T1), P2(T2), P1(T1), ...
请注意,还有另一类,混合线程,其中一些用户空间线程被转换为一个内核线程。但是,它更复杂,需要更彻底的分析。