我一直在浏览基于该主题的一些说明,尽管我对线程有一个总体的了解,但是我不确定是用户级别线程还是内核级别线程之间的区别。
我知道进程基本上是由多个线程或单个线程组成的,但是这些线程是前面提到的两种类型吗?
据我了解,内核支持的线程可以访问内核以进行系统调用和用户级线程无法使用的其他用途。
那么,用户级线程是否仅仅是程序员在使用内核支持的线程执行由于其状态而无法正常执行的操作时创建的线程?
我一直在浏览基于该主题的一些说明,尽管我对线程有一个总体的了解,但是我不确定是用户级别线程还是内核级别线程之间的区别。
我知道进程基本上是由多个线程或单个线程组成的,但是这些线程是前面提到的两种类型吗?
据我了解,内核支持的线程可以访问内核以进行系统调用和用户级线程无法使用的其他用途。
那么,用户级线程是否仅仅是程序员在使用内核支持的线程执行由于其状态而无法正常执行的操作时创建的线程?
Answers:
编辑:这个问题有点令人困惑,所以我用两种不同的方式回答。
为了清楚起见,我通常说“ OS级线程”或“本地线程”,而不是“内核级线程”(我在下面的原始答案中将其与“内核线程”相混淆。)OS级线程由创建和管理操作系统。大多数语言都支持它们。(C,最新的Java等),它们极难使用,因为您100%负责预防问题。在某些语言中,甚至本机数据结构(例如哈希或字典)也将在没有额外的锁定代码的情况下中断。
OS线程的反面是由您的语言管理的绿色线程。根据语言(这些语言在C中的协程,在Go中的goroutines,在Ruby中的fibre等),这些线程被赋予各种名称。这些线程仅存在于您的语言中,而不存在于您的操作系统中。因为该语言选择上下文切换(即在语句末尾),所以它可以防止TONS出现细微的竞争状况(例如看到部分复制的结构或需要锁定大多数数据结构)。程序员看到了“阻塞”调用(即data = file.read()
),但是该语言将其转换为对OS的异步调用。然后,该语言允许其他绿色线程在等待结果时运行。
绿色线程对于程序员来说要简单得多,但是它们的性能各不相同:如果您有很多线程,则绿色线程对于CPU和RAM都可能更好。另一方面,大多数绿色线程语言不能利用多核。(您甚至无法再购买单核计算机或电话!)。错误的库可能会通过阻塞OS调用来停止整个语言。
两全其美的做法是每个CPU拥有一个OS线程,并且许多绿色线程被神奇地转移到OS线程上。像Go和Erlang这样的语言都可以做到这一点。
系统调用和其他用途对用户级线程不可用
这只有一半是正确的。是的,如果您自己调用操作系统,就很容易造成问题(即,做一些阻塞性的事情。)但是该语言通常会有替换,因此您甚至都不会注意到。这些替代品确实调用了内核,与您认为的略有不同。
编辑:这是我最初的答案,但这是关于用户空间线程与仅内核线程的关系,(事后看来)这可能不是问题。
用户线程和内核线程完全相同。(您可以通过在/ proc /中查看来发现内核线程也在那里。)
用户线程是执行用户空间代码的线程。但是它可以随时调用内核空间。即使它以提高的安全级别执行内核代码,它仍然被认为是“用户”线程。
内核线程是仅运行内核代码且不与用户空间进程关联的线程。这些类似于“ UNIX守护程序”,只是它们是仅内核的守护程序。因此,您可以说内核是一个多线程程序。例如,有一个用于交换的内核线程。这迫使所有交换问题都被“序列化”到单个流中。
如果用户线程需要某些东西,它将调用内核,将该线程标记为休眠。稍后,交换线程找到数据,因此将用户线程标记为可运行。后来,“用户线程”从内核返回到用户域,好像什么也没发生。
实际上,所有线程都是从内核空间开始的,因为clone()操作发生在内核空间中。(在您可以“返回”用户空间中的新进程之前,还有许多内核核算工作要做。)
在进行比较之前,让我们首先了解什么是线程。线程是独立进程域内的轻量级进程。之所以需要它们,是因为过程繁重,消耗大量资源,更重要的是,
两个单独的进程不能共享内存空间。
假设您打开一个文本编辑器。它是在内存中执行的具有独立可寻址位置的独立进程。在此过程中,您将需要许多资源,例如插入图形,拼写检查等。为这些功能中的每一个功能创建单独的过程并在内存中独立维护它们是不可行的。为了避免这种情况,
可以在一个进程内创建多个线程,这些线程可以共享一个进程内独立存在的公共内存空间。
现在,一次回到您的问题。
我不太确定用户级线程和内核级线程之间的区别。
线程根据其执行域大致分为用户级线程和内核级线程。在某些情况下, 一个或多个用户线程映射到一个或多个内核线程。
-用户级线程
用户级线程通常位于应用程序级,在此应用程序创建这些线程以维持其在主内存中的执行。除非需要,否则这些线程与内核线程隔离工作。
因为它们不必引用许多寄存器,并且上下文切换比内核级线程快得多,所以它们更易于创建。
用户级别的线程通常会导致应用程序级别的更改,而内核级别的线程将继续以自己的速度执行。
-内核级线程
这些线程主要独立于正在进行的进程,并由操作系统执行。
操作系统需要这些线程来执行诸如内存管理,进程管理等任务。
由于这些线程维护,执行和报告操作系统所需的进程;内核级线程的创建和管理成本更高,并且这些线程的上下文切换很慢。
大多数内核级线程不能被用户级线程抢占。
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.
-映射到内核线程上的用户级线程
这也许是最有趣的部分。许多用户级线程映射到内核级线程,内核级线程又与内核进行通信。
一些重要的映射是:
一对一
当一个用户级线程仅映射到一个内核线程时。
优点:每个用户线程都映射到一个内核线程。即使用户线程之一发出了阻塞的系统调用,其他进程也不会受到影响。
缺点:每个用户线程都需要一个内核线程进行交互,并且内核线程的创建和管理成本很高。
多对一
当许多用户线程映射到一个内核线程时。
优点:因为可以将相似的用户线程映射到一个内核线程,所以不需要多个内核线程。
缺点:即使一个用户线程发出了阻塞的系统调用,映射到该内核线程的所有其他用户线程也会被阻塞。
另外,由于内核一次只能处理一个内核线程,因此无法实现良好的并发水平。
多对多
当许多用户线程映射到等于或更少数量的内核线程时。程序员决定将多少个用户线程映射到多少个内核线程。一些用户线程可能仅映射到一个内核线程。
优点:实现了高水平的并发。程序员可以决定一些潜在危险的线程,这些线程可能发出阻塞的系统调用,并将它们与一对一映射。
缺点:如果不谨慎决定内核线程的数量,可能会减慢系统速度。
您问题的另一部分:
内核支持的线程可以访问内核以进行系统调用和用户级线程不可用的其他用途。
那么,用户级线程是否仅仅是程序员在使用内核支持的线程执行由于其状态而无法正常执行的操作时创建的线程?
部分正确。由于内核线程负责执行OS的进程,因此几乎所有内核线程都可以访问系统调用和其他关键中断。用户线程将无法访问其中一些关键功能。例如,文本编辑器永远无法拍摄能够更改进程物理地址的线程。但是,如果需要,用户线程可以映射到内核线程,并发出一些作为独立实体无法执行的系统调用。然后,内核线程将该系统调用映射到内核,并在认为合适的情况下执行操作。
一些开发环境或语言会在其中添加自己的线程,例如功能,该线程是为了利用该环境的某些知识而编写的,例如,GUI环境可以实现一些线程功能,这些功能可以在每个事件循环的用户线程之间进行切换。
游戏库可能具有一些类似于角色行为的线程。有时,类似于用户线程的行为可以用不同的方式来实现,例如我经常使用可可,并且它具有计时器机制,该机制每x秒数执行一次代码,使用几分之一秒,就像线程一样。Ruby具有类似于协作线程的yield特性。用户线程的优点是他们可以在更可预测的时间进行切换。对于内核线程,每次线程再次启动时,它都需要加载正在处理的任何数据,这可能会花费一些时间,对于用户线程,您可以在完成对某些数据的处理后进行切换,因此不需要重新加载。
我没有遇到看起来与内核线程相同的用户线程,只是遇到了像计时器之类的线程,尽管我已经在较早的教科书中阅读了有关它们的内容,所以我想知道它们是否在过去更流行,但是在真正的多线程OS(现代Windows和Mac OS X)的崛起以及功能更强大的硬件,我想知道它们是否已不受欢迎。
从这里引用:
为了降低并发成本,将流程的执行方面分成多个线程。这样,操作系统现在可以管理线程和进程。所有线程操作都在内核中实现,并且OS调度系统中的所有线程。操作系统管理的线程称为内核级线程或轻量级进程。NT:线程Solaris:轻量级进程(LWP)。
在这种方法中,内核知道并管理线程。在这种情况下,不需要运行系统。内核有一个线程表来跟踪系统中的所有线程,而不是每个进程中的线程表。此外,内核还维护传统的进程表以跟踪进程。操作系统内核提供系统调用以创建和管理线程。
优点:
因为内核完全了解所有线程,所以调度程序可能会决定给具有大量线程的进程比具有少量线程的进程更多的时间。内核级线程特别适合经常阻塞的应用程序。
缺点:
内核级线程缓慢且效率低下。例如,线程操作比用户级线程慢数百倍。由于内核必须管理和调度线程以及进程。每个线程都需要一个完整的线程控制块(TCB)来维护有关线程的信息。结果,存在大量的开销并增加了内核复杂度。
用户级线程
内核级线程使并发比进程便宜得多,因为分配和初始化的状态要少得多。但是,对于细粒度的并发,内核级线程仍然会遭受太多开销。线程操作仍然需要系统调用。理想情况下,我们要求线程操作必须与过程调用一样快。内核级线程必须通用才能满足所有程序员,语言,运行时等的需求。对于这种细粒度的并发,我们仍然需要“便宜”的线程。为了使线程便宜且快速,它们需要在用户级别实现。用户级线程完全由运行时系统(用户级库)管理。内核对用户级线程一无所知,并且像单线程进程一样对其进行管理。用户级线程又小又快,每个线程都由一台PC代表,寄存器,堆栈和小线程控制块。通过过程调用可以完成创建新线程,在线程之间切换以及同步线程的操作。即没有内核参与。用户级线程比内核级线程快一百倍。
优点:
该技术最明显的优点是,可以在不支持线程的操作系统上实现用户级线程程序包。用户级线程不需要修改操作系统。简单表示:每个线程仅由PC,寄存器,堆栈和一个小的控制块表示,所有这些都存储在用户进程地址空间中。简单管理:这仅意味着创建线程,在线程之间切换以及线程之间的同步都可以在无需内核干预的情况下完成。快速高效:线程切换并不比过程调用贵。
缺点:
用户级线程不是一个完美的解决方案,而是其他所有事物,它们是一个折衷方案。由于用户级线程对于操作系统是不可见的,因此无法与操作系统很好地集成在一起。结果,Os可能做出错误的决定,例如使用空闲线程调度进程,阻止进程的线程发起I / O的进程,即使该进程还有其他可以运行的线程以及使用持有锁的线程取消调度进程。解决此问题需要内核与用户级线程管理器之间进行通信。线程与操作系统内核之间缺乏协调。因此,无论进程中有一个线程还是有1000个线程,整个进程只占用一个时间片。由每个线程放弃对其他线程的控制。用户级线程需要非阻塞系统调用,即 多线程内核。否则,即使进程中剩下可运行的线程,整个进程也会在内核中被阻塞。例如,如果一个线程导致页面错误,则过程将阻塞。