原文地址:第二章 进程与线程——王道操作系统 – Beatless一、进程与线程 进程的概念和特征 程序的特性: 程序并发执行的特征: 程序失去封闭性:其是指在并发环境中,一个…
https://honeyworks.cn/index.php/348/
目录
一、进程与线程
进程的概念和特征
程序的特性:
- 顺序性:每一个操作都必须要在上一个操作完成后开始。
- 封闭性:程序运行独占全部资源,不受外界影响。
- 可再现性:只要程序执行环境和初始条件相同,当程序重复执行时,结果相同。
程序并发执行的特征:
- 间断性:并发执行的程序由于共享资源,以及为了完成同一任务相互合作,相互制约。将导致并发程序具有“执行-暂停-执行”间断性活动规律。
- 失去封闭性:多个程序共享资源。
- 不可再现性:即使执行环境和初始条件相同,结果却各不相同。
程序失去封闭性:其是指在并发环境中,一个程序在执行时受到其他程序的影响,导致其行为与在孤立环境下执行时不同。
进程的定义:
- 一个具有一定功能的程序关于某个数据集合的一次运行活动,是操作系统动态执行的基本单元。
- 一个正在执行的程序实例。
- 一个程序及其数据从磁盘加载到内存后,在CPU上执行的过程。
进程控制块(Process Control Block,PCB):描述进程的基本情况和运行状态,进而控制和管理进程。
进程实体:又称进程映像,由程序段、数据段和PCB构成(也包括执行栈区)。PCB给操作系统使用,程序段、数据段是进程自己使用。
同一程序的不同进程具有独立性,且它们的PCB、数据段各不相同,但是程序段相同。
创建进程就是创建进程的PCB,撤销进程就是撤销进程的PCB。
进程的特征:
- 动态性:进程具有生命周期。
- 并发性:进程们同存于内存中,能在一段时间同时运行。
- 独立性:进程是一个独立运行、独立获得资源和独立接受调度的基本单位。
- 异步性:各进程相互制约,他们各自按照不可预知的速度向前推进。
- 结构性:程序具有数据段、程序段和PCB
会创建进程的操作:
- 用户登录
- 高级调度(作业调度)
- 系统处理用户程序的请求
- 用户程序的应用请求
进程是一个独立的运行单位,也是操作系统进行资源分配和调度的基本单位。
对于单CPU来说,一般只有一个进程处于运行态,但是当系统发生死锁的时候,可能进程全部都处于阻塞态。
进程的组成
1、进程控制块PCB
进程创建的时候,操作系统为它新建一个PCB,该结构之后常驻内存,任意时刻都可以存取,在进程结束时删除。PCB是进程实体的一部分,是进程存在的唯一标志。
PCB包含四个部分:进程描述信息、进程控制和管理信息、资源分配清单和CPU相关信息(CPU上下文)。
Ⅰ、进程描述信息:进程标识符PID、用户标识符UID。
Ⅱ、进程控制和管理信息:进程的现行状态、进程的优先级。
Ⅲ、资源分配清单:说明有关内存地址空间或虚拟地址空间、打开文件列表、所使用的输入输出设备。
Ⅳ、CPU上下文:程序计数器PC的值和CPU各寄存器的值(通用寄存器、地址寄存器、控制寄存器、标志寄存器和状态字寄存器)。
PCB常用的组织方式:链接方式(队列)和索引方式(索引表)。
2、程序段
程序段:能被程序调度程序调度到CPU执行的程序代码段,并且能被同一程序的多个进程共享。
程序段中的共享程序段必须使用可重入编码编写(纯代码),否则无法实现共享功能。
3、数据段
一个进程中的数据,可以是进程对应的程序加工处理的原始数据,也可以是程序执行时产生的中间或最终结果。
数据段也可以被同一程序的多个进程共享。
进程的状态与转换
进程状态的转变由中断处理程序完成。
进程有以下五个状态:
- 运行态:在单CPU中每时刻只有一个进程处于运行态。
- 就绪态:一旦得到CPU便可以立即运行,存放于就绪队列中。
- 阻塞态:等待CPU以外的其他资源,通常处于阻塞队列中。
- 创建态:进程正在被创建,尚未转变到就绪态。
- 终止态:进程正在从系统中消失,正常结束或其他原因。
进程的基本状态:运行态、就绪态、阻塞态。
进程状态转换:
- 就绪态 -> 运行态:处于就绪态的进程被调度后,获得CPU资源,进程由就绪态转变为运行态。
- 运行态 -> 就绪态:进程时间片用完或出现了更高优先级的进程。
- 运行态 -> 阻塞态:请求某一资源的使用和分配或等待某一事件的发生。进程以系统调用的形式请求操作系统提供服务。
- 阻塞态 -> 就绪态:进程等待的事件到来。
一个进程从运行态变为阻塞态是主动行为(主动请求某项资源),而从阻塞态变为就绪态是被动行为(被动等待获取资源)。
不能从就绪态直接到阻塞态,也不能从阻塞态直接到运行态。
进程控制
进程控制具有创建新进程、撤销已有进程和实现进程状态转换等功能。在操作系统中,一般将进程控制用的程序段称作原语,原语的特点是执行期间不允许中断,它是一个不可分割的单位。
1、进程的创建
允许一个进程创建另一个进程,此时创建者称为父进程,被创建者称为子进程。父进程可与子进程共享一部分资源;当子进程被撤销时,应当返还从父进程获得的资源;在撤销父进程的时候还会撤销其所有的子进程。
创建进程的操作:终端用户登录系统、作业调度(高级调度)、系统提供服务以及用户程序的应用请求。
创建新进程的操作——创建原语:
- 为进程分配唯一的PID,并申请一个空白PCB(PCB是有限的)。若PCB申请失败,则进程创建失败。
- 为进程分配其运行所需的资源,从操作系统获得或仅从父进程获取。如果资源不足(如内存),并不是创建失败,而是处于创建态,等待资源。
- 初始化PCB,包括初始化标志信息、CPU状态信息、CPU控制信息以及进程优先级。
- 进入就绪队列(如果能够进入),等待被调度运行。
2、进程的终止
终止原因:①正常结束 ②异常结束 ③外界干预
异常结束的原因包括但不限于:存储区越界、保护错、非法指令、特权指令错、运行超时、算术运算错和IO故障等。
终止进程的操作——终止原语:
- 根据PID检索PCB,从中读出进程状态。
- 若进程处于运行态,立刻终止进程的执行,将CPU分配给其他进程。
- 若进程有子孙进程,通常需要终止其所有的子孙进程。
- 将进程的所有资源都返还给父进程或操作系统。
- 将PCB从所在队列/链表中删除。
3、进程的阻塞
阻塞原因:正在执行的进程,由于期待的某些事件未发生,进程便通过调用阻塞原语,使自己的状态变为阻塞态,将CPU的使用权让出。
阻塞进程的操作——阻塞原语:
- 找到该PID对应的PCB。
- 若进程处于运行态,则保护现场,将CPU状态转为阻塞态,停止运行。
- 将该PCB插入对应事件的等待队列,将CPU分配给其他就绪进程。
4、进程的唤醒
唤醒原因:被阻塞的进程所期待的事件出现。
唤醒进程的操作——唤醒原语:
- 找到相应进程的PCB。
- 将其从等待队列中移出,将其状态设置为就绪态。
- 将该PCB插入就绪队列,等待调度程序的调度。
进程的通信
通信是指进程之间的信息交换。PV操作是低级通信方式,高级通信方式是指以较高的效率传输大量数据的通信方式。
进程是分配系统资源的单位,各进程拥有的内存地址空间相互独立,并且进程在运行期间一般不能访问其他进程的空间。想让两个进程共享空间,需要通过特殊的系统调用实现。
进程内的各线程共享进程空间。
高级通信方式主要有三类:①共享存储 ②消息传递 ③管道通信
共享存储是三者中速度最快的存储方式。
1、共享存储
通过”增加页表项/段表项“即可将一片共享内存区映射到各个进程的地址空间。
int shm_open; // 申请共享空间 | |
void *mmap; // 将共享内存区映射到自己的地址空间 |
共享存储又分为两种:
- 低级共享方式——基于数据结构的共享:
- 基于一个数组或一个变量等;
- 灵活性差,信息传递速度慢。
- 高级共享方式——基于存储区的共享:
- 申请内存空间,自己决定存储方式;
- 灵活性高,信息传递速度块。
操作系统只负责为进程提供可共享使用的存储空间和同步互斥工具(P操作、V操作)。
在对共享空间进行操作的时候需要使用同步互斥工具,如P、V操作。
2、消息传递
进程之间的数据交换以格式化的消息为单位,进程通过发送(send)和接受(receive)原语进行数据交换。
消息传递分为两种方式:①直接通信方式 ②间接通信方式
在直接通信方式中,进程P需要指明消息的接收进程(Q)。
在间接通信方式种,进程P需要指明消息要发送到哪个信箱(A or B)中,而对应的进程Q需要指明要到哪个信箱中接收消息。
3、管道通信
管道是一个特殊的共享文件,又称pipe文件,数据在管道中总时先进先出的(同队列)。从管道中读取数据是一次性操作,数据一旦被读取,就释放空间以便写更多数据。
普通管道只允许单项通信(半双工),若要实现两个进程双向通信,则需要定义两个管道。
管道pipe的特点:
- 互斥:当一个进程对管道进行读/写的时候,其它进程必须等待。
- 同步:写满时,写进程会被阻塞;读空时,读进程被阻塞。
- 确定对方存在。
- 数据以数据流的形式存在,数据总时先进先出的。
一个管道可以有多个读进程或多个写进程对其进行操作,但是这会增加数据竞争和混乱的风险,为了避免这种情况,与使用互斥锁或信号量等同步机制来保证每次只有一个进程对管道进行读或写操作。
线程和多线程模型
1、线程的基本概念
它是一个轻量级进程,是一个基本的CPU执行单元,也是程序执行流的最小单元,由线程ID、程序计数器PC、寄存器集合和堆栈组成。
一个线程可以创建和撤销另一个线程,同一进程中的多个线程之间可以并发执行。
进程只作为除CPU外的系统资源分配单位,而线程作为CPU的分配单元。
线程的优点:①提高系统的并发性 ②节约系统资源 ③便于子进程通信
进程 | 线程 | |
调度 | 每次调度都要进行CPU上下文切换,系统开销较大 | 独立调度的基本单位,切换代价较小 |
并发性 | 并发执行 | 并发执行 |
拥有资源 | 系统中拥有资源的基本单位 | 不拥有资源,仅有一点必不可少、保证独立运行的资源。但可以访问其隶属进程的资源 |
独立性 | 拥有独立的地址空间和资源,除了共享全局变量,不允许其他进程访问。 | 共享进程的地址空间和资源 |
系统开销 | 创建和撤销进程要分配和回收相应的资源;切换进程时要切换CPU上下文 | 创建和撤销线程以及切换线程时的系统开销较小 |
支持多CPU系统 | 只能运行在一个CPU上 | 进程中的每一个线程都可以运行在一个CPU上 |
在线程引入之前,进程是程序执行流的最小单元。
2、线程的属性
(1)线程是一个轻型实体,它不拥有资源,仅有一点必不可少、保证独立运行的资源。但每个线程都应有一个唯一的标识符和一个线程控制块,线程控制块记录线程执行的寄存器和栈等现场状态。
(2)不同的线程可以执行相同的程序。
(3)同一进程中的各个线程共享该进程所拥有的资源。
(4)线程是CPU的调度独立单位,多个线程可以并发执行。
(5)一个线程被创建后,便开始了它的生命周期,直至终止会经历阻塞态、就绪态和运行态等各种状态。
3、线程的状态与转换
线程也包含CPU上下文,在线程切换的时候要保存/恢复:①程序计数器PC ②其他寄存器 ③堆栈指针
4、进程的组织与控制
(1)线程控制块(Thread Contral Block, TCB):
- 线程标识符
- 一组寄存器,包括但不限于PSW寄存器、PC寄存器、通用寄存器
- 线程运行状态
- 线程优先级
- 线程专有存储区(线程切换用于保存现场)
- 堆栈指针
(2)线程的创建:操作系统中有用于创建和终止线程的函数(或系统调用)
(3)线程的终止:线程完成任务或发生异常,由终止线程调用相关函数执行终止操作。被终止但未释放资源的线程仍可被其它线程调用,以使被终止的线程恢复运行。
5、线程的实现方式
线程的实现方式分为两种:①用户级线程(ULT)②内核级线程(KLT)
(1)用户级线程(User-Level Thread, ULT)
有关线程的所有管理工作都由应用程序在用户空间中完成,无需操作系统干预。应用程序可以通过使用线程库设计成多线程程序。
线程库:为程序员提供创建和管理线程的API。
这种情况下CPU调度以进程为单位。
优点:
- 线程的切换不需要返回内核空间,节省开销。
- 调度算法可以根据不同进程的需要,选择不同算法。(量体裁衣)
- 与操作系统无关,线程的管理代码使属于用户程序的一部分。
缺点:
- 系统调用使一个线程阻塞,其余线程都被阻塞。
- 不能发挥多CPU的优势,每时刻仅有一个线程在执行(多PUC系统)。
(2)内核级线程(Kernel-Level Thread,KLT)
在内核的支持下运行,线程的管理工作也是在内核空间内实现的。操作系统为每个内核级线程设置一个TCB,以此来感知进程的存在。
这种情况下的CPU调度以线程为单位。
优点:
- 能发挥多CPU的优势。
- 其中一个进程被阻塞,内核可以调度其他线程占用CPU。
- 内核支持线程拥有下的数据结构和堆栈,线程切换较快。
- 内核本身也采用多线程技术,可以提高系统的执行速度和效率。
缺点:同一进程中的线程切换需要CPU状态的转换,相对于UTL系统开销较大。
(3)组合方式
结合用户级线程(ULT)和内核级线程(KLT)。
实现方式:在用户空间提供一个没有内核支持的库,再由操作系统提供内核级的一个库。
6、多线程模型
(1)多对一模型:
优点:线程管理是在用户空间进行的,无需切换到内核态,因而效率较高。
缺点:如果一个线程再访问内核时发生阻塞,整个进程都会被阻塞;再任何时刻都只有一个进程能够访问内核,多个线程不能同时在CPU上运行。
(2)一对一模型:
优点:当一个线程被阻塞后,允许调度另一个线程运行,所以并发能力较强。
缺点:每创建一个用户级线程,相应地就要创建一个内核级线程,开销较大。
(3)多对多模型:
特点:克服了上述两种模型的缺点,并且拥有上述两种模型的优点。
额外知识:
- C语言编程使用内存一般分为三个段:正文段、数据堆段和数据栈段;
- 二进制代码和常量存放在正文段;
- 动态分配的存储区在数据堆段;
- 临时变量在数据栈段;