线程简介

来源:互联网 发布:云警联网报警主机编程 编辑:程序博客网 时间:2024/06/05 11:53

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下的线程

    线程是在进程的基础上进一步的抽象,也就是说一个进程分为两个部分:线程集合和资源集合。线程是进程中的一个动态对象,它应该是一组独立的指令流,进程中的所有线程将共享进程里的资源。但是线程应该有自己的私有对象:比如程序计数器、堆栈和寄存器上下文。
    线程分为三种类型:
    内核线程、轻量级进程和用户线程。

内核线程

    它的创建和撤消是由内核的内部需求来决定的,用来负责执行一个指定的函数,一个内核线程不需要和一个用户进程联系起来。它共享内核的正文段核全局数据,具有自己的内核堆栈。它能够单独的被调度并且使用标准的内核同步机制,可以被单独的分配到一个处理器上运行。内核线程的调度由于不需要经过态的转换并进行地址空间的重新映射,因此在内核线程间做上下文切换比在进程间做上下文切换快得多。

轻量级进程

    轻量级进程是核心支持的用户线程,它在一个单独的进程中提供多线程控制。这些轻量级进程被单独的调度,可以在多个处理器上运行,每一个轻量级进程都被绑定在一个内核线程上,而且在它的生命周期这种绑定都是有效的。轻量级进程被独立调度并且共享地址空间和进程中的其它资源,但是每个LWP都应该有自己的程序计数器、寄存器集合、核心栈和用户栈。

用户线程

    用户线程是通过线程库实现的。它们可以在没有内核参与下创建、释放和管理。线程库提供了同步和调度的方法。这样进程可以使用大量的线程而不消耗内核资源,而且省去大量的系统开销。用户线程的实现是可能的,因为用户线程的上下文可以在没有内核干预的情况下保存和恢复。每个用户线程都可以有自己的用户堆栈,一块用来保存用户级寄存器上下文以及如信号屏蔽等状态信息的内存区。库通过保存当前线程的堆栈和寄存器内容载入新调度线程的那些内容来实现用户线程之间的调度和上下文切换。内核仍然负责进程的切换,因为只有内核具有修改内存管理寄存器的权力。用户线程不是真正的调度实体,内核对它们一无所知,而只是调度用户线程下的进程或者轻量级进程,这些进程再通过线程库函数来调度它们的线程。当一个进程被抢占时,它的所有用户线程都被抢占,当一个用户线程被阻塞时,它会阻塞下面的轻量级进程,如果进程只有一个轻量级进程,则它的所有用户线程都会被阻塞。也就是说Linux中,每个线程都有一个task_struct,所以线程和进程可以使用同一调度器调度。其实Linux核心中,轻量级进程和进程没有质上的差别,因为Linux中进程的概念已经被抽象成了计算状态加资源的集合,这些资源在进程间可以共享。如果一个task独占所有的资源,则是一个HWP,如果一个task和其它task共享部分资源,则是LWP。clone的用法和pthread_create有些相似,两者的最根本的差别在于clone是创建一个LWP,对核心是可见的,由核心调度,而pthread_create通常只是创建一个用户线程,对核心是不可见的,由线程库调度。linux的pthread_create最终调用clone,pthread_create调用clone,并把开辟一个stack作为参数thread 建立, 同步,销毁等由线程库负责。
0 0
原创粉丝点击