这篇文章是上一篇博客的补充,旨在把没有讲清楚的「用户级线程和内核级线程」补充完整。但愿读者能对线程有更进一步的了解。算法
小白最近在学习多线程编程。编程
网上关于多线程的资料不少,小白很快就把线程的基本概念弄懂了,但关于「用户级线程和内核级线程」的概念,她却怎么也搞不清楚,只好向操做系统基础扎实的小明请教。多线程
对于小白的问题,小明总会耐心解答:“线程里面这两个概念确实比较难理解,我先给你讲用户级线程吧。”函数
“既然你说你已经看过线程的基本概念,那我就直接跳过这一部分了。学习
好久好久以前,线程的概念是出现了,但操做系统厂商可不能直接就去修改操做系统的内核,由于对他们来讲,稳定性是最重要的。贸然把未经验证的东西加入内核,出问题了怎么办?因此想要验证线程的可用性,得另想办法。”操作系统
“我知道我知道,那些研究人员就编写了一个关于线程的函数库,用函数库来实现线程!”小白得意的说:“这个我刚刚在网上看到了。”线程
“是的,他们把建立线程、终止线程等功能放在了这个线程库内,用户就能够经过调用这些函数来实现所须要的功能。”小明找了张纸,写上了几个函数:pthread_creat
,pthread_exit
,pthread_join
,pthread_yield
,接着说:“这是几个重要的函数,你应该能大概猜出这些函数的功能吧?”code
“emmmm,让我想一想,pthread_creat
是建立一个新线程,pthread_exit
是结束线程,pthread_join
嘛,我猜是准备运行某一个线程,而后把它加进就绪队列。最后一个函数我就不知道了。”blog
“不知道也不要紧,一会你就清楚了。”小明接着讲:“要知道,刚刚咱们说的线程库,是位于用户空间的,操做系统内核对这个库一无所知,因此从内核的角度看,它仍是按正常的方式管理。”队列
小白问道:“也就是说操做系统眼里仍是只有进程喽?那我用线程库写的一个多线程进程,只能一次在一个 CPU 核心上运行?”
小明点点头,说:“你说的没错,这实际上是用户级线程的一个缺点,这些线程只能占用一个核,因此作不到并行加速,并且因为用户线程的透明性,操做系统是不能主动切换线程的,换句话讲,若是 A,B 是同一个进程的两个线程的话, A 正在运行的时候,线程 B 想要运行的话,只能等待 A 主动放弃 CPU,也就是主动调用 pthread_yield
函数。”
tobe 注:对操做系统来讲,用户级线程具备不可见性,也称透明性。
“停一下,让我想想,”小白飞速思考着小明的话,“是否是说,即便有线程库,用户级线程也作不到像进程那样的轮转调度?”
“很是正确!看来你对进程的概念很清楚嘛。不过呢,虽然不能作到轮转调度,但用户级线程也有他本身的好处——你能够为你的应用程序定制调度算法,毕竟何时退出线程你本身说了算。刚刚说了,由于操做系统只能看到进程的存在,那若是某一个线程阻塞了,你以为会发生什么?”
“在操做系统眼里,是进程阻塞了,那么整个进程就会进入阻塞态,在阻塞操做结束前,这个进程都没法获得 CPU 资源。那就至关于,全部的线程都被阻塞了。”小白得意的回答。
“没错,因此若是任由线程进行阻塞操做,进程的效率将受到很大的影响,因此在这个过程当中,出现了一个替代方案——jacket。所谓 jacket,就是把一个产生阻塞的系统调用转化成一个非阻塞的系统调用。”
小白惊讶地问:“这怎么作获得?该阻塞的调用,还能变得不阻塞?”
小明答道:“我来举个例子吧,不是直接调用一个系统 I/O 例程,而是调用一个应用级别的 I/O jacket 例程,这个 jacket 例程中的代码会检查而且肯定 I/O 设备是否是正忙,若是忙的话,就在用户态下将该线程阻塞,而后把控制权交给另外一个线程。隔一段时间后再次检查 I/O 设备。就像你说的,最后仍是会执行阻塞调用,但使用 jacket 能够缩短被阻塞的时间。不过有些状况下是能够不被阻塞的,取决于具体的实现。”
小明停顿了一会,说:“用户级线程的概念大概就这么多,咱们接下来说内核级线程吧。”
“有了用户级线程的铺垫,内核级线程就好讲多了。如今咱们知道,许多操做系统都已经支持内核级线程了。为了实现线程,内核里就须要有用来记录系统里全部线程的线程表。当须要建立一个新线程的时候,就须要进行一个系统调用,而后由操做系统进行线程表的更新。固然了,传统的进程表也仍是有的。你想一想看,若是操做系统「看得见」线程,有什么好处?“
小白自信的回答:“操做系统内核若是知道线程的存在,就能够像调度多个进程同样,把这些线程放在好几个 CPU 核心上,就能作到实际上的并行了。”
“还有一点你没有说到,若是线程可见,那么假如线程 A 阻塞了,与他同属一个进程的线程也不会被阻塞。这是内核级线程的绝对优点。”
“那内核级线程就没有什么缺点吗?”
“缺点固然是有的,你想一想看,让操做系统进行线程调度,那意味着每次切换线程,就须要「陷入」内核态,而操做系统从用户态到内核态的转变是有开销的,因此说内核级线程切换的代价要比用户级线程大。还有很重要的一点——线程表是存放在操做系统固定的表格空间或者堆栈空间里,因此内核级线程的数量是有限的,扩展性比不上用户级线程。”
"内核级线程就这么点东西,我最后给你留一张图,你要是能看得懂,就说明你理解今天的概念了。"
小白得意地说:“我固然看得懂了,谢谢小明!”
但愿你在看完个人文章以后有所收获。
感谢你的阅读,咱们后会有期!
声明:原创文章,未经受权,禁止转载