用户级和内核支持的线程之间的区别?
我一直在研究基于这个主题的一些笔记,尽pipe我对线程有一般的理解,但我并不确定用户级和内核级线程之间的区别 。
我知道进程基本上是由多个线程或单个线程组成的,但这两个前面提到的types的线程是什么?
据我所知,内核支持的线程可以访问系统调用的内核,而用户级线程则不能使用其他用途。
那么,当用户级线程是由程序员创build的线程时,是否使用内核支持的线程来执行由于其状态而无法正常执行的操作?
编辑:这个问题有点混乱,所以我回答了两种不同的方式。
操作系统级别的线程与绿色线程
为了清楚起见,我通常会说“OS级别的线程”或“本地线程”而不是“内核级线程”(我在下面的原始答案中将其与“内核线程”混淆了)。操作系统级别的线程是由操作系统。 大多数语言都支持它们。 (C,最近的Java等)他们是非常难以使用,因为你是100%负责防止问题。 在某些语言中,即使是本地数据结构(例如哈希或字典)也会中断而无需额外的locking代码。
OS线程的对面是一个由您的语言pipe理的绿色线程 。 这些线程根据语言(C语言中的协程,Go中的goroutines,Ruby中的纤程等)给出各种名称。 这些线程只存在于您的语言中,而不在您的操作系统中。 因为语言select上下文切换(即在语句结束时),所以它可以防止细微的竞争条件(例如看到部分复制的结构,或者需要locking大多数数据结构)的TONS。 程序员看到“阻塞”调用(即data = file.read()
),但该语言将其转换为对OS的asynchronous调用。 该语言允许其他绿色线程在等待结果的同时运行。
绿色的线程对于程序员来说更简单,但是它们的性能是不一样的:如果你有很多的线程,绿色的线程对于CPU和RAM来说都会更好。 另一方面,大多数绿色线程语言不能利用多核心。 (你甚至不能购买单核电脑或电话!)。 而一个不好的库可以通过阻塞OS调用来停止整个语言。
两全其美的是每个CPU有一个操作系统线程,许多绿色的线程魔术般地移动到操作系统线程上。 像Go和Erlang这样的语言可以做到这一点。
系统调用和用户级线程不可用的其他用途
这只是一半的事实。 是的,如果你自己调用操作系统,你可以很容易地导致问题(即做一些被阻塞的东西)。但是这个语言通常有replace,所以你甚至没有注意到。 这些替代品确实会调用内核,只是略微不同于您的想法。
内核线程vs用户线程
编辑:这是我的原始答案,但它是关于用户空间线程vs仅内核线程,事后(可能事后)可能不是问题。
用户线程和内核线程完全相同。 (你可以通过查看/ proc /看到内核线程也在那里)。
用户线程是执行用户空间代码的线程。 但是可以随时调用内核空间。 它仍然被认为是“用户”线程,尽pipe它在高级安全级别上执行内核代码。
内核线程是只运行内核代码并且不与用户空间进程相关联的线程。 这些就像“UNIX守护进程”,除了它们是内核守护进程。 所以你可以说内核是一个multithreading程序。 例如,有一个交换的内核线程。 这将强制所有交换问题获得“序列化”到一个单一的stream。
如果用户线程需要某些东西,它会调用内核,将线程标记为睡眠。 之后,交换线程find数据,因此它将用户线程标记为可运行。 后来,“用户线程”从内核返回到用户空间,就好像什么都没有发生过一样。
实际上, 所有线程都从内核空间开始,因为clone()操作发生在内核空间中。 (并且在用户空间中可以“返回”到一个新的进程之前,有很多内核会计要做。)
在比较之前,让我们先了解一个线程是什么。 线程是独立进程域内的轻量级进程。 他们是必需的,因为过程繁重,消耗大量资源,更重要的是,
两个单独的进程不能共享一个内存空间。
假设你打开一个文本编辑器。 这是一个独立的进程在内存中执行一个单独的可寻址位置。 在这个过程中,你将需要很多资源,例如插入graphics,拼写检查等。为这些function创build单独的进程并将其独立地保存在内存中是不可行的。 为了避免这一点,
多个线程可以在一个进程内创build,这个进程可以共享一个共同的内存空间,独立存在于一个进程内。
现在回到你的问题,一次一个。
我并不确定用户级和内核级线程之间的区别。
线程根据其执行领域大致分为用户级线程和内核级线程 。 还有一种情况是一个或多个用户线程映射到一个或多个内核线程 。
– 用户级线程
用户级线程大多数位于应用程序级别,应用程序创build这些线程以维持其在主内存中的执行。 除非需要,这些线程与内核线程隔离。
由于不必引用多个寄存器,上下文切换比内核级线程快得多,因此这些创build起来更容易。
用户级线程大多会导致应用程序级的更改,内核级线程继续按照自己的步调执行。
– 内核级线程
这些线程大多独立于正在进行的进程,并由操作系统执行。
操作系统需要这些线程来执行内存pipe理,进程pipe理等任务。
由于这些线程维护,执行和报告操作系统所需的进程; 内核级线程的创build和pipe理成本较高,而且这些线程的上下文切换较慢。
大多数内核级线程不能被用户级线程抢占。
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.
– 映射到内核线程的用户级线程
这也许是最有趣的部分。 许多用户级线程映射到内核级线程,内核级线程又与内核进行通信。
一些重要的映射是:
一对一
当一个用户级线程只映射到一个内核线程时。
优点:每个用户线程映射到一个内核线程。 即使其中一个用户线程发出阻塞系统调用,其他进程仍然不受影响。
缺点:每个用户线程都需要一个内核线程进行交互,并且内核线程创build和pipe理的开销很大。
多对一
当许多用户线程映射到一个内核线程。
优点:不需要多个内核线程,因为相似的用户线程可以映射到一个内核线程。
缺点:即使其中一个用户线程发出阻塞系统调用,也会阻塞映射到该内核线程的所有其他用户线程。
另外,由于内核一次只能处理一个内核线程,所以不能实现高水平的并发。
许多到许多
当许多用户线程映射到相等或较less的内核线程数时。 程序员决定有多less用户线程将映射到多less个内核线程。 一些用户线程可能只映射到一个内核线程。
优点:实现了高度的并发性。 程序员可以决定一些有潜在危险的线程,这些线程可能会发出一个阻塞的系统调用,并把它们放在一对一的映射中。
缺点:内核线程的数量,如果不谨慎决定会减慢系统。
你的问题的其他部分:
内核支持的线程可以访问内核进行系统调用,而用户级线程则不能使用其他用途。
那么,当用户级线程是由程序员创build的线程时,是否使用内核支持的线程来执行由于其状态而无法正常执行的操作?
部分正确。 几乎所有的内核线程都可以访问系统调用和其他关键中断,因为内核线程负责执行操作系统的进程。 用户线程将无法访问这些关键function。 例如,文本编辑器不能拍摄有能力改变过程物理地址的线程。 但是,如果需要,用户线程可以映射到内核线程,并发出一些不能作为独立实体执行的系统调用。 然后内核线程会将这个系统调用映射到内核,并且如果认为合适的话就会执行动作。
某些开发环境或语言会添加自己的线程(如特性),这些线程是为了利用一些环境知识而编写的,例如GUI环境可以实现某些线程function,这些线程function可在每个事件循环的用户线程之间切换。 一个游戏库可以有一些像字符一样的线程行为。 有时用户线程的行为可以以不同的方式实现,例如我使用cocoa很多,它有一个计时器机制,每隔数秒钟执行你的代码,使用几分之一秒,它就像一个线程。 Ruby有一个合作线程的yield特性。 用户线程的优点是可以在更可预测的时间切换。 有了内核线程,每当一个线程重新启动时,它需要加载它正在处理的任何数据,这可能需要一段时间,用户线程可以在完成一些数据的处理时切换,所以不需要重新加载。 我没有遇到过与内核线程相同的用户线程,只有类似于定时器这样的机制的线程,尽pipe我已经在旧的教科书中阅读过它们,所以我不知道它们是否在过去更受欢迎,真正的multithreading操作系统(现代Windows和Mac OS X)和更强大的硬件的兴起,我想知道他们是否已经失宠了。
用户线程:1.该库为线程创build,调度和pipe理提供支持,而不受内核的支持。 2.不知道用户级线程创build和调度的内核在用户空间中完成,无需内核干预。 3.用户级线程通常快速创build和pipe理,但它们有缺点。 4.如果内核是单线程的,那么执行阻塞系统调用的任何用户级线程都将导致整个进程阻塞,即使在应用程序中可以运行其他线程也是如此。 5.用户线程库包括POSIX Pthreads,Mach C线程和Solaris 2 UI线程。
内核线程:1.内核在内核空间执行线程创build,调度和pipe理。 2.内核线程创build和pipe理通常比用户线程要慢。 3.内核pipe理线程,如果一个线程执行阻塞系统调用。 4.多处理器环境,内核可以在不同的处理器上调度线程。 5.包括Windows NT,Windows 2000,Solaris 2,BeOS和Tru64 UNIX(以前称为Digital UN1X) – 支持内核线程。