内核支持线程 & 用户级线程
各种进程,包括系统进程和用户进程,它们的创建、撤销和I/O操作、切换等,都是使用系统调用进入内核,再由内核的相应处理程序完成的——可以说所有进程都是在OS内核的支持下运行。
内核支持线程(Kernel Supported Threads)就是在内核支持下运行的。使用内核支持线程的情况下,用户进程和系统进程中的线程的创建、撤销和切换都是由内核在内核空间实现。内核空间会为每一个内核支持线程设置一个TCB(线程控制块,Thread Control Block), 内核通过TCB感知一个线程的存在 。
内核支持线程的优点如下:
相应地,它也有一些缺点:
下面说一种在仅设置了内核支持线程的OS中可行的线程控制方法。
相比起内核支持线程是在内核空间操作的,用户级线程是存在于用户空间中。
用户级线程与内核没有什么关系,它的创建、撤销、线程间同步与通信,都无需使用系统调用。它的TCB自然也设置在用户空间,因而内核也不知道它的存在。
在仅设置了用 户级线程的系统 里, 调度以进程而不是线程为单位 ,而在仅设置 系统级线程 的系统里是 以线程为单位 的。这是它比较傻的地方,由于系统调用需要阻塞进程,当进程里的一个线程执行系统调用时,作为调度单位的整个进程都会被阻塞!而如上所说,内核支持线程中仅阻塞系统调用的线程而其他线程仍然能运行。
而在同样由于以线程为单位的原因,在多CPU系统里,内核每次只能给进程分配一个CPU,在进程里的多个线程就不能同时利用多个CPU了。
既然它有这么多缺点,自然也有许多好处来给人们采用它的理由。上面说到内核支持的用户线程切换时需要在内核态和用户态间切换,而用户级线程并不存在这个问题——所有管理用户级线程的数据结构都在用户空间中,切换也在用户空间完成,可以省一笔切换模式的开销。
另一个有点是用户级线程的实现和OS平台无关——管理线程的一切操作都在用户空间,进程还可以根据自己的需要定义自己专用的线程调度算法,这一切都不与内核的底层实现有什么关系,用户级线程甚至可以在不支持线程机制的操作系统实现!
现在我们总结一下上面讲的用户级线程的优缺点:
优点:
缺点:
用户级系统靠一个中间系统实现——运行时系统(Runtime System)。
我们来介绍一下它。运行时系统是一套管理和控制线程的函数集合,里面的各种函数有用于创建和撤销线程的、用于线程同步和通信的、切换线程、其他线程调度的等等。我们把运行时系统所有的函数都存在用户空间,作为与内核的接口——就是说用户级线程只管调用运行时系统的函数,而这些函数会自己解决怎么和内核交流和完成需要的功能。对于不同OS内核,运行时函数要采用不同的底层实现;而对于用户级线程来说调用的都是一样的函数,它与内核是无关的。
需要注意的是,系统资源最终都是由内核管理分配,用户级线程需要资源的时候也是通过调用运行时系统的函数找内核要。
既然内核支持线程和用户级线程都有自己的优缺点,把它们恰当组合起来我们可以扬长避短,得到更好的性能。
这种组合是通过内核控制线程(又称为轻型进程,Light Weight Process,LWP)实现的。
我们使系统拥有若干个内核控制线程,把它们做成一个缓冲池,称为线程池。当一个用户级线程需要内核提供的服务时,就连接到一个内核控制线程上,通过它来获得内核支持。这样一来,用户级线程也有了内核支持的属性,我们就这样把两种线程实现方式组合了起来。
每一个内核控制线程都连接在一个内核支持线程上(有可能多个内核控制线程连接在一个内核支持线程上)。用户级线程要和内核通信时必须等待没有被占用的内核控制线程,如果没有空闲的就阻塞排队。
在内核级线程执行操作时,如果发生阻塞,则与之相连接的多个内核控制线程也将随之阻塞,进而使连接到内核控制线程上的用户级线程也被阻塞。而如果在一个进程中含有多个LWP,则当一个 LWP阻塞时,进程中的其他LWP可继续执行——如果它们连接的内核支持线程没有被阻塞的话。