线程的3种实现方式
在引入线程的操作系统中,进程是资源分配的基本单位,线程是独立调度的基本单位。在同一进程中,线程的切换不会引起进程切换。在不同进程中进行线程切换,如从一个进程内的线程切换到另一个进程中的线程时,会引起进程切换。
线程分为两种:
名称 | 描述 |
---|---|
用户级线程(User-Level Thread, ULT) | 由应用程序所支持的线程实现, 对内核不可见 |
内核级线程(Kernel-Level Thread, KLT) | 内核级线程又称为内核支持的线程 |
组合线程(Hybrid Multithreading)是一种别的实现方式而不是线程的种类。
用户级线程
线程的用户级线程实现方式
有关线程管理的所有工作都由应用程序完成,内核意识不到多线程的存在。
用户级线程仅存在于用户空间中,此类线程的创建、撤销、线程之间的同步与通信功能,都无法利用系统调用来实现。
应用程序需要通过使用线程库来控制线程。 通常,应用程序从单线程起始,在该线程中开始运行,在其运行的任何时刻,可以通过调用线程库中的派生创建一个在相同进程中运行的新线程。由于线程在进程内切换的规则远比进程调度和切换的规则简单,不需要进行用户态/核心态切换,所以切换速度快。
用户线程多见于一些历史悠久的操作系统,例如Unix操作系统。
因为用户级线程驻留在用户空间,且管理和控制它们的线程也在用户空间,每个线程并不具有自身的线程上下文,所以它们对于操作系统是不可见的,这也就是它无法被调度到处理器内核的原因。
操作系统认为所有的进程都是单线程的,因此,就线程的同时执行而言,任意给定时刻每个进程只能够有一个线程在运行,而且只有一个处理器内核会被分配给该进程。对于一个进程,可能有成千上万个用户级线程,但是它们对系统资源没有影响。运行时库调度并分派这些线程。
下图说明了用户级线程的实现方式,
如同在图中看到的那样,库调度器从进程的多个线程中选择一个线程,然后该线程和该进程与一个内核线程关联起来。内核线程将被操作系统调度器指派到处理器内核。用户级线程是一种”多对一”的线程映射。
用户线程的优点
- 可以在不支持线程的操作系统中实现。
- 创建和销毁线程、线程切换代价等线程管理的代价比内核线程少, 因为保存线程状态的过程和调用程序都只是本进程空间的操作
- 允许每个进程定制自己的调度算法,线程管理比较灵活。
- 线程能够利用的表空间和堆栈空间比内核级线程多
- 不需要trap,不需要上下文切换(context switch),也不需要对内存高速缓存进行刷新,使得线程调用非常快捷
- 线程的调度不需要内核直接参与,控制简单。
用户线程的缺点
- 线程发生I/O或页面故障引起的阻塞时,如果调用阻塞系统调用则内核由于不知道有多线程的存在,而会阻塞整个进程从而阻塞所有线程, 因此同一进程中只能同时有一个线程在运行(在用户级线程中,每个进程里的线程表在运行时由系统管理。当一个线程转换到就绪状态或阻塞状态时,在该线程表中存放重新启动该线程所需的信息,与内核在进程表中存放的进程的信息完全一样)。
- 页面失效也会产生类似的问题。
- 一个单独的进程内部,没有时钟中断,所以不可能用轮转调度的方式调度线程。
- 资源调度按照进程进行,多个处理机下,同一个进程中的线程只能在同一个处理机下分时复用,因此对于多线程并不能被多核系统加速。
内核级线程
线程的内核级线程实现
内核线程建立和销毁都是在内核的支持下运行,由操作系统负责管理,通过系统调用完成的。
线程管理的所有工作由内核完成,应用程序没有进行线程管理的代码,只有一个到内核级线程的编程接口。内核为进程及其内部的每个线程维护上下文信息,调度也是在内核基于线程架构的基础上完成。
内核线程驻留在内核空间,它们是内核对象。操作系统调度器管理、调度并分派这些线程。运行时库为每个用户级线程请求一个内核级线程,将用户进程映射或绑定到上面。用户线程在其生命期内都会绑定到该内核线程。一旦用户线程终止,两个线程都将离开系统。这被称作”一对一”线程映射。
内核空间内为每一个内核支持线程设置了一个线程控制块(TCB),内核根据该控制块,感知线程的存在,并进行控制。
操作系统的内存管理和调度子系统必须要考虑到数量巨大的用户级线程。您必须了解每个进程允许的线程的最大数目是多少。操作系统为每个线程创建上下文。进程的每个线程在资源可用时都可以被指派到处理器内核,这些线程可以在全系统内进行资源的竞争。
内核线程的特点
当某个线程希望创建一个新线程或撤销一个已有线程时,它进行一个系统调用
内核线程的优点
- 多处理器系统中,内核能够并行执行同一进程内的多个线程。
- 如果进程中的一个线程被阻塞,能够切换同一进程内的其他线程继续执行(用户级线程的一个缺点)。
- 所有能够阻塞线程的调用都以系统调用的形式实现,代价较大。
- 当一个线程阻塞时,内核根据选择可以运行另一个进程的线程。
- 信号是发给进程而不是线程的,当一个信号到达时,应该由哪一个线程处理它?线程可以“订阅”它们感兴趣的信号(订阅发布模型)。
用户级线程和内核级线程的区别
- 内核支持线程是OS内核可感知的,而用户级线程是OS内核不可感知的。
- 用户级线程的创建、撤消和调度不需要OS内核的支持,是在语言(如Java)这一级处理的;
而内核支持线程创建、撤消和调度都需OS内核提供支持,而且与进程的创建、撤消和调度大体是相同的。 - 用户级线程执行系统调用指令时将导致其所属进程被中断,而内核支持线程执行系统调用指令时,只导致该线程被中断。
- 在只有用户级线程的系统内,CPU调度还是以进程为单位,处于运行状态的进程中的多个线程,由用户程序控制线程的轮换运行;在有内核支持线程的系统内,CPU调度则以线程为单位,由OS的线程调度程序负责线程的调度。
- 用户级线程的程序实体是运行在用户态下的程序,而内核支持线程的程序实体则是可以运行在任何状态下的程序。
组合方式
在一些系统中,使用组合方式的多线程实现, 线程创建完全在用户空间中完成,线程的调度和同步也在应用程序中进行。一个应用程序中的多个用户级线程被映射到一些(小于或等于用户级线程的数目)内核级线程上。
下图说明了用户级与内核级的组合实现方式, 在这种模型中,每个内核级线程有一个可以轮流使用的用户级线程集合