在传统的操作系统中,进程是系统进行资源分配的基本单位,按进程为单位分给存放其映象所需要的虚地址空间、执行所需要的主存空间、完成任务需要的其他各类外围设备资源和文件。同时,进程也是处理器调度的基本单位,进程在任一时刻只有一个执行控制流,通常将这种结构的进程称单线程(结构)进程(single threaded process) 。
首先来考察一个文件服务器的例子,当它接受一个文件服务请求后,由于等待磁盘传输而经常被阻塞,假如不阻塞可继续接受新的文件服务请求并进行处理,则文件服务器的性能和效率便可以提高,由于处理这些请求时要共享一个磁盘缓冲区,程序和数据,要在同一个地址空间中操作。这一类应用非常多,例如,航空售票系统需要处理多个购票和查询请求,这些信息都与同一个数据库相关;而操作系统在同时处理许多用户进程的查询请求时,都要去访问数据库所在的同一个磁盘。对于上述这类基于同数据区的同时多请求应用,用单线程结构的进程难以达到这一目标,即使能解决问题代价也非常高,需要寻求新概念、提出新机制。随着并行技术、网络技术和软件设计技术的发展,给并发程序设计效率带来了一系列新的问题,主要表现在:
这就迫切要求操作系统改进进程结构,提供新的机制,使得应用能够按照需求在同一进程中设计出多条控制流,多控制流之间可以并行执行,多控制流切换不需通过进程调度;多控制流之间还可以通过内存区直接通信,降低通信开销。这就是近年来流行的多线程(结构)进程(multiple threaded process) 。如果说操作系统中引入进程的目的是为了使多个程序能并发执行,以改善资源使用率和提高系统效率,那么,在操作系统中再引入线程,则是为了减少程序并发执行时所付出的时空开销,使得并发粒度更细、并发性更好。这里解决问题的基本思路是:把进程的两项功能--“独立分配资源”与“被调度分派执行”分离开来,前一项任务仍由进程完成,它作为系统资源分配和保护的独立单位,不需要频繁地切换;后一项任务交给称作线程的实体来完成,它作为系统调度和分派的基本单位,会被频繁地调度和换,在这种指导思想下,产生了线程的概念。
传统操作系统一般只支持单线程(结构)进程,如 MS-DOS 支持单用户进程,进程是单线程的;传统的 UNIX 支持多用户进程,每个进程也是单线程的。目前,很多著名的操作系统都支持多线程(结构)进程,如: Solaris、 Mach、 SVR4、 OS/390、 OS/2、
WindowNT、 Chorus 等; JAVA 的运行引擎则是单进程多线程的例子。许多计算机公司都推出了自己的线程接口规范,如 Solaris thread 接口规范、 OS/2 thread 接口规范、Windows NT thread 接口规范等; IEEE 也推出了 UNIX 类操作系统的多线程程序设计标准 POSIX 1003.4a。事实上,线程概念不仅局限于操作系统中,在程序设计语言、数据库管理系统和其他一些应用软件中,也通过引入线程来改善系统和应用程序的性能,可以相信多线程技术在程序设计中将会被越来越广泛地采用。
1、多线程环境中的进程概念在传统操作系统的单线程进程中,进程和线程概念可以不加区别。下图 给出了单线程进程的内存布局和结构,它由进程控制块和用户地址空间,以及管理进程执行的调用/返回行为的系统堆栈或用户堆栈构成。一个进程的结构可以划分成两个部分:对资源的管理和实际的指令执行序列。显然,采用并发多进程程序设计时,并发进程之间的切换和通信均要借助于操作系统的进程管理和进程通信机制,因而,实现代价较大,而较大的进程切换和进程通信代价,又进一步影响了并发的粒度。
单线程进程的内存布局
设想是否可以把进程的管理和执行任务相分离,如下图 所示,让进程是操作系统中进行保护和资源分配的单位,允许一个进程中包含多个可并发执行的控制流,这些控制流切换时不必通过进程调度,通信时可以直接借助于共享内存区,每个控制流称为一个线程,这就是并发多线程程序设计。
管理和执行相分离的进程
多线程进程的内存布局如下图所示,在多线程环境中,仍然有与进程相关的内容是 PCB 和用户地址空间,而每个线程除了有独立堆栈,以及包含现场信息和其他状态信息外,也要设置线程控制块 TCB(Thread Control Block)。线程间的关系较为密切,一个进程中的所有线程共享其所属进程拥有的资源,它们驻留在相同的地址空间,可以存取相同的数据。例如,当一个线程改变了主存中一个数据项时,如果这时其他线程也存取这个数据项,它便能看到相同的结果。
多线程进程
最后,给出多线程环境中进程的定义:进程是操作系统中进行保护和资源分配的基本单位。它具有:
线程是操作系统进程中能够独立执行的实体(控制流),是处理器调度和分派的基本单位。线程是进程的组成部分,每个进程内允许包含多个并发执行的实体(控制流),这就是多线程。同一个进程中的所有线程共享进程获得的主存空间和资源,但不拥有资源。线程具有:
线程的主要特性是:
如下图所示,进程可以划分为两个部分:资源集合和线程集合。进程要支撑线程运行,为线程提供地址空间和各种资源,它封装了管理信息,包括对指令代码、全局数据和 I/O 状态数据等共享部分的管理。线程封装了执行信息,包括对 CPU 寄存器、执行栈(用户栈、内核栈)和局部变量、过程调用参数、返回值等线程私有部分的管理。由于线程具有许多传统进程所具有的特征,所以,也把线程称为轻量进程LWP(Light-Weight Process)。
线程的内存布局
下面用一个日常生活中的例子来说明多线程结构进程可以进一步提高系统的并发性。某大厦的装璜工程可作为一个“进程”运行,下有许多工程队,如瓦工队、木工队、水电工队、油漆工队等,每个工程队作为一个“线程”运行。“进程”负责采购资源(源料)和工程管理,有源料时这些工程队可以按进度齐头并进同时工作(多线程并行执行),以加快装璜进度。缺少源料时,如缺少水泥、木材、水管、油漆之一时,相应工程队等待(线程被阻塞),而可以调度其他工程队(线程)工作。从而,提高了整个系统(装璜工程)的并发性,加快了工程进度。
和进程类似,线程也有生命周期,因而,也存在各种状态。从调度需要来说,线程的关键状态有:运行、就绪和等待。另外,线程的状态转换也类似于进程。由于线程不是资源的拥有单位,挂起状态对线程是没有意义的,如果一个进程挂起后被对换出主存,则它的所有线程因共享了进程的地址空间,也必须全部对换出去。可见由挂起操作引起的状态是进程级状态,不作为线程级状态。类似地,进程的终止会导致进程中所有线程的终止。
进程中可能有多个线程,当处于运行态的线程执行中要求系统服务,如执行一个I/O 请求而成为等待态时,那么,多线程进程中,是不是要阻塞整个进程,即使这时还有其他就绪的线程?对于某些线程实现机制,所在进程也转换为阻塞态;对于另一些线程实现机制,如果存在另外一个处于就绪态的线程,则调度该线程处于运行状态,否则进程才转换为等待态。显然前一种做法欠妥,丢失了多线程机制的优越性,降低了系统的灵活性。
多线程进程的进程状态是怎样定义的?由于进程不是调度单位,不必划分成过细的状态,如 Windows 操作系统中仅把进程分成可运行和不可运行态,挂起状态属于不可运行态。
多线程技术是利用线程包(库)来提供一整套有关线程的原语集来支持多线程运行的,有的操作系统直接支持多线程,而有的操作系统不支持多线程。因而,线程包(库)可以分成两种:用户空间中运行的线程包(库)和内核中运行的线程包(库)。一般地说,线程包(库)至少应提供以下功能的原语调用:孵化、封锁、活化、结束、通信、同步、互斥等,以及切换(保护和恢复线程上下文)的代码,调度(对线程的调度算法及实施处理器调度)的代码。同时应提供一组与线程有关的应用程序编程接口 API,支持应用程序创建、调度、撤销和管理线程的运行。
基本的线程控制原语有:
对于在用户空间运行的线程包(库),由于它完全在用户空间中运行,操作系统内核对线程包(库)不可见, 而仅仅知道管理的是一般的单线程进程。 这种情况下, 线程包(库)起到一个微内核的作用,实质上是多线程应用程序的开发和运行支撑环境。优点是:节省了内核的宝贵资源,减少内核态和用户态之间的切换,因而,线程(包)库的运行开销小效率高;容易按应用特定需要选择进程调度算法,也就是说,线程库的线程调度算法与操作系统的低级调度算法是无关的;能运行在任何操作系统上。缺点是:当线程执行一个系统调用时,不仅该线程被阻塞,而且,进程内的所有线程会被阻塞,这种多线程应用就不能充分利用多处理器的优点。
在内核中运行的线程包(库),则是通过内核来管理线程包(库)的。内核中不但要保存进程的数据结构,也要建立和维护线程的数据结构及保存每个线程的入口,线程管理的所有工作由操作系统内核来实现。由内核专门提供一组应用程序编程接口(API),供开发者开发多线程应用程序。优点是:能够调度同一进程中多个线程同时在处理器上并行执行,充分发挥多处理器的能力,若进程中的一个线程被阻塞了,内核能调度同一进程的其他线程占有处理器运行,也可以调度其他进程中的线程运行。缺点是:在同一进程中, 控制权从一个线程传送到另一个线程时需要用户态-内核态-用户态的模式切换,系统开销较大。
基于上述两种线程包(库)可把线程的实现分成三类:用户级线程 ULT(User LevelThread) (如 POSIX 的 P-threads、Java 的线程库)和内核级线程 KLT(Kernel Level Thread)(如 Windows2000/XP 、 OS/2 和 Mach C-thread)。也有一些系统(如 Solaris)提供了混合式,同时支持 ULT 和 KLT 两种线程的实现。下面将会进一步讨论三种线程包(库)的实现方法。
近年来,已出现了具有支持多线程运行的微处理器体系结构,称为超线程技术。如现在的多核处理器,是具有超线程技术的微处理器。它的微处理器包含两个逻辑上独立的微处理器,能够同时执行两个独立的线程代码流。每个均可被单独启停、中断和被调度执行特定的线程,而不会影响芯片上另一个逻辑上独立的处理器共享微处理器内核的执行资源,包括:引擎、高速 cache、总线接口和固件等。
在一个进程中包含多个并行执行的控制流,而不是把多个可并发执行的控制流一一分散在多个进程中,这是并发多线程程序设计与并发多进程程序设计的不同之处。并发多线程程序设计的主要优点是使系统性能获得很大提高,具体表现在:
一个进程中可包括若干线程,每个线程拥有自己的程序计数器和堆栈,用来跟踪程序运行的轨迹,这些线程分享了进程的处理器时间配额。在单处理器系统中,一个线程先执行,接着同一进程或其他进程中的另一个线程执行,这很象分时系统的做法。在多处理器系统中,同一进程或不同进程中的多个线程真正并行执行。一个进程中的所有线程都在同一个地址空间中,共享全局变量和各种资源,如打开文件、定时器、信号量、内存区。
进程中的线程可有多种组织方式:第一种是调度员/工作者模式,进程中的一个线程担任调度员的角色接受和处理工作请求,其他线程为工作者线程。由调度员线程分配任务和唤醒工作者线程工作。第二种是组模式,进程中的各个线程都可以取得并处理该请求,不存在调度者线程。有时每个线程被设计成专门处理特定的任务,同时建立相应的任务队列。第三种是流水线模式,线程排成一个次序,第一个线程产生的数据传送给下一个线程处理,依次类推,数据沿排定的卜次序由线程依次传递以完成请求的任务。
多线程技术在现代计算机软件中得到了广泛的应用,取得了较好的效果。下面举例说明多线程技术的一些主要应用:
本文主要来自孙钟秀老师主编的《操作系统教程》书本。