进程的概念和特征
进程的概念
在多道程序环境下,允许多个程序井发执行,此时它们将失去封闭性,并具有间断性及不可再现性的特征。为此引入了进程( Process )的概念,以便更好地描述和控制程序的并发执行,实现操作系统的并发性和共享性(最基本的两个特性)。
为了使参与并发执行的程序(含数据)能独立地运行,必须为之配置一个专门的数据结构,称为进程控制块 (Process Control Block,PCB)。
系统利用 PCB 来描述进程的基本情况和运行状态,进而控制和管理进程。相应地,由程序段、相关数据段和 PCB三部分构成了进程映像(进程实体)。
所谓创建进程,实质上是创建进程映像中的 PCB;而撤销进程,实质上是撤销进程的 PCB。值得注意的是,进程映像是静态的,进程则是动态的。
⚠ 注意: PCB 是进程存在的唯一标志!
从不同的角度,进程可以有不同的定义,比较典型的定义有:
- 进程是程序的一次执行过程。
- 进程是一个程序及其数据在处理机.上顺序执行时所发生的活动。
- 进程是具有独立功能的程序在一个数据集合上运行的过程,它是系统进行资源分配和调度的一个独立单位。
引入进程实体的概念后,我们可以把传统操作系统中的进程定义为:“进程是进程实体的运行过程,是系统进行资源分配和调度的一个独立单位。”
读者要准确理解这里说的系统资源。它指处理机、存储器和其他设备服务于某个进程的“时间”,例如把处理机资源理解为处理机的时间片才是准确的。因为进程是这些资源分配和调度的独立单位,即“时间片”分配的独立单位,这就决定了进程一定是一个动态的、过程性的概念。
进程的特征
进程是由多道程序的并发执行而引出的,它和程序是两个截然不同的概念。进程的基本特征是对比单个程序的顺序执行提出的,也是对进程管理提出的基本要求。
- 动态性。进程是程序的一次执行,它有着创建、活动、暂停、终止等过程,具有一定的生命周期,是动态地产生、变化和消亡的。动态性是进程最基本的特征。
- 并发性。指多个进程实体同时存于内存中,能在一段时间内同时运行。并发性是进程的重要特征,同时也是操作系统的重要特征。引入进程的目的就是为了使程序能与其他进程的程序并发执行,以提高资源利用率。
- 独立性。指进程实体是一个能独立运行、独立获得资源和独立接受调度的基本单位。凡未建立 PCB 的程序,都不能作为一个独立的单位参与运行。
- 异步性。由于进程的相互制约,使得进程具有执行的间断性,即进程按各自独立的、不可预知的速度向前推进。异步性会导致执行结果的不可再现性,为此在操作系统中必须配置相应的进程同步机制。
- 结构性。每个进程都配置一一 PCB 对其进行描述。从结构上看,进程实体是由程序段、数据段和进程控制块三部分组成的。
进程的组织
进程是一个独立的运行单位,也是操作系统进行资源分配和调度的基本单位。它由以下三部分组成,其中最核心的是进程控制块(PCB)。
进程的管理者(操作系统)所需的数据都在 PCB 中,程序本身的运行所需的数据在程序段和数据段中。
进程控制块
进程创建时,操作系统为它新建一个 PCB,该结构之后常驻内存,任意时刻都可以存取,并在进程结束时删除。PCB 是进程实体的一部分,是进程存在的唯一标志。
进程执行时,系统通过其 PCB 了解进程的现行状态信息,以便对其进行控制和管理;进程结束时,系统收回其 PCB,该进程随之消亡。操作系统通过 PCB 表来管理和控制进程。
当操作系统欲调度某进程运行时,要从该进程的 PCB 中查出其现行状态及优先级;在调度到某进程后,要根据其 PCB 中所保存的处理机状态信息,设置该进程恢复运行的现场,并根据其 PCB 中的程序和数据的内存始址,找到其程序和数据:进程在运行过程中,当需要和与之合作的进程实现同步、通信或访问文件时,也需要访问 PCB;当进程由于某种原因而暂停运行时,又需将其断点的处理机环境保存在 PCB 中。可见,在进程的整个生命期中,系统总是通过 PCB 对进程进行控制的,亦即系统唯有通过进程的 PCB 才能感知到该进程的存在。
下表是一个 PCB 的实例。 PCB 主要包括进程描述信息、进程控制和管理信息、资源分配清单和处理机相关信息等。各部分的主要说明如下:
| 进程描述信息 | 进程控制和管理信息 | 资源分配清单 | 处理机相关信息 |
|---|---|---|---|
| 进程标识符(PID) | 进程当前状态 | 代码段指针 | 通用寄存器值 |
| 用户标识符(UID) | 进程优先级 | 数据段指针 | 地址寄存器值 |
| 代码运行入口地址 | 堆栈段指针 | 控制寄存器值 | |
| 程序的外存地址 | 文件描述符 | 标志寄存器值 | |
| 进入内存时间 | 键盘 | 状态字 | |
| 处理机占用时间 | 鼠标 | ||
| 信号量使用 |
- 进程描述信息。
- 进程标识符:标志各个进程,每个进程都有一个唯一的标识号 。
- 用户标识符:进程归属的用户,用户标识符主要为共享和保护服务。
- 进程控制和管理信息。
- 进程当前状态:描述进程的状态信息,作为处理机分配调度的依据。
- 进程优先级:描述进程抢占处理机的优先级,优先级高的进程可优先获得处理机。
- 资源分配清单,用于说明有关内存地址空间或虚拟地址空间的状况,所打开文件的列表和所使用的输入/输出设备信息。
- 处理机相关信息,主要指处理机中各寄存器的值,当进程被切换时,处理机状态信息都必须保存在相应的 PCB 中,以便在该进程重新执行时,能从断点继续执行。
在一个系统中,通常存在着许多进程的 PCB,有的处于就绪态,有的处于阻塞态,而且阻塞的原因各不相同。为了方便进程的调度和管理,需要将各进程的 PCB 用适当的方法组织起来。
目前,常用的组织方式有链接方式和索引方式两种。
- 链接方式将同一状态的 PCB 链接成一个队列,不同状态对应不同的队列,也可把处于阻塞态的进程的 PCB,根据其阻塞原因的不同,排成多个阻塞队列。
- 索引方式将同一状态的进程组织在一个索引表中,索引表的表项指向相应的 PCB,不同状态对应不同的索引表,如就绪索引表和阻塞索引表等。
程序段
程序段就是能被进程调度程序,调度到 CPU 执行的程序代码段。
注意 ,程序可被多个进程共享,即多个进程可以运行同一个程序。
数据段
一个进程的数据段,可以是进程对应的程序加工处理的原始数据,也可以是程序执行时产生的中间或最终结果 。
进程的状态与转换
进程的状态
进程在其生命周期内,由于系统中各进程之间的相互制约关系及系统的运行环境的变化,使得进程的状态也在不断地发生变化(一个进程会经历若干不同状态)。通常进程有以下五种状态,前三种是进程的基本状态。
- 运行态。进程正在处理机上运行。在单处理机环境下,每个时刻最多只有一个进程处于运行态。
- 就绪态。进程获得了除处理机外的一切所需资源,一旦得到处理机,便可立即运行。系统中处于就绪状态的进程可能有多个,通常将它们排成一个队列,称为就绪队列。
- 阻塞态,又称等待态。进程正在等待某一事件而暂停运行,如等待某资源为可用(不包括处理机)或等待输入/输出完成。即使处理机空闲,该进程也不能运行。
- 创建态。进程正在被创建,尚未转到就绪态。创建进程通常需要多个步骤:首先申请一个空白的 PCB,并向 PCB 中填写一些控制和管理进程的信息;然后由系统为该进程分配运行时所必需的资源;最后把该进程转入就绪态。
- 结束态。进程正从系统中消失,可能是进程正常结束或其他原因中断退出运行。进程需要结束运行时,系统首先必须置该进程为结束态,然后再进一步处理资源释放和回收等工作。
注意区别就绪态和等待态:
- 就绪态是指进程仅缺少处理机,只要获得处理机资源就立即运行
- 而等待态是指进程需要其他资源(除了处理机)或等待某一事件。
之所以把处理机和其他资源划分开,是因为在分时系统的时间片轮转机制中,每个进程分到的时间片是若干毫秒。也就是说,进程得到处理机的时间很短且非常频繁,进程在运行过程中实际上是频繁地转换到就绪态的;而其他资源(如外设)的使用和分配或某一事件的发生(如 I/O 操作的完成)对应的时间相对来说很长,进程转换到等待态的次数也相对较少。这样来看,就绪态和等待态是进程生命周期中两个完全不同的状态,显然需要加以区分。
进程状态的转换
说明了 5 种进程状态的转换,而三种基本状态之间的转换如下:
- 就绪态 → 运行态:处于就绪态的进程被调度后,获得处理机资源(分派处理机时间片),于是进程由就绪态转换为运行态。
- 运行态 → 就绪态:处于运行态的进程在时间片用完后,不得不让出处理机,从而进程由运行态转换为就绪态。此外,在可剥夺的操作系统中,当有更高优先级的进程就绪时,调度程度将正在执行的进程转换为就绪态,让更高优先级的进程执行。
- 运行态 → 阻塞态:进程请求某一资源(如外设)的使用和分配或等待某一事件的发生 (如 I/O 操作的完成)时,它就从运行态转换为阻塞态。进程以系统调用的形式请求操作系统提供服务,这是一种特殊的、由运行用户态程序调用操作系统内核过程的形式。
- 阻塞态 → 就绪态:进程等待的事件到来时,如 I/O 操作结束或中断结束时,中断处理程序必须把相应进程的状态由阻塞态转换为就绪态。
需要注意的是,一个进程从运行态变成阻塞态是主动的行为,而从阻塞态变成就绪态是被动的行为,需要其他相关进程的协助。
进程控制
进程控制的主要功能是对系统中的所有进程实施有效的管理,它具有创建新进程、撤销己有进程、实现进程状态转换等功能。
在操作系统中, 一般把进程控制用的程序段称为原语,原语的特点是执行期间不允许中断(原子操作),它是一个不可分割的基本单位 。原语“关中断指令”和“开中断指令”实现,只允许在核心态下执行的特权指令。
进程控制会导致进程状态的转换。无论哪个原语,要做的无非三类事情:
- 更新 PCB 中的信息(如修改进程状态标志、将运行环境保存到 PCB、从 PCB 恢复运行环境)
- 所有的进程控制原语一定都会修改进程状态标志
- 剥夺当前运行进程的 CPU 使用权必然需要保存其运行环境
- 某进程开始运行前必然要恢复期运行环境
- 将 PCB 插入合适的队列
- 分配/回收资源
进程的创建
无 → 创建态 → 就绪态
允许一个进程创建另一个进程。 此时创建者称为父进程,被创建的进程称为子进程。子进程可以继承父进程所拥有的资源。当子进程被撤销时,应将其从父进程那里获得的资源归还给父进程。此外,在撤销父进程时,必须同时撤销其所有的子进程。
在操作系统中,终端用户登录系统、作业调度、系统提供服务、用户程序的应用请求等都会引起进程的创建 。
- 用户登录:分时系统中,用户登录成功,系统会建立为其建立一个新的进程。
- 作业调度:多道批处理系统中,有新的作业放入内存时,会为其建立一个新的进程。
- 提供服务:用户向操作系统提出某些请求时,会新建一个进程处理该请求。
- 应用请求:由用户进程主动请求创建一个子进程。
操作系统创建一个新进程的过程如下(创建原语):
-
为新进程分配一个唯一的进程标识号,并申请一个空白的 PCB(PCB 是有限的)。若 PCB 申请失败,则创建失败。
-
为进程分配资源,为新进程的程序和数据及用户栈分配必要的内存空间(在 PCB 中体现)。
注意,若资源不足(如内存空间),则并不是创建失败,而是处于阻塞态,等待内存资源。
-
初始化 PCB,主要包括初始化标志信息、初始化处理机状态信息和初始化处理机控制信息,以及设置进程的优先级等。
-
若进程就绪队列能够接纳新进程,则将新进程插入就绪队列,等待被调度运行。
进程的终止
就绪态/阻塞态/运行态 → 终止态 → 无
引起进程终止的事件主要有:
- 正常结束,表示进程的任务已完成并准备退出运行。
- 异常结束,表示进程在运行时,发生了某种异常事件,使程序无法继续运行,如存储区越界、保护错、非法指令、特权指令错、运行超时、算术运算错、I/O 故障等。
- 外界干预,指进程应外界的请求而终止运行,如操作员或操作系统干预、父进程请求和父进程终止。
操作系统终止进程的过程如下(撤销原语) :
- 根据被终止进程的标识符,检索 PCB,从中读出该进程的状态。
- 若被终止进程处于执行状态,立即终止该进程的执行,将处理机资源分配给其他进程。
- 若该进程还有子孙进程,则应将其所有子孙进程终止。
- 将该进程所拥有的全部资源,或归还给其父进程,或归还给操作系统。
- 将该 PCB 从所在队列(链表)中删除。
进程的阻塞和唤醒
运行态 → 阻塞态
正在执行的进程,由于期待的某些事件未发生,如请求系统资源失败、等待某种操作的完成、新数据尚未到达或无新工作可做等,由系统自动执行阻塞原语(Block),使自己由运行态变为阻塞态。可见,进程的阻塞是进程自身的一种主动行为,也因此只有处于运行态的进程(获得 CPU),才可能将其转为阻塞态。阻塞原语的执行过程如下:
- 找到将要被阻塞进程的标识号对应的 PCB。
- 若该进程为运行态,则保护其现场,将其状态转为阻塞态,停止运行。
- 把该 PCB 插入相应事件的等待队列,将处理机资源调度给其他就绪进程。
阻塞态 → 运行态
当被阻塞进程所期待的事件出现时,如它所启动的 I/O 操作已完成或其所期待的数据已到达,由有关进程(比如,释放该 I/O 设备的进程,或提供数据的进程)调用唤醒原语(Wakeup),将等待该事件的进程唤醒。唤醒原语的执行过程如下:
- 在该事件的等待队列中找到相应进程的 PCB。
- 将其从等待队列中移出,并置其状态为就绪态。
- 把该 PCB 插入就绪队列,等待调度程序调度。
需要注意的是,Block 原语和 Wakeup 原语是一对作用刚好相反的原语,必须成对使用。Block 原语是由被阻塞进程自我调用实现的,而 Wakeup 原语则是由一个与被唤醒进程合作或被其他相关的进程调用实现的。
进程切换
运行态 → 阻塞态/就绪态;就绪态 → 运行态
对于通常的进程而言,其创建、撤销及要求由系统设备完成的 I/O 操作,都是利用系统调用而进入内核,再由内核中的相应处理程序予以完成的。进程切换同样是在内核的支持下实现的,因此可以说,任何进程都是在操作系统内核的支持下运行的,是与内核紧密相关的。进程切换是指处理机从一个进程的运行转到另一个进程上运行,在这个过程中,进程的运行环境产生了实质性的变化。进程切换的过程如下:
- 保存处理机上下文,包括程序计数器和其他寄存器。
- 更新 PCB 信息。
- 把进程的 PCB 移入相应的队列,如就绪、在某事件阻塞等队列。
- 选择另一个进程执行,并更新其 PCB。
- 更新内存管理的数据结构。
- 恢复处理机上下文。
注意,进程切换与处理机模式切换是不同的,模式切换时,处理机逻辑上可能还在同一进程中运行。若进程因中断或异常进入核心态运行,执行完后又回到用户态刚被中断的程序运行,则操作系统只需恢复进程进入内核时所保存的 CPU 现场,而无须改变当前进程的环境信息。但若要切换进程,当前运行进程改变了,则当前进程的环境信息也需要改变。
注意:“调度”和“切换”的区别。调度是指决定资源分配给哪个进程的行为,是一种决策行为;切换是指实际分配的行为,是执行行为。一般来说,先有资源的调度,然后才有进程的切换。
进程的通信
进程通信是指进程之间的信息交换。
注意,用户进程空间一般都是独立的,进程运行期间一般不能访问其他进程的空间,要想让两个用户进程共享空间,必须通过特殊的系统调用实现,而进程内的线程是自然共享进程空间的。简单理解就是,甲和乙中间有一个大布袋,甲和乙交换物品是通过大布袋进行的,甲把物品放在大布袋里,乙拿走。但乙不能直接到甲的手中拿东西,甲也不能直接到乙的手中拿东西。
PV 操作是低级通信方式,高级通信方式是指以较高的效率传输大量数据的通信方式。高级通信方法主要有以下三类。
共享存储
在通信的进程之间存在一块可直接访问的共享空间,通过对这片共享空间进行写/读操作实现进程之间的信息交换。在对共享空间进行写/读操作时,需要使用同步互斥工具(如 P 操作、V 操作),对共享空间的写/读进行控制。
共享存储又分为两种:
- 低级方式的共享是基于数据结构的共享;
- 高级方式的共享则是基于存储区的共享。
操作系统只负责为通信进程提供可共享使用的存储空间和同步互斥工具,而数据交换则由用户自己安排读/写指令完成。
管道通信
管道通信是消息传递的一种特殊方式。所谓“管道”,是指用于连接一个读进程和一个写进程以实现它们之间的通信的一个共享文件,又名 pipe 文件。为了协调双方的通信,管道机制必须提供以下三方面的协调能力:互斥、同步和确定对方的存在。
- 管道只能采用半双工通信,某一时间段内只能实现单向的传输。如果要实现双向同时通信,则需要设置两个管道。
- 各进程要互斥地访问管道。
- 数据以字符流的形式写入管道,当管道写满时,写进程系统调用将被阻塞,等待读进程将数据取走。当读进程将数据全部取走后,管道变空,此时读进程系统调用将被阻塞。
- 如果没写满,就不允许读。如果没读空,就不允许写。
- 数据一旦被读出,就从管道中被抛弃,这就意味着读进程最多只能有一个,否则可能会有读错数据的情况。
下面以 Linux 中的管道为例进行说明。在 Linux 中,管道是一种使用非常频繁的通信机制。从本质上说,管道也是一种文件,但它又和一般的文件有所不同,管道可以克服使用文件进行通信的两个问题,具体表现如下:
- 限制管道的大小。实际上,管道是一个固定大小的缓冲区。在 Linux 中,该缓冲区的大小为4KB,这使得它的大小不像文件那样不加检验地增长。使用单个固定缓冲区也会带来问题,比如在写管道时可能变满,这种情况发生时,随后对管道的 write()调用将默认地被阻塞,等待某些数据被读取,以便腾出足够的空间供 write()调用写。
- 读进程也可能工作得比写进程快。当所有当前进程数据已被读取时,管道变空。当这种情况发生时,一个随后的 read()调用将默认地被阻塞,等待某些数据被写入,这解决了 read()调用返回文件结束的问题。
注意:从管道读数据是一次性操作,数据一旦被读取, 它就从管道中被抛弃,释放空间以便写更多的数据。管道只能采用半双工通信,即某一时刻只能单向传输。要实现父子进程双方互动通信,需要定义两个管道。
管道可以理解为共享存储的优化和发展,因为在共享存储中,若某进程要访问共享存储空间,则必须没有其他进程在该共享存储空间中进行写操作,否则访问行为就会被阻塞。而管道通信中,存储空间进化成了缓冲区,缓冲区只允许一边写入、另一边读出,因此只要缓冲区中有数据,进程就能从缓冲区中读出,而不必担心会因为其他进程在其中进行写操作而遭到阻塞,因为写进程会先把缓冲区写满,然后才让读进程读,当缓冲区中还有数据时,写进程不会往缓冲区写数据。当然,这也决定了管道通信必然是半双工通信。
消息传递
在消息传递系统中,进程间的数据交换是以格式化的消息(Message)为单位的。若通信的进程之间不存在可直接访问的共享空间,则必须利用操作系统提供的消息传递方法实现进程通信。进程通过系统提供的发送消息和接收消息两个原语进行数据交换。
- 直接通信方式。发送进程直接把消息发送给接收进程,并将它挂在接收进程的消息缓冲队列上,接收进程从消息缓冲队列中取得消息。
- 间接通信方式。发送进程把消息发送到某个中间实体,接收进程从中间实体取得消息。这种中间实体一般称为信箱, 这种通信方式又称信箱通信方式。该通信方式广泛应用于计算机网络中,相应的通信系统称为电子邮件系统。
简单理解就是,甲要告诉乙某些事情,就要写信,然后通过邮差送给乙。直接通信就是邮差把信直接送到乙的手上;间接通信就是乙家门口有一个邮箱,邮差把信放到邮箱里。
线程概念和多线程模型
线程的基本概念
引入进程的目的是为了更好地使多道程序并发执行,提高资源利用率和系统吞吐量;而引入线程的目的则是为了减小程序在并发执行时所付出的时空开销,提高操作系统的并发性能。
线程最直接的理解就是“轻量级进程”,它是一个基本的 CPU 执行单元,也是程序执行流的最小单元,由线程 ID、程序计数器、寄存器集合和堆栈组成。
线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其他线程共享进程所拥有的全部资源。一个线程可以创建和撤销另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现出间断性。线程也有就绪、阻塞和运行三种基本状态。
引入线程后,进程的内涵发生了改变,进程只作为除 CPU 外的系统资源的分配单元,而线程则作为处理机的分配单元。由于一个进程内部有多个线程,若线程的切换发生在同一个进程内部,则只需要很少的时空开销。
- 资源分配、调度:
- 传统进程机制中,进程是资源分配、调度的基本单位;
- 引入线程后,进程是资源分配的基本单位,线程是调度的基本单位
- 并发性:
- 传统进程机制中,只能进程间并发
- 引入线程后,各线程间也能并发,提升了并发度
- 系统开销:
- 传统的进程间并发,需要切换进程的运行环境,系统开销很大
- 线程间并发,如果是同一进程内的线程切换,则不需要切换进程环境,系统开销小。引入线程后,并发所带来的系统开销减小
线程与进程的比较
线程是处理机调度的单位,进程是资源分配的单位。
- 调度。在传统的操作系统中,拥有资源和独立调度的基本单位都是进程。在引入线程的操作系统中,线程是独立调度的基本单位,进程是拥有资源的基本单位。在同一进程中,线程的切换不会引起进程切换。在不同进程中进行线程切换,如从一个进程内的线程切换到另一个进程中的线程时,会引起进程切换。
- 拥有资源。不论是传统操作系统还是设有线程的操作系统,进程都是拥有资源的基本单位,而线程不拥有系统资源(也有一点儿必不可少的资源),但线程可以访问其隶属进程的系统资源。要知道,若线程也是拥有资源的单位,则切换线程就需要较大的时空开销,线程这个概念的提出就没有意义。
- 并发性。在引入线程的操作系统中,不仅进程之间可以并发执行,而且多个线程之间也可以并发执行,从而使操作系统具有更好的并发性,提高了系统的吞吐量。
- 系统开销。由于创建或撤销进程时,系统都要为之分配或回收资源,如内存空间、I/O 设备等,因此操作系统所付出的开销远大于创建或撤销线程时的开销。类似地,在进行进程切换时,涉及当前执行进程 CPU 环境的保存及新调度到进程 CPU 环境的设置,而线程切换时只需保存和设置少量寄存器内容,开销很小。此外,由于同一进程内的多个线程共享进程的地址空间,因此这些线程之间的同步与通信非常容易实现,甚至无须操作系统的干预。
- 地址空间和其他资源(如打开的文件)。进程的地址空间之间互相独立,同一进程的各线程间共享进程的资源,某进程内的线程对于其他进程不可见。
- 通信方面。进程间通信(IPC) 需要进程同步和互斥手段的辅助,以保证数据的一致性,而线程间可以直接读/写进程数据段(如全局变量)来进行通信。
线程的属性
多线程操作系统把线程作为独立运行(或调度)的基本单位,此时的进程已不再是一个基本的可执行实体,但它仍具有与执行相关的状态。所谓进程处于“执行”状态,实际上是指该进程中的某线程正在执行。线程的主要属性如下:
- 线程是一个轻型实体,它(几乎)不拥有系统资源,但每个线程都应有一个唯一的标识符和一个线程控制块,线程控制块记录了线程执行的寄存器和栈等现场状态。
- 不同的线程可以执行相同的程序,即同一个服务程序被不同的用户调用时,操作系统把它们创建成不同的线程。
- 同一进程中的各个线程共享该进程所拥有的资源。
- 由于共享内存地址空间,同一进程中的线程间通信甚至无需系统干预。
- 同一进程中的线程切换,不会引起进程切换
- 不同进程中的线程切换,会引起进程切换
- 切换同进程内的线程,系统开销很小
- 线程是处理机的独立调度单位,多个线程是可以并发执行的。在单 CPU 的计算机系统中,各线程可交替地占用 CPU;在多 CPU 的计算机系统中,各线程可同时占用不同的 CPU,若各个 CPU 同时为一个进程内的各线程服务,则可缩短进程的处理时间。
- 一个线程被创建后,便开始了它的生命周期,直至终止。线程在生命周期内会经历阻塞态、就绪态和运行态等各种状态<变化。
为什么线程的提出有利于提高系统并发性?可以这样来理解:由于有了线程,线程切换时,有可能会发生进程切换,也有可能不发生进程切换,平均而言每次切换所需的开销就变小了,因此能够让更多的线程参与并发,而不会影响到响应时间等问题。
线程的实现方式
线程的实现可以分为两类:用户级线程(User-Level Thread, ULT)和内核级线程(Kernel-Level Thread, KLT)。内核级线程又称内核支持的线程。
在用户级线程中,有关线程管理(线程的创建、撤消和切换等)的所有工作都由应用程序完成,内核意识不到线程的存在。应用程序可以通过使用线程库设计成多线程程序。通常,应用程序从单线程开始,在该线程中开始运行,在其运行的任何时刻,可以通过调用线程库中的派生例程创建一个在相同进程中运行的新线程。图 2.5(a)说明了用户级线程的实现方式。
在内核级线程中,线程管理的所有工作由内核完成,应用程序没有进行线程管理的代码,只有一个到内核级线程的编程接口。内核为进程及其内部的每个线程维护上下文信息,调度也在内核基于线程架构的基础上完成。图 2.5(b)说明了内核级线程的实现方式。
有些系统中使用组合方式的多线程实现。线程创建完全在用户空间中完成,线程的调度和同步也在应用程序中进行。一个应用程序中的多个用户级线程被映射到一些(小于等于用户级线程的数目)内核级线程上。图 2.5©说明了用户级与内核级的组合实现方式。
操作系统只“看得见”内核级线程,因此只有内核级线程才是处理机分配的单位。
多线程模型
有些系统同时支持用户线程和内核线程,由此产生了不同的多线程模型,即实现用户级线程和内核级线程的连接方式。
- 多对一模型。将多个用户级线程映射到一个内核级线程,线程管理在用户空间完成。此模式中,用户级线程对操作系统不可见(即透明)。
- 优点:线程管理是在用户空间进行的,因而效率比较高。
- 缺点:一个线程在使用内核服务时被阻塞,整个进程都会被阻塞;多个线程不能并行地运行在多处理机上。
- 一对一模型。将每个用户级线程映射到一个内核级线程。
- 优点:当一个线程被阻塞后,允许另一个线程继续执行,所以并发能力较强。
- 缺点:每创建一个用户级线程都需要创建一个内核级线程与其对应,这样创建线程的开销比较大,会影响到应用程序的性能。
- 多对多模型。将$n$个用户级线程映射到$m$个内核级线程上,要求$m$≤$n$。
- 特点:多对多模型是多对一模型和一对一模型的折中,既克服了多对一模型并发度不高的缺点,又克服了一对一模型的一个用户进程占用太多内核级线程而开销太大的缺点。此外,还拥有多对一模型和一对一模型各自的优点,可谓集两者之所长。

