进程与线程
20世纪60年代中期,设计多道程序OS时,引入了进程概念,利用进程解决了单处理机环境下的程序并发执行的问题。在此后20年后多道程序OS中一直都是以进程作为能拥有资源和独立调度的基本单位的。
直到80年代中期,提出了比进程更小的基本概念——线程的概念,试图用它来提高程序并发执行的程度。特别进入20世纪90年代后,多处理机系统得到迅速发展,由于线程能更好的提高并行执行程度,因而多处理机基本都引入了线程,来改善OS的性能
进程两个基本属性(也是进程实现并发的基础):
- 可拥有资源的独立单位。
- 可独立调度和分派的基本单位。
进程的时空开销:创建进程、进程切换、撤销进程。
其中进程切换时,需要保留当前进程的CPU环境、设置新选中进程的CPU环境,因而话费了不少处理机时间。
缺点:进程是资源的拥有者,因而在创建撤销和切换中,且同需要付出较大的时空开销,这就限制了进程的数目,而且进程切换不宜过于频繁,从而限制了并发程度的进一步提高。
随着计算机体系结构的发展,对称多处理机(SMP)计算机系统的出现,传统的进程概念的缺点导致不能够充分发挥出多CPU的并行能力,为了提高系统性能,不少研究操作系统的学者们想到,设法将进程两个基本属性分开,即拥有资源的基本单位,与调度和分派的基本单位分开,让拥有资源的基本单位,不对之施以频繁的切换。正是这种思想指导下,形成了线程的概念。此后,在引入线程的OS中
进程:可拥有资源的独立单位
线程:可独立调度和分派的基本单位
引入线程与传统进程的OS比较(6个方面):
1.调度的基本单位:可拥有资源的基本单位没有变,但线程成为调度和分派的基本单位,线程切换仅需保存和设置少量的寄存器内容,切换代价远低于进程。
2.并发性:引入线程后,进程之间可以并发执行,同一进程内部线程也能并发执行,不同进程中的线程也能并发执行。并发的粒度更小了,如计算机流水线技术一样,使得OS有更好的并发性,更加有效的提高了系统资源的利用率和系统的吞吐量。
3.拥有资源:进程成为了线程的容器,线程本省并不拥有系统资源,仅保留仅有一点必不可少的、能保证独立运行的资源(如PC,TCB,存储局部变量的栈)。线程能访问所在进程中所有资源,线程切换不能超出进程拥有的资源,也不会引起进程切换,但进程的切换必将引起线程的切换。
4.独立性:由于进程资源被线程共享(线程安全),所以线程之间的独立性比进程之间独立性低得多
5.系统开销:线程创建切换撤销比进程创建切换撤销代价要小得多,甚至一些虚拟机实现线程复用。
6.支持多处理机系统:引入线程后,可以将一个进程的多个线程分配到多个处理机上,使他们并行执行,加快了进程的完成。传统的进程模型,一个进程只能运行在一个处理机上,不能充分发挥出处理机的并行性能。
内核支持线程KST与用户级线程ULT
OS内核:操作系统将OS划分为若干层次,再将OS的不同功能分别设置在不同的层次中。通常将一些与硬件紧密相关的模块(如中断处理程序等)、各种常用设备的驱动程序以及运行频率较高的模块(如时钟管理、进程调度和许多模块所公用的一些基本操作),都安排在紧密硬件的软件层次中,将它们常驻内存,即通常被称为的OS内核。
内核支持线程:在OS中的所有进程,无论是系统进程还是用户进程,都是在操作系统内核的支持下运行的,与内核紧密相关的。而内核支持线程KST同样是内核支持下运行的,他们的创建、阻塞、撤销和切换等,也都是在内核空间实现的(限制了内核支持线程的数目)。为了对内核线程进行控制和管理,在内核空间也为每个内核线程设置了一个线程控制块,内核根据该控制块而感知某线程的存在,并对其加以控制,
优点:
1在多处理器系统中,内核能够同时调度同一进程中的多个线程并行执行
2如果进程中的一个线程被阻塞了,内核可以调度该进程中的其他线程占有处理器运行,也可以运行其他进程中的线程。
3内核支持线程具有很小的数据结构和堆栈,线程切换比较快,切换开销小;
4内核本身也可以采用多线程技术,可以提高系统的执行速度和效率。
缺点:
对于用户的线程切换而言,其模式切换的开销较大,
在同一个进程中,从一个线程切换到另一个线程时,需要从用户态转到核心态,在核心态进行线程调度后,再转到用户态运行,这是因为用户进程的线程在用户态运行,而线程调度和管理是在内核实现的,系统开销较大。
用户级线程:用户空间实现的。对线程的创建、撤销、同步和通信等功能。都无需内核的支持,即用户级线程与内核无关。即用户级线程数目可以很大,逻辑上受内存限制。线程任务控制块设置在用户空间,线程操作不需要内核支持,所以内核完全不知道用户级线程存在。
因此时间轮转调度算法是针对内核支持线程而言的,即必存在内核-用户线程映射模型,用户级线程在对应内核支持线程的时间片内切。
对于仅设置用户线程的系统,调度仍是以进程为单位进行的,内核在采用时间轮转调度算法时,各进程轮流执行一个时间片,进程内的线程在时间片内运行。
对于设置了内核支持线程的系统,调度以内核支持线程为单位进行的,内核采用时间轮转调度算法时,各内核支持线程轮流执行一个时间片,内核支持线程映射的用户线程在对应的内核支持线程的时间片内运行。
例子:进程A只有一个内核支持线程,进程B有100个内核支持线程,线程B获得的CPU时间是A的100倍。若此时进程A有1个用户级线程,进程B有100个用户级线程,进程A、进程B用户级线程获得CPU平均时间一样。
优点:
1.用户级线程的切换不需要转换到内核空间,仅需在用户态运行,节省了模式切换的开销
2.调度算法是进程或者内核支持线程专用的,用户级线程可选择自己的调度算法,灵活
3.对于用户级线程的线程管理算法,与内核无关,所以所有的应用程序可以复用此代码。
缺点:
1.系统调用的阻塞问题。大多数系统调用将使进程阻塞,因此,当线程执行一个系统调用时,不进该线程阻塞,进程内所有线程也会被阻塞。内核支持线程的实现方式下,进程中的其他线程仍然可以运行。
2.在仅设置用户级线程实现方式中,多线程不能利用多处理机的并行优点(调度仍是以进程为单位进行的,一个CPU对应一个进程,进程内线程只能在此CPU下运行)。
三种连接模型(多对一,一对一,多对多)
多对一模型:多个用户级线程映射到一个内核支持线程。
优点:线程管理开销小、效率高;
缺点:由于每次只允许一个用户级线程进行映射,当用户级线程在访问内核时发生阻塞,整个进程都会被阻塞;另外,内核支持线程对应一个CPU,所以多线程不能同时在多个处理器并行;
一对一模型:一个用户级线程映射到一个内核支持线程。
优点:用户级线程访问阻塞时,其他用户级线程不会阻塞,提供了比多对一更好的并发能力,适合多处理器并行。
缺点:创建一个用户级线程就要创建一个内核支持线程,开销大,线程数目受限制。
多对多模型:多个用户级线程映射到到同样数量或者更少数量的内核支持线程上。
优点:结合了两种模型的优点,可以向一对一模型一样,使一个进程的多个线程并行地运行在多处理器系统上,也可以像多对一模型一样,减少线程的管理开销和提高效率。
Java多线程模型是一对一模型。
golang多线程模型是多对多模型。
参考自《操作系统原理》