进程
定义:进程是具有一定功能的程序在一个数据集合上的运行过程,他是系统进行资源分配和调度的一个可发执行的基本单位。(可以说进程是执行中的程序的抽象)
进程与程序的不同:(Process != Program)
程序是文本,是语句的描述(静态)
进程是运行中的程序,含有上下文信息(动态)
一个程序可以对于多个进程;
但一个进程只对应一个程序。
进程的特征:
1.动态性:进程是动态产生,动态消亡的。
2.并发性:每个进程都可以和其它进程一起向前执行。
3.独立性:每个是一个可以独立运行的基本单位,同时也是系统分配资源和调度的独立单位。
4.异步性:因为程序的相互制约,进程具有执行的间断性,进程按各自独立的、不可预知的速度向前推进。
5.结构特征:为了控制和管理进程,系统为每个进程设置了一个进程控制块–PCB。系统中运行的实体通常由程序,数据和一个PCB组成。
进程的状态和转换
- 就绪:
进程一旦获取CPU就能投入执行的状态 - 执行:
进程获取CPU正在运行的状态 - 阻塞:
进程由于等待资源或某个事件的发送而暂停执行的状态
状态的转换:
进程的组成
进程控制块PCB:
1.一个进程只有一个PCB
2.是进程存在与否的唯一标记
3.描述
4.管理信息
进程控制块和进程队列有三种逻辑结构:
- 线性方式
- 链接方式
- 索引方式
进程控制
创建、撤销进程以及完成进程各状态之间的转换。由具有特定功能的原语完成。例如:
- 进程创建原语
- 进程撤销原语
- 阻塞原语
- 唤醒原语
原语:操作系统内核中用于完成特定功能的一个过程,此过程在执行过程呈现原子特征,不可中断。
例如:
这里可以看到,创建进程时需要讲先查PCB总链,查找同名,在看有没有空的PCB,在将PCB加入PCB总链。
查找进程一般都是查找PCB。
线程
因为进程要创建,撤销和切换,时间空间开销比较大,限制了并发性的提高。引入线程:
概念:线程Thread是进程中实施调度和分派的基本单位。进程是逻辑上操作系统提供的任务,那么线程就是该任务的许多可能的子任务中的一个。提供线程的目的是方便而有效的实现并行性。
线程状态:运行、阻塞、就绪、终止状态。
进程和线程的关系:
- 一个进程至少有一个线程;而一个线程只能在一个进程的地址空间内活动。
- 资源分配给进程,同一进程的所有线程共享该进程的资源。
- CPU分配给线程,即真正在处理机运行的是线程。
4.线程在执行过程需要协作同步,不同进程的线程之间要利用消息通信的方式进行同步。
线程的实现:
首先,线程也有一个thread结构,叫线程控制块。保存一些线程的私有信息。
一般有两种线程的实现方式:
- 用户级线程(ULT):在内核之上支持用户级线程,并在用户层通过线程库来实现。线程库提供对于线程的创建、调度和管理。而不需要操作系统内核的支持。
- 核心级线程:由操作系统直接支持,内核在其空间内执行线程的创建、调度和管理。一般将进程看成是一个整体来管理。
线程池:
将多个线程放在一个池中。
好处:
- 利用现成的线程池中的线程比等待创建一个线程后去服务更快。
- 线程池的数量是确定的,这样对那些不支持同时存在大量线程的系统来所很重要。
设计原则:
- CPU的数量
- 物理内存的容量
- 并发用户数量
线程优势:
- 响应度高:有效地实现并发
- 资源共享:
- 经济:进程创建所需的内存和资源分配成本比较高。
- 效能高:对多处理机的利用率高。
同步
进程间的三种关系:
- 同步: 一组相互协同的进程,为了完成同一任务,对某些共享资源进行操作时,为了协调资源占用为相互等待、相互交换信息所产生的制约关系。
- 互斥:并发进程间因互相竞争使用独占资源而产生的制约关系。
- 通信:进程间交换信息。
临界资源和临界区:
临界资源:一次只允许一个进程使用的资源。
临界区:每个进程中访问或使用临界资源的那段代码程序就叫做临界区。
互斥的实现方式:
信号量
信号量被定义成一个共享的整形量,对信号量有以下限制:
- 信号量可以被初始化为一个非负值。
- 只有P和V两个操作可以访问信号量。
当多个进程进入临界区时,需设置一个信号量mutex,其初值为1。 下面为通用结构:
do{
P(mutex);
临界区(Critical Scetion);
V(mutex);
剩余区(remainder Section);
}while(1);
信号量的结构:信号量包括两个成员,一个是整型量,一个指向PCB的指针。
当多个进程都要等待一个信号量时,他们就排成一个进程队列,由信号量的指针指向队首,PCB通过自身指针链接,队尾的链接指针为0 。
信号量的值与使用相应资源的情况有关:
1. 当其值大于0时,表示可用资源的数量;
2. 当其小于0时,表示的是等待这个资源的进程数量。
P,V操作:
- 一、P操作:
第一步:S.value-1;
第二步:若S.value大于0,则进程继续执行,否则将该进程设置为 阻塞(相应进程的PCB链入该信号量队列的队尾,释放CPU资源,进行等待) - 二、V操作:
第一步:S.value++;
第二步:若S.value大于零,该进程继续执行。否则,释放信号量队列的第一个PCB所对应的进程Q(将该进程的阻塞状态变为就绪状态),该进程依旧继续执行。
- 信号量的应用
用信号量实现进程间的互斥
例如设置一个互斥的信号量mutex,初值为1.P1(分配进程)和P0(释放进程)的临界区代码可描述为:
- p1:
``
P(mutex)
分配打印机
(读写分配表)
V(mutex)
``
- P0
P(mutex)
释放打印机
(读写分配表)
V(mutex)
经典同步问题:
1. 生产者和消费者问题:
2. 读者与写者问题
3.理发师问题
4. *哲学家就餐问题
5. 缓冲区问题
这里有以上题目的参考答案:
戳我☞: 经典同步问题答案.
P.V语句使用的三(二)种情况
进程通信
一.消息传递
进程之间不存在可直接访问的共享空间,要利用操作系统提供的通信类系统调用来实现进程间的通信。
-
发送原语:send
-
接受原语:receive
有三种: -
1.一对一通信
-
2.服务器/客户端通信:socket编程
-
3.信箱
二、共享存储
好处:为通信进程提供直接通信的手段,使得通信进程通过写(send)、读(receive)对方的虚空间完成通信,通信的效率很高。
三、管道通信
链接发送进程和接受进程,采用FIFO进行数据的传输。
好处:交换的信息量大,信息的保存时间长。
缺点:I/O操作的次数多,同步和控制机构复杂。
进程调度
进程调度:通过某种规则或算法从就绪进程队列中选出一个进程投入运行。
- CPU调度程序的主要板块:
- 排队程序:
- 分配程序:
- 上下文切换程序:保证执行一个进程到另一个进程时,移除CPU中的进程状态到进程的PCB中。
- 调度程序对于多道程序计算机的性能有很大的影响。
- 一个进程一旦等待必须多久等到的性能问题由调度策略来决定
-
三、调度准则
*主要因素:
1.设计目标
2.公平性
3.均衡性
4.综合平衡
5.基于相对优先级 - 调度性能评价:
-
- CPU利用率
- 吞吐量
- 周转时间
- 就绪等待时间
- 响应时间
说明几个公式:
周转时间 = 结束(完成)时间 - 到达时间
带权周转时间 = 周转时间/服务时间(运行时间)
-
四、常见的CPU调度策略:
-
FCFS 先来先服务:
最先到达的进程最先服务,对于进程的繁忙的CPU比较有利,FCFS的算法是非抢占式的。
-
短作业优先算法:
-
非抢占式——Short-job-first简称SJF,这种算法假定已经知道了进程的服务时间
,优先将CPU分配给占用时间最短的作业。如果两个作业占用时间相同,则采取FCFS策略。 -
抢占式–最短剩余时间调度算法(Shortest-remained-time-first):按照当前作业进入时比较剩余的占用(服务)时间,可以中断当前的作业。
-
优先级调度算法:
-
静态优先级调度:进程的优先级在创建进程的时候就已经确认了下来,在整个运行期间保持不变,优先级高的进程优先运行。
这种方法易于实现,系统的开销也小,但容易产生饥饿现象,如果高级别的进程很多,形成稳定的进程队列,那么低级别的进程可能会被“饿死”。所以引出: -
动态优先级调度,即高响应比优先算法:
高响应比公式:R = 响应时间/要求运行时间
其中响应时间 = 要求运行时间+等待时间
R = (要求运行时间+等待时间)/要求运行时间
例题:
优先级算法也分为抢占式和非抢占式两种。 -
轮转调度算法:
RR(round-robin)定义了一个个小小的时间单元,被称为时间片,每个时间片通常在10毫秒到100毫秒之间。
将就绪的进程按FIFO的原则排成队列,按照时间片轮换给每个队列中的进程,如果未完成,则循环轮换。轮换调度在FIFO基础上添加了进程的抢占切换。算法特点:
- RR策略的平均等待时间比较长
- RR算法尤其适用于分时系统
- RR算法的性能依赖于时间片的大小
例子:
时间片的大小通常和上下文切换、进程周转时间、系统的响应时间和CPU主频有关。 -
多级队列调度算法:
把就绪多列分为多个独立的队列,每个队列有自己调度策略。
死锁
死锁产生的背景
资源共享的产生。
Djitkstra发现这个问题在银行家算法中。
死锁不仅会发生在硬件资源中,若设计不当,可能也会发生在软件资源 的使用中。
产生死锁的必要条件
若以下四个条件同时成立,则死锁就会发生:
- 互斥条件:至少有一个资源以非共享方式被持有,即同时只有一个进程可以使用该资源。如果另一个进程请求这个资源,那么该进程必须等待这个资源被释放。
- 持有并等待条件:进程至少必须持有一个资源并等待另外的党外你被其他进程所持有的资源。
- 不可抢夺条件:已分配给进程的资源不可被抢占,资源的释放只能是持有者进程完成工作后释放。
- 循环等待条件 :系统必然存在一条至少由两个进程组成的循环链,链中每个进程都在等待相关进程所占有的资源。
资源分配图:
由资源结点Ri和进程结点Pj组成,i,j = 0,1,2…n;
其中Ri指向Pj的边叫做分配边,Pj指向Ri的边为请求边。
一般出现循环如上图就是死锁。
进程对资源的使用过程:请求 ==> 分配 ==> 释放
死锁的处理方法:
1. 预防死锁 (约束资源请求来预防死锁 )
方法:确保产生死锁的四个条件有一个不成立,就可以预防死锁。
- 资源一次性分配;(破坏请求和保持条件)
- 可剥夺资源;当某进程新的资源未满足时,释放已占有的资源或剥夺等待进程的资源(破坏不可剥夺条件)
- 资源有序分配法;系统给每类资源赋予一个编号,每个进程编号递增的顺序请求资源。(破坏循环等待条件)
预防死锁又被称为静态策略,同时也会造成较低的设备利用率和系统吞吐率。
2.避免死锁
在系统运行过程中,对进程发出的每一个能够满足的资源申请进行动态检查 ,根据检查结果决定是否分配资源。
-
安全状态
如果系统能够以某些顺序为每个进程分配资源(可满足进程的最大需求)并依然可以避免死锁,那么系统的状态是安全的。严格地说,系统只有存在一个安全序列时才处于安全状态。对于一个进程序列<P~1~、P~2~...P~n~>,对于每个Pi对资源的请求都能由当前占有的资源加上Pj(j<i)释放的资源来满足,那么该序列安全。
不是所有的不安全状态都是死锁状态,不安全状态可能导致死锁状态,就像四个死锁必要产生条件不一定产生死锁,死锁是不安全状态中的特例。
-
资源分配图算法
如果每种资源只有一个,那么可以用资源分配图算法。
声明边:声明边Pi==>Rj表明:进程Pi可能会在稍后某些时候请求资源Rj,这条边用虚线表示。类似,Rj==>Pj表示释放资源。在系统中首先要预先声明资源,要执行Pi时,Pi的资源声明边必须要在声明图里。
假如进程Pi要请求Rj时,只有在将请求边Pi==>Rj转化为Rj==>Pj后不会导致资源分配图出现循环才能批准这个请求。
检测的循环算法一般要执行
n*n
次,n为进程数。 -
银行家算法
举个例子:
3.死锁的检测与解除
-
资源单一时
如果每种资源只有一个,修改资源分配图,去掉资源和相应的边,只留下进程和等待边,形成等待图。 -
多个资源
采用类似于银行家算法的方式检测。 -
死锁检测算法的应用
什么时候使用死锁检测算法,应该考虑到两点
- 可能多久发生一次死锁
- 死锁发生时会影响多少进程
-
死锁的解除
- 进程终止:
通过异常终止进程来消除死锁,有两种方案:- 异常终止所有死锁进程:有效解除死锁循环,但是代价高昂——要丢弃一些已经算了很久的进程的数据,还要重新计算。
- 每次异常终止一个进程直到消除死锁循环:这种方法会带来相当可观的开销:每次终止进程之后都需要执行一次死锁接触算法来确定是否有仍有死锁进程。同时为了进行终止进程的选择,要考虑
* 进程的优先级
* 进程计算看多长时间,还需要多长时间
* 进程使用的资源数量和种类
* 进程尚需要资源
* 需要终止多少进程
* 进程是交互式还是分批的
- 抢占资源:就是采用抢占资源将这些资源分配给其他进程来解除死锁。需要解决三个问题
1. 选择一个进程:像终止进程一样,要选择一些代价比较小的进程,比如持有的资源数量和死锁进程已经消耗的时间。
2. 回滚:抢占了一个进程的资源之后,必须将它回滚到某种安全状态,并保存相关信息。
很难确定是什么安全状态,所以直接全部回滚,但是要在消除死锁的时候回滚。还要系统保持所有运行进程的状态信息。好无语
3. “饥饿”:怎样保证不会一直从同一个进程中抢占资源。
- 进程终止: