我一直在查阅关于这个主题的一些笔记,虽然我对线程有一般的了解,但是我对用户级线程和内核级线程之间的差异不太确定。
我知道进程基本上是由多个线程或单个线程组成,但是这些线程是前面提到的两种类型中的哪一种?
据我所知,内核支持的线程可以访问内核以进行系统调用和其他用户级线程无法使用的操作。
那么,用户级线程只是由程序员创建的线程,然后利用内核支持的线程执行不能正常执行的操作吗?
我一直在查阅关于这个主题的一些笔记,虽然我对线程有一般的了解,但是我对用户级线程和内核级线程之间的差异不太确定。
我知道进程基本上是由多个线程或单个线程组成,但是这些线程是前面提到的两种类型中的哪一种?
据我所知,内核支持的线程可以访问内核以进行系统调用和其他用户级线程无法使用的操作。
那么,用户级线程只是由程序员创建的线程,然后利用内核支持的线程执行不能正常执行的操作吗?
为了更加清晰,我通常会说“操作系统级线程”或“本地线程”,而不是“内核级线程”(在我的原始回答中与“内核线程”混淆了)。操作系统级线程由操作系统创建和管理。大多数编程语言都支持它们(如C、最近的Java等)。但是,由于您需要100%负责防止问题,所以它们非常难以使用。在某些语言中,即使本地数据结构(例如哈希表或字典)也需要额外的锁定代码才能正常工作。
与操作系统级线程相反的是由您的编程语言管理的绿色线程。这些线程根据语言的不同具有各种名称(如C中的协程,Go中的goroutine,Ruby中的纤程等)。这些线程仅存在于您的语言中,而不是在您的操作系统中。因为语言选择上下文切换(即在语句结束时),所以它可以防止许多微妙的竞争条件(例如查看部分复制的结构或需要锁定大多数数据结构)。程序员看到“阻塞”调用(即data = file.read()
),但语言将其转换为对操作系统的异步调用。然后,语言允许其他绿色线程在等待结果时运行。
对程序员来说,绿色线程要简单得多,但它们的性能有所不同:如果您有很多线程,绿色线程可以更好地处理CPU和RAM。另一方面,大多数绿色线程语言无法利用多个核心。(您甚至无法再购买单核计算机或手机了!)。而且,一个糟糕的库可以通过执行阻塞OS调用来停止整个语言。
最好的方法是每个CPU拥有一个OS线程,并且有许多绿色线程可以神奇地移动到OS线程上。像Go和Erlang这样的编程语言可以实现这一点。
系统调用和其他用户级线程无法使用的功能
这只是一半正确。是的,如果您自己调用操作系统(即执行阻塞操作),则可能会轻松引起问题。但是,语言通常具有替代方法,因此您甚至不会注意到。这些替代方法确实调用内核,但与您想象的略有不同。
编辑:这是我的原始答案,但它是关于用户空间线程与仅内核线程(事后看来)可能不是问题。
用户线程和内核线程完全相同。 (您可以通过查看/ proc /并查看内核线程是否存在来了解情况。)
用户线程是执行用户空间代码的线程。但是,它随时可以调用内核空间。即使在提高的安全级别下执行内核代码,它仍然被视为“用户”线程。
一个Kernel线程只运行内核代码,不与用户空间进程关联。它们类似于“UNIX守护程序”,但是它们是仅限内核的守护程序。因此,可以说内核是一个多线程程序。例如,有一个用于交换的内核线程。这迫使所有交换问题“串行化”为单个流。在我们开始比较之前,让我们先了解一下什么是线程。线程是独立进程域内的轻量级进程。它们是必需的,因为进程很重,消耗大量资源,更重要的是,
假设你打开一个文本编辑器。它是在内存中执行的独立进程,具有单独的可寻址位置。您需要此进程中的许多资源,如插入图形、拼写检查等。为每个这些功能创建单独的进程并在内存中独立维护它们是不可行的。为避免这种情况,可以在单个进程内创建多个线程,这些线程可以共享公共内存空间,在进程内独立存在。两个独立的进程无法共享内存空间。
现在,回到你的问题,逐个来看。
我不太确定用户级线程和内核级线程之间的区别。
根据其执行域,线程大致分为用户级线程和内核级线程。也有一些情况下,一个或多个用户级线程映射到一个或多个内核级线程。
- 用户级线程
用户级线程主要在应用程序级别,其中应用程序创建这些线程以维持其在主内存中的执行。除非需要,否则这些线程与内核线程隔离工作。
这些线程更容易创建,因为它们不必引用许多寄存器,并且上下文切换比内核级线程快得多。
用户级线程主要可以在应用程序级别引起变化,而内核级线程继续以自己的速度执行。
- 内核级线程
这些线程大多独立于正在进行的进程,并由操作系统执行。
操作系统需要这些线程来执行诸如内存管理、进程管理等任务。
由于这些线程维护、执行和报告操作系统所需的进程,因此创建和管理这些线程更加昂贵,这些线程的上下文切换也很慢。
大多数内核级线程无法被用户级线程抢占。
MS DOS written for Intel 8088 didn't have dual mode of operation. Thus, a user level process had the ability to corrupt the entire operating system.
- 用户级线程映射到内核级线程
这可能是最有趣的部分。许多用户级线程映射到内核级线程,这些线程与内核进行通信。
一些重要的映射包括:
一对一
当一个用户级线程映射到一个内核级线程时。
优点:每个用户线程映射到一个内核线程。即使其中一个用户线程发出阻塞系统调用,其他进程也不会受到影响。
缺点:每个用户线程都需要一个内核线程进行交互,而内核线程创建和管理成本很高。
多对一
当多个用户线程映射到一个内核线程时。
优点:不需要多个内核线程,因为相似的用户线程可以映射到一个内核线程。
缺点:即使其中一个用户线程发出阻塞系统调用,映射到该内核线程的所有其他用户线程都会被阻塞。
此外,无法实现良好的并发性,因为内核一次只能处理一个内核线程。
多对多
当许多用户线程映射到相等或较少数量的内核线程时。程序员决定有多少个用户线程将映射到多少个内核线程。一些用户线程可能只映射到一个内核线程。
优点:实现了极高的并发性。程序员可以决定一些可能会发出阻塞系统调用的危险线程,并将它们与一对一映射放在一起。
缺点:如果没有谨慎决定内核线程的数量,系统可能会变慢。
您问题的另一部分:
内核支持的线程可以访问内核以进行系统调用和其他用户级线程不可用的操作。
因此,用户级线程是否只是由程序员创建的线程,然后利用内核支持的线程执行由于其状态而无法正常执行的操作?
部分正确。几乎所有的内核线程都可以访问系统调用和其他关键中断,因为内核线程负责执行操作系统的进程。用户线程将无法访问其中一些关键功能。例如,文本编辑器永远不能启动一个具有更改进程物理地址能力的线程。但是,如果需要,用户线程可以映射到内核线程并发出一些它作为独立实体无法执行的系统调用。内核线程将然后将此系统调用映射到内核并执行必要的操作。
摘自此处:
为了使并发更便宜,将进程的执行方面分离成线程。因此,操作系统现在管理线程和进程。所有线程操作都在内核中实现,操作系统调度系统中的所有线程。操作系统管理的线程称为内核级线程或轻量级进程。 NT: 线程 Solaris: 轻量级进程(LWP)。
在这种方法中,内核知道并管理线程。在这种情况下不需要运行时系统。内核不是在每个进程中使用线程表,而是有一个线程表来跟踪系统中的所有线程。此外,内核还维护传统的进程表来跟踪进程。操作系统内核提供系统调用来创建和管理线程。
优点:
由于内核完全知道所有线程,调度程序可能决定给具有大量线程的进程比给具有少量线程的进程更多的时间。 内核级线程特别适合频繁阻塞的应用程序。
缺点:
内核级线程速度慢且效率低下。例如,线程操作的速度比用户级线程慢几百倍。 由于内核必须管理和调度线程以及进程,因此需要一个完整的线程控制块(TCB)来维护有关线程的信息。因此会有重大开销和内核复杂性的增加。
用户级线程
内核级线程使并发相对于进程变得更便宜,因为需要分配和初始化的状态要少得多。但是,对于细粒度的并发,内核级线程仍然存在过多的开销。线程操作仍需要系统调用。理想情况下,我们需要将线程操作速度与过程调用一样快。内核级线程必须是通用的,以支持所有程序员、语言、运行时等的需求。对于这种细粒度的并发,我们仍然需要更“便宜”的线程。 为了使线程便宜又快,需要在用户级别实现。用户级线程完全由运行时系统(用户级库)管理。内核不知道用户级线程,并将其像单线程进程一样管理。用户级线程很小且快速,每个线程由PC、寄存器、堆栈和小型线程控制块表示。创建新线程、在线程之间切换和同步线程通过过程调用完成。即没有内核参与。用户级线程比内核级线程快百倍。
优点:
这种技术最明显的优点是可以在不支持线程的操作系统上实现用户级线程包。用户级线程不需要修改操作系统。简单表示:每个线程只需由一个PC、寄存器、堆栈和一个小的控制块组成,所有这些都存储在用户进程地址空间中。简单管理:这意味着创建线程、在线程之间切换以及线程之间的同步都可以在没有内核干预的情况下完成。快速高效:线程切换的开销并不比过程调用更大。