43

所以这里真的有两个问题。首先,(是的,我已经搜索过了,但想要澄清一下),用户线程和内核线程有什么区别?仅仅是一个由用户程序生成,另一个由操作系统生成,后者可以访问特权指令吗?它们在概念上是相同的还是线程本身存在实际差异?

其次,我的问题的真正问题是:我正在使用的书说“用户线程和内核线程之间必须存在关系”,然后列出了这种关系的不同模型。但是这本书未能清楚地解释为什么用户线程必须始终映射到特定的内核线程。为什么是这样?

4

2 回答 2

40

内核线程是由操作系统维护的线程对象。它是一个能够被处理器调度和执行的实际线程。通常,内核线程是具有权限设置、优先级等的重量级对象。内核线程调度程序负责调度内核线程。

用户程序也可以制作自己的线程调度程序。他们可以创建自己的“线程”并模拟上下文切换以在它们之间切换。但是,这些线程不是内核线程。每个用户线程实际上不能独立运行,用户线程运行的唯一方法是,如果内核线程实际上被告知执行用户线程中包含的代码。也就是说,用户线程比内核线程有很大的优势。它们可以更轻量级,因为它们不一定需要有自己的优先级,可以由单个进程管理(可能有更好的关于何时需要运行哪些线程的信息),并且不会创建很多出于安全和锁定目的的内核对象。

用户线程必须与内核线程相关联的原因是用户线程本身只是用户程序中的一堆数据。内核线程是系统中的真正线程,因此用户线程要取得进展,用户程序必须让其调度程序获取用户线程,然后在内核线程上运行它。用户线程和内核线程之间的映射不必是一对一的(1:1);您可以让多个用户线程共享同一个内核线程(一次只有一个用户线程运行),并且您可以拥有一个在 1 : n 映射中跨不同内核线程轮换的用户线程。

于 2013-02-09T20:42:25.200 回答
6

我认为一个真实世界的例子会消除混乱,所以让我们看看在 Linux 中是如何完成的。

首先Linux不区分进程和线程,可以调度的实体在Linux中称为任务,用task_struct. 因此,每当您执行fork()系统调用时,task_struct都会创建一个新的来保存与新任务相关的数据(或指针)。

所以在 Linux 世界中,内核线程意味着一个task_struct对象。

因为调度程序只知道这些可以分配给不同 CPU(逻辑或物理)的实体。换句话说,如果您希望 Linux 调度程序安排您的进程,您必须创建一个task_struct.

用户线程是由某些执行环境(从现在开始的EE)(例如JVM)在内核之外支持和管理的东西。这些 EE 将为您提供一些创建新线程的功能。

但是为什么用户线程必须始终映射到特定的内核线程。

假设您使用 EE 创建了一些线程。最终它们必须由 CPU 执行,从上面的解释我们知道线程必须有一个task_struct才能分配给某个 CPU。这就是映射必须存在的原因。EE 的职责是创建task_structs.

如果您的 EE 使用多对一模型,那么它将只task_struct为所有线程创建一个模型,并将所有这些线程调度到该模型上task_struct。将其想象为有一个 CPU ( task_struct) 和许多进程(在 EE 中创建的线程),您的操作系统(EE)将在单个 CPU 上多路复用这些进程。

如果它使用一对一模型,那么task_struct在 EE 中创建的每个线程都会有一个模型。因此,当您在 EE 中创建新线程时,task_struct会在内核中创建相应的线程。

Windows 做的事情不同(进程和线程不同),但总体思路保持不变,内核线程是 CPU 调度程序考虑分配的实体,因此用户线程必须映射到相应的内核线程(如果您希望 CPU 执行它们)。

于 2018-01-23T06:11:14.753 回答