死锁的概念:
死锁可以被定义为一组相互竞争系统资源或进行通信的进程间“永久”阻塞。当一组进程的每个进程都在等待某个事件,而只有在这组进程中的其他被阻塞的进程才可以触发该事件,这时就称为这组进程发生死锁。
所有的死锁都涉及至少两个进程之间对资源的需求冲突。
可重用资源与可消耗资源
可重用资源:一次只供一个进程安全的使用,并且不会因为使用而耗尽资源。进程得到资源,后来又释放这些资源,使得其他进程再次使用。
可消耗资源:可以被创建和销毁的资源。当消费进程得到一个资源时,该资源就不在存在。
死锁的条件:
死锁有三个必要条件:
(1)互斥。一次只有一个进程可以使用一个资源。其他进程不能访已经分配给其他进程的资源。
(2)占有且等待。当一个进程等待其他进程时,继续占有已经分配的资源。
(3)不可抢占。不能强行抢占进程已经占有的资源。
(4)循环等待。存在一个封闭的进程链,使得每个进程至少占有此链中下一个进程所需要的一个资源。
前三个只是死锁存在的必要条件,第四个是必要条件。即假设前三个条件存在,可能发生一系列事件会导致不可解的循环等待。这个不可解的循环等待实际上就是死锁的定义。条件4里的循环等待之所以是不可解的是因为前面三个条件的存在。因此,这四个条件在一起构成了死锁的必要充分条件。(第四项循环等待条件与其他三项有本质区别。前三个条件都是策略条件,第四个是取决于所涉及的进程请求和释放资源的顺序而可能发生的一种情况。)
死锁预防:
是试图设计一种系统来排除发生死锁的可能性。一种是间接死锁预防:防止前三个条件中的任何一个的发生。一种是直接预防:防止循环等待的发生。
(1)互斥: 第一个条件无法禁止。如果对资源进行互斥访问,操作系统必须支持互斥。
(2)占有且等待:可以要求进程一次性请求所有资源,并且阻塞这个进程知道所有请求同时满足。但这是低效的。
(3)不可抢占:占有某些资源的进程进行进一步的资源请求时被拒绝,则必须释放它之前所占有的资源。或者请求另一个进程占有的资源时,可以抢占另一个进程,要求他释放资源。但要在两个进程优先级不同的条件下。
(4)循环等待: 定义资源类型的线性顺序访问来预防。
死锁避免:
死锁避免允许三个必要条件,通过选择,确保永远不会到达死锁点。在死锁避免中,是否允许当前的资源分配请求是通过判断该请求是否可能导致死锁来决定的。因此,死锁避免需要知道将来的进程资源请求的情况。
这里介绍两种死锁避免的方法:(1)如果一个进程的请求会导致死锁,则不启动此进程。
(2)如果一个进程增加资源请求会导致死锁,则不允许此分配。
1进程启动拒绝
考虑一个有着n个进程和m种不同类型的资源的系统。定义以下向量和矩阵:
1)Resource=R=(R1,R2,...Rm) --------------------------------系统中每种资源的总量
2)Avilable=V=(V1,V2,.......Vm)-----------------------------------未分配给进程的每种资源的总量。
3)矩阵Claim=C
Cij是进程i对资源j的需求。
4)矩阵Allocation=A
Aij 是当前分配给进程I的资源j.
矩阵Claim给出了每个进程对每种资源的最大需求,其中每一行代表一个进程对所有资源的请求。为了使得死锁避免正常工作,这个矩阵的信息必须由进程提前声明。类似的,矩阵Allocation显示每个进程当前的资源分配情况。一下三个关系式成立:
(1) 第一个式子:对所有j 所有资源或者可用,或者已经被分配。
(2) 第二个式子:对所有i,j 任何一个进程对任何一种资源的请求都不能超过系统中该系统的总量。
(3)第三个式子: 对所有i,j 分配给任何一个进程的任何一种资源都不会超过该进程最初声明的此资源的最大请求个数。
有了以上的知识,可以定义一个死锁避免的策略:如果一个进程的资源需求会导致死锁,则拒绝启动这个新进程。仅当对所有j
才启动一个新进程Pn+1 。就是说只有当前进程的最大请求量加上新的进程可以满时,才启动该进程。
2 资源分配拒绝-----银行家算法
首先介绍下状态和安全状态的概念。一个进程有固定数目的进程和固定数目的资源,任何时候一个进程可能分配到零个或者多个资源。系统的状态是当前给进程分配的资源的情况。因此状态包含之前定义的两个向量Resource和Available以及两个矩阵Claim和Allocation。安全状态是指由一个资源分配序列不会导致死锁(所有进程都能运行结束)不安全状态是指一个不安全的状态。
死锁避免的策略是:当进程请求一组资源时,假设同意该请求,从而改变了系统的状态,再确定其结果是否处于安全状态。如果是,则同意其请求;否则阻塞该进程直到同意该请求后任然是安全的。
死锁避免的算法。数据结构state定义了系统的状态。request[*] 是一个向量,定义了进程i的资源请求。首先进行一次检测,确保该请求不会超过进程最初声明的要求。如果该请求有效,下一步确定是否可能实现这个请求(有足够的资源)。如果不能则被挂起。如果可能,确定完成这个请求是否是安全的。为做到这一点,资源被暂时分配给进程i以形成一个newstate,然后算法测试安全性。
//全局数据结构
struct state
{
int resource[m];
int avaolable[m];
int claim[n][m];
int alloc[n][m];
};
//资源分配算法
if (alloc[i, *] + request[*] > claim[i, *])
<error>; //总申请大于需求
else if (request[*]>available[*])
<suspend process>
else //模拟分配
{
< define newstate by;
alloc[i, *] = alloc[i, *] + request[*];
available[*] = available[*] - request[*]>;
}
if (safe(newstate))
<carry out allocation>
else
{
<restore original state>;
<suspend process>;
}
测试安全算法
boolean safe(state s)
{
int currentvail[m];
process rest[<number of processes>];
rest = { all proceses };
possible = true;
while (possible)
{
<find a process Pk in rest such that
claim[k, *] - alloc[k, *] <= currentavail; >
if (found) //模拟Pk的执行
{
currentavail = currentavail + alloc[k, *];
rest = rest - {Pk};
}
else
possible = false;
}
return (rest == null);
}
(以上内容参考《操作系统精髓与设计原理》第六章)