线程是OS进行独立调试、执行的基本单位,进程是系统进行资源分配的基本单位,一个进程可以包含若干个线程。无论是系统进程还是用户进程,进程的创建、撤消、以及要求系统设备完成的IO操作,都是利用系统调用而进入内核,再由内核中相应处理程序予以完成。进程的切换同样是在内核的支持下实现的。即不论什么样的进程,它们都是在OS内核的支持下运行的,是与内核紧密相关的。
1. 线程的分类
线程根据其实现方式不同又可分为内核支持线程KST(Kernel Supported Threads)、用户级线程ULT(User Level Threads)、组合方式(ULT/KST)三种方式。
1.1 内核支持线程KST
内核支持线程是驻留在内核空间中的内核对象。用户进程或者系统进程中的线程的创建、撤消、切换都是在内核空间中实现的,并由内核为其分配线程控制块进行管理。每个用户线程会被映射或者绑定到一个内核线程,形成一对一的线程对应关系,如上图所示,内核可以感知线程的存在,其调度是以线程为基本单位。
内核支持线程的优点如下:
- 在多处理器系统中,内核能够同时调度同一个进程中的多个线程并行执行;
- 如果进程中一个线程被阻塞了,内核可以调度该进程中的其他线程占有处理器运行,也可以运行其它进程中的线程;
- 内核本身可以使用多线程技术,以提高系统的执行和效率。
缺点:
- 对于用户级线程切换时,由于用户进程的线程是在用户态运行的,需要从用户态转到内核态进行,导致切换时开销较大。
1.2 用户级线程ULT
用户级线程仅存在于用户空间中,对于这种线程的创建、撤消、线程之间的同步与通信等,无须利用系统调用来实现,也同样无须内核的运行,而是通过中间系统(线程库)在用户空间中来完成。由于不需要用户/内核态切换,线程切换速度比较快。但由于内核无法感知用户级线程的存在,其调度仍是以进程为单位进行的。当内核调度一个进程运行时,用户级线程库调度该进程的一个线程执行,如果时间片允许,进程的其他线程也可能被执行,该进程的多个线程共享该进程的运行时间片。如果一个线程需要进行i/o读写,该线程调用系统调用进入内核,在启动I/O设备后内核会把该进程值为阻塞态,并把CPU分配给其他进程。即使该进程的其他线程可以运行,内核也不会发现这一情况,在该进程的状态变为就绪之前内核不会调度该进程运行,因而属于该进程的线程都不可能运行,因而用户级线程的并行性会受到一定的限制。用户级线程是一种"多对一"的线程映。
用户级线程的优点:
- 线程不需要转换到内核空间。对一个进程而言,其所有线程的管理数据结构均在进程的用户空间中,管理线程切换的线程库也在用户地址空间中运行,节省了切换的开销和内核资源
- 调度算法可以是进程专用的。在不干扰系统调度的情况下,不同的进程可以根据自身的需要,来选择不同的调度算法对自己的线程进行管理和调度,而与操作系统的低级调度算法无关
- 用户级线程实现与OS平台无关,其属于用户程序的一部分,可以在不支持线程机制的操作系统平台上实现。
缺点:
- 系统调用阻塞问题。当线程因系统调用而被阻塞,进程内的所有线程都会阻塞(以进程为调度单位的原因)。
- 在单纯的用户级线程实现方式中,多线程不能利用多处理进行多重处理的优点。内核每次分配给一个进程仅有一个CPU,进程中只有一个线程可以执行,在该线程放弃CPU之前,其它线程只能等待。
1.3 组合方式
即将用户级线程和内核支持线程两种方式进行组合,在Solaris 中,用户创建的多个用户级线程被映射到一些内核线程上(多对多的线程映射),内核线程的数目可能少于用户级线程的数目,内核级线程的数目决定了该进程的并发度。
2. 线程实现方式
无论是进程还是线程,都必须直接或者间接取得内核的支持。内核支持线程可以利用系统调用为其服务,线程控制简单,用户级线程须借助于某和形式的中间系统来取得内核的服务,做在对线程的控制上稍复杂些。
2.1 内核支持线程的实现
在仅设置了内核支持线程的OS中,一种可能的线程控制方法是:系统在创建一个新进程时,便为它分配一个任务数据务PTDA(Per Task Data Area),其中包含若干个线程控制块TCB空间。在每一个TCB中可保存线程标识符、优先级、线程运行的CPU状态信息等。虽然这些信息与用户级线程TCB中的信息相同,但现在却是被保存在内核空间中。
2.2 用户线程的实现
用户级线程是用户空间实现的,它们都运行在一个中间系统上,当前有两种方式实现的中间系统:运行时系统和内核控制线程。
2.2.1 运行时系统(Running System)
所谓“运行时系统”,实质是用于管理和控制线程的函数集合,包括创建、撤销、线程的同步和通信的函数以及调度的函数。正因为这些函数,才能使用户线程与内核无关,所有函数都驻留在用户空间中,作为用户线程和内核之间的接口。在进行线程切换时,不需要转入核心态,而是由运行时系统中的线程切换来执行切换任务。由于用户线程不能使用系统调用,所以当线程需要系统资源时,将请求传送给运行时系统,由后者通过相应的系统调用来获取系统资源。
2.2.2 内核控制线程
这种线程又称为轻型进程LWP(Light Weight Process)。每一进程可拥有多个LWP,同用户级线程一样,每个LWP都有自己的数据结构(如TCB),其中包括线程标识符、优先级、状态,另外还有栈和局部存储区等。它们可以共享进程所拥有的资源。LWP可通过系统调用来获得内核提供的服务,这样一个用户级线程运行时,只要将它们连接到一个LWP上,此时它便具有了内核支持线程的所有属性,这种线程实现方式就是组合方式。
在一个系统中的用户线程数量可能很大,为了节省开销,不可能设置过多的LWP,而把这些LWP做成一个缓冲池,称为“线程池”。用户进程中的任一用户线程都可以连接到LWP池中的任何一个LWP上,为使每一用户级线程都利用LWP与内核通信,可以使多个用户级线程多路复用一个LWP,但只有当前连接到LWP上的线程才能与内核通信,其余进程或者阻塞,或者等待LWP。而每一个LWP都要连接到一个内核级线程上,这样,通过LWP可把用户级线程与内核线程连接起来,用户线程可通过LWP来访问内核,但内核所看到的总是多个LWP而看不到用户级线程,亦即,由LWP实现了内核与用户级线程之间的隔离,从而使用户级线程与内核无关。如下图所示。
参考:教材《计算机操作系统》第三版 汤子瀛 P77