FreeBSD下的线程
由于Unix系统本身并不存在线程这个概念,那么在Unix下引入线程就有两种比较直接的方式。第一种方式是不在内核中实现线程,而在用户程序本身中实现线程,这实际是对线程的一种模拟,线程之间的切换和调度是在用户的进程内部进行的,这种方式就被称为用户空间的线程。这种线程的好处是实现非常简单,而且性能也非常好,因为线程之间的切换都在用户进程内部进行,切换开销比较小。
它的缺点也很明显,首先就是不能充分利用高端服务器系统的SMP多处理器的优点,因为一个进程只能由一个处理器处理,第二点由于用户空间线程是在用户空间切换,某个线程遇到一个需要阻塞的系统调用而就会造成整个进程被阻塞,因而其他线程也被阻塞,这中情况实际在线程的概念中是不允许的,但实际实现中很难被百分之百避免。
第二种实现方式是通过修改进程的实现方式来完成,可以使用不完全的进程创建方式创建共享数据空间的进程,在Linux下这种系统调用为clone(),而在FreeBSD下它为rfork()。这种方式是Linux的基本线程处理方式。
用进程的方式实现的线程,能够利用多处理器的优点,也能达到共享数据的目的,但它的线程切换开销实际是和进程是一样的,需要内核参与因而开销较大。而且线程实际上是进程,在很多方面还存在问题,例如不同线程的进程标识号不一致,多线程程序的执行优先级要比单线程的要高,线程之间的同步需要比较大的开销等等,因而也不能算理想的线程实现。
在线程方面做得比较好的操作系统有 Windows NT/2000和SUNSolaris,对于Windows NT/2000,它们本身就是以线程为基础进行任务调度的,性能较好比较正常。SUN Solaris的线程实现则提供了另一个很好的参考平台,在Solaris中,事实上是混用了用户空间的线程和进程基础的线程的,使用多个进程运行更多的线程,因此可以综合利用多处理器的优点和在进程内部切换线程的优点。
因此, FreeBSD既能利用用户空间的线程切换开销小的优点,又能利用内核线程支持多处理器的优点。实现的方法是将系统内核的最基本的调度实体从进程改变为线程。简单的讲,FreeBSD进程中的多个线程按照系统的处理器数量在内核中分为相应数量的组,可以分别在多个处理器上执行,这样就可以充分发挥多处理器的优势。而线程之间的切换是由用户空间来调度的,但用户空间的线程调度程序可以和内核交互,例如当一个线程阻塞的时候,内核将通知线程调度程序启动另一个线程,而内核进行进程调度的时候,就直接针对线程组切换到正确的线程。
虽然原理上并不复杂,但实现起来相当困难,最基本的一点就是需要将内核调度的实体从原先的进程改变为线程,这样内核调度的时候就可以直接切换到线程队列中的正确线程,而不是首先切换到进程,再在进程内切换到线程。这一点工作就需要将原本很多针对进程的内核设计进行改变,例如内核代码中所有涉及进程控制块
PCB的地方都需要改动,因为系统不再使用进程控制块PCB来执行任务调度,而是使用线程控制块TCB来作为最基本的调度实体。
因此,FreeBSD是一个真正的内核级别支持多线程的系统。
Linux下的线程
线程是在进程的基础上进一步的抽象,也就是说一个进程分为两个部分:线程集合和资源集合。线程是进程中的一个动态对象,它应该是一组独立的指令流,进程中的所有线程将共享进程里的资源。但是线程应该有自己的私有对象:比如程序计数器、堆栈和寄存器上下文。线程分为三种类型:
内核线程、轻量级进程和用户线程。