1.什么是进程
概念: 进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动,是系统进行资源分配和调度的一个独立单位。
进程是程序的依次执行
进程是一个程序及其数据在处理机上顺序执行时所发生的活动
进程是程序在一个数据集合上运行的过程
进程是系统进行资源分配和调度的一个独立单位
结构:
控制块(PCB):进程的唯一标识,每个进程都不同,即使相同的软件的不同执行次数PCB 也不同
数据段:存放原始数据、中间数据
程序段:存放在文本区域,可被多个进程共享,同个应用程序的不同进程共享程序段
特征:
动态性:由创建而生,由撤销而亡
并发性:多个进程同时进行
独立性:独立资源分配
异步性:相互独立、互不干扰
线程:1.Thread,进程的轻型实体,也叫“轻量级进程”,是一系列活动按事先设定好的顺序依次执行的过程,是一系列指令的集合
2.是一条执行路径,不能单独存在,必须包含在进程中
3.线程是OS中运算调度的最小单位。
可以这么来理解,我们打开一个程序相当于创建了一个进程(那么同一个程序的不同进程就是多次打开产生的),然后这个程序中可能有很多功能,这些功能的执行就是不同的线程,线程可以共享这个程序进程分配到的资源,从而减少重新分配资源产生的成本。
为什么引入线程:提高OS的并发性
线程的属性:轻型实体、独立调度和分派的基本单位、可并发执行、共享进程资源
不同进程之间的线程是没有资源交互的,除了进程或者线程的通信,或者线程的同步
进程和线程的比较:
调度:线程是调度的基本单位,同一个进程中多个线程之间的调度或是说切换,不会引发进程的调度
拥有资源:进程拥有资源,线程不拥有资源,这里的资源指内存、寄存器、IO设备等,所有线程共享进程的资源,减少线程切换的开销
并发性:线程在进程的基础上进一步提高并发性
系统开销:减少空间开销
地址空间和其他资源:每个进程都是一个独立的内存,每个进程之间的地址空间是不同的,进程内部的线程共享进程的所有资源
通信:进程之间的通信比较麻烦,线程就简单一些,因为线程之间共享资源。
线程相对于进程,大大降低了创建、撤销和切换可执行实体的成本和难度。
线程的实现方式:用户级线程,内核级线程。(区别在于线程的控制块是在用户空间还是在内核空间)
用户级线程:所有的操作都在用户空间完成,但操作系统实际上无法识别用户级线程,进行资源调度的时候都是按进程来进行调度的。而且但凡任何一个线程想要访问操作系统内核资源的时候,那么就需要进行系统调用,cpu切换到内核空间,整个进程都会被中断(因为陷入指令),所以实际上无法提高效率。
内核级线程:控制块在内核空间,但数据段和程序段还在用户空间,我们所谓的切换,实际上切换的是控制块,这时候内核就可以识别不同的线程,进而实现线程切换。
但是用户级线程某个进程的所有的线程的控制块、数据段、程序段都是在用户空间中,由进程来进行管理,切换的效率较高。
内核级线程的控制块在内核空间,但是数据段和程序段在用户空间,线程的执行在用户空间,线程切换的时候先用访管指令从用户空间切换到内核空间,内核空间的线程再切换到另一个线程,再切换到用户空间。所以切换的效率变低了。
组合方式:用户级线程和内核级线程多对多,但是用户空间的线程会多一点。
2.进程是怎么运行的
进程的生命周期:从创建到终止
进程的状态:
就绪(Ready):可运行但是未运行状态,进程已经分配到除了CPU(处理机)以外的所有资源
执行(Running):进程获得cpu的执行权(从就绪到执行为进程调度)
阻塞(Blocked):在执行的过程中需要进行I/O操作或者由于进程同步、进程通信等一些原因会 进入阻塞状态,是一种放弃处理机的状态。
创建和终止状态:
创建:由用户发起的,创建到就绪之间有很多过程
终止:执行结束是自然结束,执行完当前程序的最后一条指令之后会再执行一条终止指令(holt);异常终止,内部出现错误、操作源强制终止等。
终止先将当前的进程标记为已终止,然后资源释放和回收,最后清楚PCB。
进程的控制:OS对进程实现有效的管理,包括创建新进程、撤销已有进程、挂起、阻塞和唤醒、进程切换等多种操作。OS通过原语操作实现进程控制。
原语:由若干条指令组成,完成特定的功能,是一种原子操作。(可以理解成是由若干指令组成的实现特定功能的小程序)
原语的特点:
1.原子操作,要么全做,要么全不做,执行过程中不会被中断。
2.管态/系统态/内核态下执行,常驻内存。
3.是内核三大支撑功能(中断处理/时钟管理/原语操作)之一。
进程控制的原语:
创建原语:create
阻塞原语:block
唤醒原语:wakeup
撤销原语:destroy
创建:用户登录、作业调度(启动应用程序的时候,把作业从磁盘加载到内存)、提供服务(操作系统对外提供类似远程调用等功能)、应用请求(一个应用程序可以启动很多个进程)
终止:正常终止、异常终止、外界干预。
阻塞:请求某种服务(如请求一些内核资源)、启动某种操作、数据未到达、无工作可做
挂起与激活:为了系统和用户观察和分析进程,或者已经打开的进程当前不再需要了。
挂起原语:suspend
保存进程当前活动的一切镜像,相当于给当前进程拍个照,但是该进程也不会再往下执行了
静止就绪:放外存,不调度
静止阻塞:等待事件
激活原语:active
活动就绪:等待调度
活动阻塞:等待唤醒
执行状态不能直接挂起
挂起和激活不是状态,是操作。
进程的调度
处理机调度:根据一定的算法和原则将处理机资源进行重新分配的过程。
前提:作业/进程数远远大于处理机数
目的:提高资源利用率,减少处理机空闲时间
调度程序:一方面要满足特定系统用户的需求(快速响应),另一方面要考虑系统整体效率(系统平均周转时间)和调度算法本身的开销。
‘
调度层次:
高级调度/作业调度:
把后备作业调入内存(把磁盘上的应用程序加载到内存中,给它创建成进程)
只调入一次,调出一次。
中级调度/内存调度:
将进程调至外存,条件合适再调入内存(suspand/active)
在内、外存对换区进行进程对换
低级调度/进程调度:
从就绪队列选取进程分配给处理机
最基本的调度,频率非常高(相当于一个时间片完成)
调度方式
剥夺式/抢占式调度
立即暂停当前进程,分配处理机给另一个进程
原则:优先权/短进程优先/时间片原则
非剥夺/非抢占式调度
若有进程请求执行,等待直到当前进程完成或阻塞
缺点:适用于批处理系统,不适用分时/实时操作系统
调度时机:
进程运行完毕
进程时间片用完
进程要求I/O操作
执行某种原语操作(如用户空间需要访问内核空间会用到某些原语)
高优先级进程申请运行(剥夺式调度)
调度过程
1.保存镜像:记录进程现场信息
2.调度算法:确定分配处理机的原则
3.进程切换:分配处理机给其他的进程
4.处理机回收:从进程回收处理机
调度细节:
0.检查是否允许上下文切换,有可能某进程处于原语操作中,不允许切换
1.保存当前进程的上下文,包括程序计数器和寄存器
2.更新PCB信息
3.把此进程的PCB移入队列,比如就绪队列,或因某种事件的阻塞队列
4.选择另一个(就绪状态)进程执行,并更新其PCB
5.更新内存管理的数据结构
6.恢复所选进程的上下文,将CPU执行权交给所选进程
调度算法指标:
CPU利用率:忙碌时间/总时间
系统吞吐量:完成作业数/总时间
周转时间:作业完成时间-提交时间 (带权周转时间:周转时间/实际运行时间)
等待时间:作业等待处理机调度时间(关注平均值)
响应时间:提交请求到首次响应间隔
经典调度算法:
作业调度(作业从外存到内存或者从内存到外存)(也可以作为进程调度的原则)
先来先服务(FCFS)
短作业优先(SJF)
高响应比优先调度(HRRN)
优先级调度(PSA)
进程调度:
时间片轮转调度(RR)
多级反馈队列调度(MFQ)
先来先服务(FCFS)
算法内容:调度作业/就绪队列中最先入队者,等待操作完成或阻塞(调度作业指外存和内存之间进行换入换出,也就是挂起和激活,也包括最初创建进程)
算法原则:按作业/进程到达顺序服务(执行)
调度方式:非抢占式调度
使用场景:作业/进程调度
优点:有利于CPU繁忙型作业(例如处理视频图片等),充分利用CPU资源
缺点:不利于I/O繁忙型作业(需要大量读写操作),操作耗时,其他饥饿
短作业优先(SJF)
算法内容:所需服务时间最短的作业/进程优先服务(执行)
算法原则:追求最少的平均(带权)周转时间
调度方式:SJF/SPF非抢占式/SRTN抢占式(最短剩余时间优先)
适用场景:作业/进程调度
优点:平均等待/周转时间最少
缺点:长作业周转时间会增加或饥饿
估计时间不准确,不能保证紧迫任务及时处理
高响应比优先调度(HRRN)
算法内容:结合FCFS和SJF,综合考虑等待时间和服务时间计算响应比,高的优先调度
算法原则:综合考虑作业/进程的等待时间和服务时间
调度方式:非抢占式
适用场景:作业/进程调度
响应比计算:
响应比=(等待时间+服务时间)/服务时间(周转时间)
只有当前进程放弃执行权(完成/阻塞)时,重新计算所有进程响应比。如果某个进程被阻塞,那么它的等待时间重新计算的时候被视为0,所以它的响应比就是最低的
长作业等待越久响应比越高,更容易获得处理机
优先级调度(PSA)
算法内容:又叫优先权调度,按作业/进程的优先级(紧迫程度)进行调度
算法原则:优先级最高(最紧迫)的作业/进程先调度
调度方式:
抢占:高优先级立即执行
非抢占式:高优先级等待当前进程让出处理机后执行(并不能获得及时执行)
适用场景:作业/进程调度
优先级设置原则:
静态/动态优先级(静态:进程在创建就确定的;动态:在执行的过程中调整)
系统>用户;交互型>非交互型;I/O型>计算型
低优先级进程可能会产生饥饿
时间片轮转调度(RR)
算法内容:按进程到达就绪队列的顺序,轮流分配一个时间片去执行,时间用完则剥夺
算法原则:公平、轮流为每个进程服务,进程在一定时间内都能得到响应
调度方式:抢占式,由时钟中断确定时间到
适用场景:进程调度
优点:公平,响应快,适用于分时系统
时间片决定因素:系统响应时间、就绪队列进程数量、系统处理能力
时间片太大,相当于FCFS;太小,处理机频繁切换,开销增大。
多级反馈队列调度(MFQ)
算法内容:
设置多个按优先级排序的就绪队列
优先级从高到低,时间片从小到大
新进程采用队列降级法:进入第一级队列,按FCFS分时间片,没有执行完,移到第二级、第三级
前面队列不为空,不执行后续队列进程
算法原则:集前几种算法优点,相当于PSA+RR
调度方式:抢占式
适用场景:进程调度
优缺点:
对各类型相对公平;快速响应;
终端型作业用户:短作业优先
批处理作业用户:周转时间短
长批处理作业用户:在前几个队列部分执行
3.进程之间是怎么协作的
进程通信
3概念:进程间的信息交换
进程是资源分配的基本单位,各进程内存空间彼此独立
一个进程不能随意访问其他进程的地址空间
特点:
共享储存(Shared-Memory)
消息传递(Message-Passing)
管道通信(Pipe)
共享储存(Shared-Memory)
基于共享数据结构的通信方式:
多个进程共用某个数据结构(OS提供并控制)
由用户(程序员)负责同步处理
低级通信:可以传递少量数据,效率低(一般认为直接操作硬件的操作为低级操作)
基于共享储存区的通信方式
多个进程共用内存中的一块存储区域(比前一种的空间大一些)由进程控制数据的形式和方式方式(在运行过程中,进程主动去申请,两个进程申请同一块)
高级通信:可以传递大量数据,效率高
两者的区别:前一个共享空间的数据形式有所限制
优点:实现简单,
缺点:数据收发双方不可见,存在安全隐患
消息传递(Message-Passing)
直接通信:点到点发送(用send原语和receive原语)
发送和接收时指明双方进程的ID
每个进程维护一个消息缓冲队列
间接通信:广播信箱
以信箱为媒介,作为中间实体
发进程将消息发送到信箱,收进程从信箱读取
可以广播,容易建立双向通信链
与共享存储的区别在于,这种方式是通过操作原语,而前面的方式是通过操作数据结构或者内存。
管道通信(pipe)
管道:
用于连续读/写进程的共享文件,pipe文件
本质是内存中固定大小的缓冲区(可理解成在内存中进行操作的文件)
半双工通信:
同一时段只能单向通信,双工通信需要两个管道
以先进先出(FIFO)方式组织数据传输
通过系统调用read()/write()函数进行读写操作
write和read是互斥的
实际上可以通过特殊符号标记写满,因为要写入的东西并非都是整数倍
效率比较高,实现大数据量传输,安全,高效
进程同步
协调进程间的相互制约关系,使它们按照预期的方式(顺序/次序/时序)执行的过程
前提:
进程是并发执行的,进程间存在着相互制约关系
并发的进程对系统共享资源进行竞争
进程通信过程中相互发送的信号称为消息或事件
两种相互制约形式:
简介相互制约关系(互斥):进程排他性地访问共享资源
直接相互制约关系(同步):进程间地合作,比如管道通信
这里的共享资源又叫临界资源/临界区:
输入机/打印机/磁带机消息缓冲队列/变量/数组/缓冲区
访问过程:
进入区:尝试进入临界区,成功则枷锁(lock)
临界区:访问共享资源
退出区:解锁(unlock),唤醒其他阻塞进程
剩余区:其他代码
互斥访问原则:
空闲让进:临界区空闲,允许一个进程进入
忙则等待:临界区已有进程,其他进程等待(阻塞状态)
有限等待:处于等待的进程,等待时间有限
让权等待:等待时应让出CPU执行权,防止“忙等待”
实现方法:软件实现方法和硬件实现方法
软件实现方法:
单标志法:违背"空闲让进"
理想的执行过程:turn是全局变量,初值是0,然后p0进程进入区的代码可以执行完毕,p1进程在进入区死循环,p0进程进入临界区,使用之后将turn改成1,退出临界区,然后p1进程进入区的代码就可以执行完毕,然后进入临界区,开始执行,然后将turn改成0,退出临界区。
看似正确,但是如果p1进程需要执行多次,p0进程只执行一次,那么turn改成0后,就无法二次进入p0进程,将turn改成1,所以p1进程就一直在进入区进行死循环。但此时的处理机是空着的,就违背了空闲让进的原则。
双标志法先检查:违背“忙则等待”
理想的执行过程:p0在执行之前访问p1的标志,如果p1的标志是false,那么p0就将自己的标志置为true,然后开始访问临界资源,访问结束将自己的状态改为false,否则就在进入区执行死循环(忙等),对于p1的执行也是同理。
这样可以避免某个进程需要反复进入临界区但是被阻塞的问题。
但是如果p0进程的while执行完之后,下一句执行的是p1进程的while,那么两者都是可以进入临界区的,就违反了忙则等待的原则。
双标志法后检查:违反“空闲让进”、“有限等待”
理想执行情况:p0进程在进入区先把自己标记成true,然后再去判断p1进程是否为true,如果为true,那么就说明p1进程正在执行,那么p0进程就在进入区忙等,如果p0进程为false,那么p0进程就可以执行。对于p1进程也是同理。
但是仍然考虑执行顺序,对于同一个进程,进入区的两条语句未必能够不被打断的执行,可能p0标记后就开始执行标记p1的语句,然后两者就都在进入区死循环,此时处理机空闲,但是两个进程都无法进入临界区,违背“空闲让进”。而且由于两者在进入区死循环,没有其他进程打断的话,就一直死循环,然后就违背了“有限等待”的原则。
皮特森算法:违背“让权等待”,会发生“忙等”
理想的执行过程:
假设p1一条语句都没有执行,p0进入区的三条语句都是可以执行的,然后进入临界区;
假如p1执行了第一条语句,然后p0执行,那么p0会在进入区陷入死循环,那么p1执行第二条语句,然后在进入区陷入死循环,此时turn的值是0,那么p0的死循环就会结束,进入临界区,执行完将自己的标志改为false,p1的死循环就被解除了,如果两者都不止执行一次的话,也是可以成立的;
假设p1执行了前两条语句,然后开始执行p0,那么p0会陷入死循环,但是此时由于turn的值为1,p1就可以进入临界区,然后p1执行结束后,就会将p0的死循环解除
假设p1执行了三条语句,那么直接就p1进入临界区了,和刚刚讨论的第一种情况中p0类似。
所以这种算法可以实现“空闲则进”,“忙则等待”。
但是在一个进程进入临界区的时候,另一个进程是在忙等的,所以违背了让权等待的原则,但是如果处在临界区的进程执行较快的话,其实影响不大。
硬件实现方法
中断屏蔽方法:关中断/开中断
优点:禁止一切中断,CPU执行完临界区之前不会切换
缺点:
关中断可能会被滥用
关中断时间长影响效率
不适用于多处理机,无法防止其他处理机调度其他进程访问临界区
只使用内核进程(该指令运行在内核态)
Test-And-Set(TS指令/TSL指令)
读出标志并设置为True,返回旧值,原子操作
也被称为TSL指令(Test-And-Set-Lock)
违背“让权等待”,会发生忙等
Swap指令(EXCHANGE,XCHG指令)
交换两个变量的值,原子操作(一个处理机读取lock后,其他处理机都无法读取lock)
违背“让权等待”
信号量(Semaphore)机制
PV操作:
P操作:wait原语,进程等待
V操作:signal原语,唤醒等待进程
整型信号量:违背“让权等待”,会发生忙等
记录型信号量:
理想的执行过程:假设只有一个资源,3个进程,那么第一个可以成功的申请到资源,然后2,3进入阻塞队列,然后1执行完,调用signal,2被唤醒,进入临界区,然后执行完3被唤醒,进入临界区,3执行完,value就变成1了,那么就无法执行唤醒操作了。
实际上用的是signal_all原语,将等待队列中的所有进程唤醒,然后进行竞争,再循环执行wait,signal_all的过程。
上述是实现互斥的过程,实现同步就是在临界区中再调用wait和signal,实现相互唤醒。
分析进程同步和互斥问题的方法步骤
1.分析关系:进程的数量,进程间的同步或互斥关系,前驱关系
2.整理思路:根据进程进程的操作流程确定P操作、V操作的大致操作
3.设置信号量:根据前两步分析和整理,设置信号量初始值
管程:
管理进程:即用于实现进程同步的工具。是由代表共享资源的数据结构和一组过程(PV操作的函数)组成的管理程序(封装)
管程的组成:
管程名称
局部于管程内部的共享数据结构
对该数据结构操作的一组过程(函数)
管程内共享数据的初始化语句
管程的基本特性:
是一个模块化的基本程序单位,可以单独编译
是一种抽象数据类型,包含数据和操作
信息掩蔽,共享数据只能被管程内的过程访问
条件变量/条件对象
进入管程的进程可能由于条件不满足而阻塞
此时进程应释放管程以便于其他进程调用管程
进程别阻塞的条件(原因)有多个,移入不同的条件队列
进程被移入条件队列后,应释放管程
由进程的调用才会产生管程,管程是被进程调用的程序
4.如何处理死锁问题
死锁定义:
多个进程由于竞争资源而造成的阻塞现象,若无外力作用,这些进程将无法继续推进。
相似概念:饥饿
等待时间过长以至于给进程推进和响应带来明显影响,“饿而不死”
死锁产生的原因:
系统资源的竞争(最主要,最根本)
进程推进顺序非法
死锁产生的必要条件:(必须同时具备)
互斥条件:共享资源的排他性访问
不剥夺条件:访问时该共享资源不会被剥夺
请求并保持条件:保持当前资源时请求另一个资源
循环等待条件:存在共享资源的循环等待链(两个共享资源相互等待)
死锁的预防:(进程运行前)
破坏互斥条件
将只能互斥访问的资源改为同时共享访问(加了缓存队列,看上去像是共享)
将独占锁改为共享锁(信号量大于1的时候就相当于共享锁)
不是所有的资源都能改成可共享的
破坏不剥夺/不可抢占条件
请求新资源无法满足时必须释放已有资源
由OS协助强制剥夺某进程持有的资源
实现复杂、代价高(原来已经有的部分需要重新生成)
此操作过多导致原进程任务无法推进
破坏请求并保持条件
进程开始运行的时候一次性申请所需资源(缺点:资源浪费、进程饥饿)
阶段性请求和释放资源
破坏循环等待条件
对所有资源现行排序,按序号请求资源
请求时先低再高
释放时先高再低
缺点:
对资源的编号应相对稳定,限制了新设备增加
进程使用资源的顺序可能与系统编号顺序不同
限制了用户编程
死锁的避免(进程运行中)
安全性算法
系统安全状态:
安全状态一定不会出现死锁
不安全状态可能出现死锁
银行家算法:
系统预判进程请求是否导致不安全状态
是则拒绝请求,否则答应请求
资源是动态申请和分配的,判断有两个主要条件:
最初声明最大需求,如果已分配的加上当前申请的大于最大需求,那么就拒绝如果当前申请的大于目前可用的,也拒绝。
死锁的检测与解除
死锁的检测
需要一种数据结构,保存有关资源的请求和分配信息
提供一种算法,利用这些信息检测是否形成死锁
资源分配图:
两种资源
两种节点
死锁定理(死锁状态的充分条件):
当且仅当此状态下资源分配图是不可完全简化的
简化的过程类似于“拓扑排序”算法
找既不阻塞又不孤立的点(判断阻塞与当前请求有关)
然后把它的入边和出边都消掉
一直往后操作
简化后的图:(可以完全简化)
死锁解除
资源剥夺
挂起死锁进程
剥夺其资源
将资源分配给其他(死锁进程)
撤销进程(kill)
进程回归
回退到足以避免死锁的地步
需要记录进程历史信息,设置还原起点