程序:存放在磁盘中的可执行二进制文件(即*.exe文件,包含一系列指令集合)。是静态的。
进程:程序的一次执行过程。是动态的。同一个程序多次执行将对应多个进程。
线程:轻量级进程。一个进程至少有一个线程。
没有线程的概念之前,进程是资源分配和处理机分配的基本单位。
有了线程的概念后,进程是资源分配的基本单位,线程是处理机分配的基本单位。
程序开始运行时,系统会创建相应的进程,给进程分配唯一的进程ID(PID,Process ID),并创建进程控制块(PCB),分配内存空间。程序运行结束,系统将回收PCB和内存空间。
注:程序运行的过程,是CPU执行一条一条机器指令的过程(高级语言代码由编译器编译成机器指令)。
1、进程的组成
进程控制块(PCB,Process Control Block):【数据结构】
1、PCB是进程存在的唯一标志。PCB是给操作系统使用,用于管理进程。
2、PCB中存放操作系统管理进程所需的信息。包括:
- 基本的进程描述信息:进程标识符PID,用户标识符UID(进程所属用户ID)。用于实现操作系统对各进程的区分。
- 给进程分配的资源清单:正在使用哪些内存区域,正在使用哪些I/O设备,正在使用哪些文件。用于实现操作系统对资源的管理。
- 进程控制或管理信息:进程当前状态,CPU运行时间,磁盘使用情况,网络流量使用情况等。用于实现操作系统对进程的控制。
- 处理机相关信息:各寄存器中的内容(如:程序状态字寄存器PSW,程序计数器PC)。用于实现进程的切换。
系统给进程分配的内存区域有两部分:程序段(存放程序的代码即指令序列),数据段(存放进程运行过程中产生的各种数据,如:变量)。程序段和数据段是给进程自己使用,与进程自身的运行逻辑有关。
PCB、程序段、数据段共同组成了进程实体(进程映像)。进程实体反映了进程某一时刻的状态,是静态的。同一个程序多次执行,各进程的PCB和数据段不同,但程序段相同。
2、进程的特征
3、进程的组织方式:(进程PCB的组织方式)
4、进程状态
5、进程控制
进程控制:进程的创建,进程状态的转换,进程的撤销。通过操作系统内核的原语实现。
- 原语是特殊的程序,具有原子性(即原语执行中途不会被中断)。
- 原语通过关中断和开中断两个特权指令实现原子性。
(1)创建进程:操作系统创建一个进程。使用创建原语。创建态就绪态。
(2)撤销进程:操作系统终止一个进程。使用撤销原语。就绪态/阻塞态/运行态终止态无。
(3)进程阻塞和进程唤醒 【阻塞原语和唤醒原语必须成对出现】
① 进程阻塞:正在运行的进程,主动让出CPU,进入阻塞态,等待特定事件完成。使用阻塞原语。运行态阻塞态。
② 进程唤醒:处于阻塞态的进程,等待的特定事件完成,产生中断,系统将进程唤醒,进程恢复就绪态,重新等待CPU处理。使用唤醒原语。阻塞态就绪态。
(4)进程挂起和进程激活
① 进程挂起:内存空间不足或用户要求或父进程要求时,将某进程暂时从内存调出到外存,需要时再调回内存。使用挂起原语。创建态/就绪态/运行态(就绪)挂起态。阻塞态阻塞挂起态。
注意:进程的PCB常驻内存。
② 进程激活:内存空间充足或用户要求或父进程要求或自身挂起周期结束时,将进程从外存调回内存。使用激活原语。(就绪)挂起态就绪态。阻塞挂起态阻塞态。
阻塞挂起态的进程调回内存时只能恢复到阻塞态,但若阻塞进程等待的事件发生,则阻塞挂起态就绪挂起态就绪态。
(5)进程切换:正在运行的进程1,主动或被动地下CPU,进程停止执行;CPU选择另一个进程2执行。即从进程1切换到进程2。使用切换原语。进程1:运行态就绪态/阻塞态/终止态。进程2:就绪态运行态。
6、进程调度
多个进程时,系统根据一定策略从就绪队列中选择一个进程,为其分配处理机进行执行,即进程调度。
广义的进程调度包含选择一个进程和进程切换。
进程调度的方式:剥夺调度方式(抢占式,进程被动放弃处理机),非剥夺调度方式(非抢占式,进程主动放弃处理机)。
不能进程调度的情况:中断处理,原子操作(原语),进程在操作系统内核程序临界区(一般访问某内核数据结构)中。
进程在访问操作系统内核程序临界资源时,不能进行处理机调度和切换。
进程访问一般的临界资源(如打印机,CPU可以执行其他进程),可以进行处理机调度和切换。
调度算法,详见后面的补充。
7、进程的通信
内存中有多个进程,进程可以访问自己的内存空间,不能访问其他进程,如果进程之间需要数据传递,可有以下方式:共享存储、消息传递、管道。
(1)共享存储
操作系统划分一块公共区域(即共享存储区),来实现进程之间的数据交换。操作系统提供同步互斥工具,由各进程实现互斥的访问共享空间(即一个进程访问共享空间时,其他进程不能同时访问)。
(2)消息传递
消息:包括消息头(含发送进程ID、接收进程ID、消息长度等格式化的信息)和消息体(具体传递的数据)。
进程通过操作系统提供的发送消息和接收消息两个原语实现数据交换。
① 直接通信方式
操作系统内核保存着各进程的PCB。进程之间直接指明对方进程(即发送进程指明发送给谁,接收进程指明从谁那接收)。
发送进程通过发送原语send发送消息,系统将消息放入接收进程PCB中的消息队列;接收进程通过接收原语receive接收消息,系统检查接收进程PCB中的消息队列,将对应消息放入接收进程的内存空间中。
② 间接通信方式
通过中间实体即操作系统内核中的“信箱”实现中转。进程之间不指明对方进程,但指明哪个“信箱”。
发送进程向系统申请信箱,通过发送原语send将消息放入信箱,接收进程通过接收原语receive去信箱接收消息。
(3)管道通信
管道:特殊的共享文件,又称pipe文件,实际是内存中大小固定的内存缓冲区,类似“循环队列”。同一时刻,数据流向只能是单向的(即半双工通信),而且先进先出。若要双向同时通信(即全双工通信),需要两个管道。
8、进程同步、进程互斥
进程同步:又称直接制约关系。各进程之间需要按一定顺序协调地完成某任务。
进程互斥:又称间接制约关系。一个进程在访问临界资源,另一个进程只能等待;一个进程访问结束,另一个进程才能访问。
临界资源:一个时间段内,只能一个进程使用的资源。
例如:物理设备(摄像头,打印机),变量,数据,内存缓冲区等都可能是临界资源。
进程互斥的代码由四部分组成:
进程互斥需遵循的原则:
进程互斥的实现方法:(软件、硬件)
单标志法 | 双标志先检查法 | 双标志后检查法 | Peterson算法 |
---|---|---|---|
设置一个整型变量: 允许使用临界资源的进程号。 | 设置一个布尔型数组: 各元素标记各进程使用临界资源的意愿。 | 设置一个布尔型数组: 各元素标记各进程使用临界资源的意愿。 | 设置两个变量: 各进程使用临界资源的意愿(数组),允许使用临界资源的进程号(整型)。 |
① 先检查允许进入临界区的进程号是否是自己的进程号。 ② 若是自己,则访问临界区。 ③ 访问结束,将允许进入临界区的进程号设置为其他进程号。 | ① 先检查别的进程是否想要进入临界区。 ② 若没有,则上锁(自己的意愿标记为True)。开始访问临界区。 ③ 访问结束,则解锁(将自己的意愿标记为False)。 | ① 先上锁(自己的意愿标记为True)。 ② 检查别的进程是否想要进入临界区。若没有,开始访问临界区。 ③访问结束,则解锁(将自己的意愿标记为False)。 | 结合单标志法和双标志法。 ① 先将自己的意愿标记为True。允许访问的进程号设为其他进程号。 ② 检查别的进程是否想要进入临界区以及允许访问的进程号是否是别的进程。若没有,开始访问临界区。 ③访问结束,则将自己的意愿标记为False。 |
违反“空闲让进”的原则。 | 检查和上锁不能一气呵成,可能被中断。 违反“忙则等待”的原则。 | 检查和上锁不能一气呵成,可能被中断。 违反“空闲让进”和“有限等待”的原则。 | 未遵循“让权等待”的原则。 |
中断屏蔽方法 | TestAndSet指令(TS指令) | Swap指令(XCHG指令) |
---|---|---|
关中断/开中断指令 | 硬件实现。记录临界资源是否正在被使用。 | 逻辑上与TS指令类似。 |
① 关中断。 ② 访问临界资源。 ③开中断。 | ① 检查临界资源是否正被访问(即是否上锁)。无论是否上锁,现在上锁。 ② 若原来未上锁,则访问临界资源。若原来已上锁,等待上一个进程访问结束解锁。 ③访问结束,解锁。 | 逻辑上与TS指令类似。 |
简单高效。 但不适合多处理机,不适合用户进程。只适用于操作系统内核进程。 | 实现简单。适合多处理机。 不满足“让权等待”的原则。 | 逻辑上与TS指令类似。 |
同步互斥工具:
(1)锁(或互斥锁)
互斥锁:一个变量(可以是整数,也可以是布尔值),确保同一时间只能一个进程访问临界资源。状态:未锁,已锁。
需要循环等待的互斥锁,又称自旋锁,例如:TS指令、Swap指令、单标志法。
缺点是忙等,违反“让权等待”的原则。优点是进程等待不需要切换进程上下文,常用于多处理机。
通常用硬件实现互斥锁。
(2)信号量
信号量:一个变量(可以是整数,也可以是记录型变量)。表示系统中某资源的数量。
通过wait(s)原语(简称P(s)操作)和signal(s)原语(简称V(s)操作)来实现进程互斥、进程同步。
① 整型信号量
整数变量:表示信号量,记录系统中某资源的数量。
P操作:检查资源数量,若没有空闲资源,则循环等待;若有空闲资源,则使用一个资源,资源数量-1。
V操作:资源使用完,释放资源,资源数量+1。
② 记录型信号量
整数变量:记录系统中某资源的剩余数量。等待队列:记录等待使用该资源的进程。
P操作:检查资源剩余数量,若有空闲资源,则使用一个资源,资源剩余数量-1;若没有空闲资源,使用block原语将进程放入等待队列。
V操作:资源使用完,释放资源,资源剩余数量+1。若资源有空闲且等待队列有进程,则使用wakeup原语唤醒一个进程使用该资源。
使用信号量机制实现进程互斥:
设置互斥信号量,初始值为1。不同的临界资源设置不同的互斥信号量。
P操作:进入区,申请资源。V操作:退出区,释放资源。P操作、V操作成对使用。
使用信号量机制实现进程同步:
设置同步信号量,初始值为0。每一对“一前一后”的同步关系设置一个同步信号量。
先执行的“前操作”,释放资源,即V操作。后执行的“后操作”,申请资源,即P操作。
前驱关系:多个“一前一后”的同步关系。
(3)管程
管程:一个特殊模块。类似面向对象中的类。
管程的特点:
进程同步、进程互斥相关的问题:
(1)生产者-消费者问题:
解决:① 设置2个同步信号量,初始值为对应资源的初始值。② 设置1个互斥信号量。
注意:申请互斥信号量必须在申请同步信号量之后,否则会导致死锁(双方都被阻塞,都在等对方释放资源)。
(2)多生产者-多消费者问题:
解决:① 设置3个同步信号量,初始值为对应资源的初始值。② 设置1个互斥信号量。
注意:1、所有同步信号量的值最多为1,即使并发执行,也最多有一个进程能访问临界资源,则可以不设置互斥信号量。2、从“事件”的角度分析。
(3)吸烟者问题:
解决:① 设置4个同步信号量,初始值为对应资源的初始值。② 1个整型变量。③ 互斥信号量此处可省略(因缓冲区大小为1,所有同步信号量的数量最多为1,最多有一个进程可访问临界资源)。
(4)读者-写者问题:
解决:① 设置3个互斥信号量,初始值为对应资源的初始值。② 1个整数型变量(计数器)。
(5)哲学家进餐问题:
同时需要2个资源。可能导致死锁。
9、死锁
死锁:两个或两个以上的进程持有各自的资源,又在等待对方的资源,导致彼此都不能执行下去,进程都处于阻塞态。
饥饿:一个或多个进程,长期得不到某资源,导致无法执行,进程可能处于阻塞态也可能就绪态。
死循环:一个正在执行的进程,有时因自身逻辑bug问题,一直跳不出某循环,无法正常执行下去。
死锁发生的条件:互斥条件,不可剥夺条件,请求和保持条件,循环等待条件。
即某资源,一个时间段内,只能一个进程访问,访问过程中资源不能被强制剥夺,只能进程主动放弃资源;各进程自身已经持有一些资源,仍请求对方进程占有的资源,导致都在等待对方的资源,形成一个循环等待链,各进程都被阻塞。因此发生死锁。
死锁则一定循环等待,循环等待则不一定死锁。
死锁则系统一定处于不安全状态,系统处于不安全状态则不一定死锁。系统处于安全状态则一定不会死锁。
死锁的处理策略:
(1)预防死锁:(静态策略)。破坏发生死锁的4个条件。
(2)避免死锁:(动态策略)。在资源分配之前,计算系统的安全性。通过银行家算法,分析是否存在安全序列。若存在一个或多个安全序列,则系统处于安全状态,则分配资源;若不存在安全序列,有可能发生死锁,则不分配资源,如果有进程提前归还资源,系统有可能重新回到安全状态。
安全序列:按照这种序列分配资源,可以使所有进程都能顺利执行结束。安全序列可能有多个。
安全性算法:
银行家算法:
银行家算法涉及的数据结构:
(3)死锁的检测和解除:通过资源分配图(数据结构)记录资源的申请和分配情况,按照一定算法,检测是否处于死锁状态。若处于死锁状态,则解除死锁。
死锁定理:某时刻,系统在简化资源分配图后,仍有无法消除的边,则图不可完全简化,表示发生了死锁。
死锁进程:在简化资源分配图后,仍存在有向边的进程。
资源分配图:
死锁检测:(简化资源分配图)
死锁解除:
死锁解除选择哪个进程收回其资源:
线程是“轻量级进程”。线程有用户级线程和内核级线程两种。内核级线程是处理机分配的单位。
线程控制块(TCB,Thread Control Block):【数据结构】
1、用于管理线程。
2、包括线程标识符(TID,Thread ID)、线程运行状态、优先级、用于切换的处理机信息(程序计数器PC、必要的寄存器、堆栈指针)。
3、通过线程表将各线程组织起来。可多个TCB组成一张线程表。
用户级线程 | 内核级线程 |
---|---|
用户视角 | 操作系统视角 |
处于用户态 | 处于内核态 |
由用户进程通过线程池管理 | 由操作系统内核管理 |
用户级线程切换不需要操作系统转到内核态 | 内核级线程切换从用户态转到内核态,由操作系统内核完成 |
操作系统不知道用户级线程的存在 | 操作系统支持的线程,CPU分配的单位 |
一对一 | 多对一 | 多对多 |
---|---|---|
一个用户级线程映射到一个内核级线程 | 多个用户级线程映射到一个内核级线程 | n个用户级线程映射到m个内核级线程(nm) |
一个进程对应多个内核级线程 | 一个进程对应一个内核级线程 | 一个进程对应多个内核级线程 |
一个线程阻塞,其他线程可以继续执行,并发度高。 但线程切换需要切换到内核态,管理成本高,开销大 | 线程切换不需要切换到内核态,开销小。 但一个线程阻塞,整个进程阻塞,并发度低。 | 一个线程阻塞,其他线程可以继续执行,所有内核级线程都阻塞进程才阻塞。并发度高。 且减小开销。 |
注:有了线程的概念后,在CPU上运行的是线程。
高级调度:作业调度 | 中级调度:内存调度 | 低级调度:进程调度 |
---|---|---|
面向作业 | 面向进程 | 面向进程 |
外存内存 | 外存内存 | 内存CPU |
无创建态就绪态 | (就绪)挂起态就绪态, 阻塞挂起态阻塞态, 阻塞挂起态(就绪)挂起态就绪态【阻塞事件已发生】 | 就绪态运行态 |
频率:低 | 频率:中 | 频率:高 |
按照某种规则,从后备队列中选择合适的作业,将其从外存调入内存,并为其创建进程 | 按照某种规则,从挂起队列中选择合适的进程,将其从外存调回内存 | 按照某种规则,从就绪队列中选择合适的进程,为其分配处理机 |
调度算法的评价指标:
CPU利用率=CPU忙碌时间/总时间。
系统吞吐量=完成作业数量/花费的总时间。
(作业)周转时间=作业完成时间-作业提交时间。
平均周转时间=各作业周转时间总和/作业数量。
带权周转时间=作业周转时间/作业实际运行时间。
平均带权周转时间=各作业带权周转时间总和/作业数量。
调度算法:
1、先来先服务(FCFS,First Come First Serve)
2、短作业优先(SJF,Shortest Job First),短进程优先(SPF,Shortest Process First)
3、高响应比优先(HRRN,Highest Response Ratio Next)
以上3种调度算法,不关注响应时间,不区分任务的紧急情况,适用于早起的批处理系统。
4、时间片轮转(RR,Round-Robin)
系统设定时间间隔,就绪队列的每个进程依次执行,时间片时,时钟装置会发出时钟中断通知CPU,进程被强行剥夺CPU、放入就绪队列的队尾,CPU处理下一个进程(就绪队列队头的进程)。进程运行结束主动放弃CPU,也会发生调度。
5、优先级调度算法
优先级设置有两种:只在进程创建时确定优先级(静态优先级)。进程创建时优先级设定初始值,若进程等待时间很长、运行时间很长、频繁I/O操作,则适当动态调整优先级(动态优先级)。
优先级:系统进程用户进程,前台进程后台进程,I/O繁忙型(I/O型)CPU繁忙型(计算型)。
6、多级队列调度算法
系统根据进程类型设置多个队列,进程创建时,放入对应的队列。
各队列可设置不同优先级,按优先级依次调度。也可以给各队列分配不同的时间片。
各队列可使用不同的调度策略。例如:系统进程队列可使用优先级调度。交互式队列可使用时间片调度。批处理队列可使用先到先服务。
7、多级反馈队列调度算法
用于进程调度。抢占式调度。会导致饥饿。