死锁:死锁是指多个资源之间互相等待对方的资源,而在得到对方的资源之前又不释放自己的资源,这样,造成循环等待的一种现象,如果所有进程都在互相等待一个不可能发生的事件,则进程就死锁了。
死锁产生的必要条件:
1. 互斥条件:进程对资源进行排他性使用,即在一段时间内对某资源仅为一个进程所占用
2. 请求和保持条件:当进程因请求资源而阻塞时,对已获得得资源保持不放
3. 不可剥夺条件:进程已获得得资源子啊未使用之前,不能被剥夺,只能够在使用完时由自己释放
4. 环路等待条件:各个进程组成封闭的环形链,每个进程都在等待下一个进程所占用的资源
防止死锁办法:
1. 资源一次性分配(破坏请求和保持条件)
2. 可剥夺资源(破坏不可剥夺条件)
3. 资源有序分配法*(破坏循环等待条件)
死锁避免
1. 预防死锁的几种策略,会严重损坏系统性能(比如一次性分配资源)。因此在避免死锁时,要施加较弱的限制,从而获得满意的性能。
2. 由于在避免死锁的策略中,允许近场动态地申请资源。因而,系统在分配资源之前会预先计算资源分配的安全性。若此次分配不会导致系统进入不安全状态,则将资源分配给进程,否则,进程等待。其中最具有代表性的避免斯诺算法是银行家算法。
银行家算法:
1 当一个顾客对资金的最大需求量不超过银行家现有的资金时就可以接纳该顾客。
2 客户可以分期贷款,但贷款总数不能超过最大需求量。
3 当银行家现有的资金不能满足顾客尚需的贷款总额时,对顾客的贷款可推迟支付,但总能是顾客在有限的时间里获得贷款。
4 当顾客得到所需要的全部资金后,一定能在有限的时间里归还所有的资金。
哲学家就餐问题:
问题:五个哲学家围在一个圆桌就餐,每个人都必须拿起两把叉子才能就餐。
1. 服务生解法
2. 最多四个哲学家
3. 仅当一个哲学家两边都有筷子可用时才允许他拿筷子
死锁产生的必要条件:
1. 互斥条件:进程对资源进行排他性使用,即在一段时间内对某资源仅为一个进程所占用
2. 请求和保持条件:当进程因请求资源而阻塞时,对已获得得资源保持不放
3. 不可剥夺条件:进程已获得得资源子啊未使用之前,不能被剥夺,只能够在使用完时由自己释放
4. 环路等待条件:各个进程组成封闭的环形链,每个进程都在等待下一个进程所占用的资源
防止死锁办法:
1. 资源一次性分配(破坏请求和保持条件)
2. 可剥夺资源(破坏不可剥夺条件)
3. 资源有序分配法*(破坏循环等待条件)
死锁避免
1. 预防死锁的几种策略,会严重损坏系统性能(比如一次性分配资源)。因此在避免死锁时,要施加较弱的限制,从而获得满意的性能。
2. 由于在避免死锁的策略中,允许近场动态地申请资源。因而,系统在分配资源之前会预先计算资源分配的安全性。若此次分配不会导致系统进入不安全状态,则将资源分配给进程,否则,进程等待。其中最具有代表性的避免斯诺算法是银行家算法。
银行家算法:
1 当一个顾客对资金的最大需求量不超过银行家现有的资金时就可以接纳该顾客。
2 客户可以分期贷款,但贷款总数不能超过最大需求量。
3 当银行家现有的资金不能满足顾客尚需的贷款总额时,对顾客的贷款可推迟支付,但总能是顾客在有限的时间里获得贷款。
4 当顾客得到所需要的全部资金后,一定能在有限的时间里归还所有的资金。
哲学家就餐问题:
问题:五个哲学家围在一个圆桌就餐,每个人都必须拿起两把叉子才能就餐。
1. 服务生解法
2. 最多四个哲学家
3. 仅当一个哲学家两边都有筷子可用时才允许他拿筷子
4. 给所有哲学家编号,奇数号的哲学家必须首先拿左边的筷子,偶数号的先拿右边的筷子。
下面给出使用信号量来解决该问题的程序:
#include<unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include<stdlib.h>
#include<stdio.h>
#include<errno.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
#define ERR_EXIT(m) do{perror(m);exit(EXIT_FAILURE);}while(0)
#define DELAY (rand()%5+1)
union semun
{
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO
(Linux-specific) */
};
int semid;//信号量ID
int sem_p(int no)
{
struct sembuf sb={no,-1,0};
int ret=semop(semid,&sb,1);
if(ret==-1)ERR_EXIT("semop");
return ret;
}
void wait_for_2fork(int no)
{
int left=no;
int right=(no+1)%5;//拿右边的筷子
struct sembuf buf[2]={{left,-1,0},{right,-1,0}};//拿起筷子,因此第left,right信号量减一
semop(semid,buf,2);//P操作,若果能同时拿起两个筷子,就不用等待
}
void free_2fork(int no)
{
int left=no;
int right=(no+1)%5;
struct sembuf buf[2]={{left,1,0},{right,1,0}};//放下筷子,因此第left,right信号量加一
semop(semid,buf,2);//V操作,同时放下两只筷子
}
int philosephere(int no)
{
srand(getpid());
for(;;)
{
printf("%d is thinking\n",no);
sleep(DELAY);
printf("%d is hungry\n",no);
wait_for_2fork(no);
printf("%d is eating\n",no);
sleep(DELAY);
free_2fork(no);
}
}
int main(int argc,char *argv[])
{
union semun su;
su.val=1;
semid=semget(IPC_PRIVATE,5,IPC_CREAT|0666);//因为是父子进程通信,可以设置为IPC_PRIVATE,五个人,因此管理五个信号量
if(semid==-1)ERR_EXIT("semget");
for(int i=0;i<5;i++)semctl(semid,i,SETVAL,su); //将5个信号量分别初始化为1
int no=0;
int i=0;
pid_t pid;
for(i=1;i<5;i++)
{
pid=fork();//产生5个进程
if(pid==-1)ERR_EXIT("fork");
if(pid==0)
{
no=i;
break;//必须break,否则子进程会创建出新的进程
}
}
// printf("no=%d\n",no);
philosephere(no);
return 0;
}