进程间通信的目的:
1.数据传递
2.资源共享
3.通知事件
进程间通信的发展历史:
1.管道
2.SystemV
3.POSIX(可移植操作系统接口)
管道:
1.匿名管道pipe
2.命名管道
SysV IPC:
1.消息队列
2.共享内存
3.信号量
POSIX IPC:
1.消息队列
2.共享内存
3.信号量
4.互斥量
5.条件变量
6.读写锁
Socket :(进程间通信)
进程间共享的几种方式:
1.左边两个进程共享存留于文件系统中某个文件上的某些信息,为了访问这些信息,每个进程都得穿越内核如(read、write、lseek等)。当一个文件有待更新时,某种形式的同步是需要的,这样既可以保护多个写入者,防止相互串扰,也可以保护一个或多个读者,防止写入者的干扰。
2.中间两个进程共享驻留于内核中的某些信息。管道是这种共享类型的典型例子。现在访问共享信息的每次操作都涉及向内核的一个系统调用。
3.右边两个进程有一个双方都能访问的共享内存区。每个进程一旦设置号该共享内存区,就能根本不涉及内核而访问其中的数据,共享该内存区的进程需要某种形式的同步
IPC对象的持续性:
1.随进程持续性的 IPC :一直存在到打开着该对象的最后一个进程关闭该对象为止。如管道和FIFO;
2.随内核持续性的IPC :一直存在到得喝重新自举或显式的删除该对象为止。如SystemV 消息队列和共享内存区。 Posix消息队列和共享内存区。
3.随文件系统持续性的IPC :一直存在到显式的删除该对象为止。即使内存重新自举了,该对象还是保持其值。Posix消息队列和共享内存区。
进程同步:(司机-售票员)两个或多个进程需要相互配合才能完成一项任务
进程互斥:(卖票,提款等)由于各个进程都要访问共享资源,而且这些资源需要排他使用,因此各个进程间需要竞争使用这些资源,这种关系就叫进程互斥
司机售票员问题:
司机:
while(1)
{
启动车辆;
正常行驶;
到站停车;
}
售票员:
while(1)
{
关门;
卖票;
开门;
}
卖票:
售票机:
while(p > 0) //p为还有的票数
{
p--;
}
信号量机制:(计数器)
PV操作提出者:迪杰斯特拉(Dijkstra)
信号量:
互斥:P操作和V操作在同一个进程中
同步:P操作和V操作在不同的进程中
信号量的结构体模型:
struct semaphore{
int value; //资源数目
struct task_struct *ptr; //等待该信号量,处于等待状态
};
信号量值的含义:
S > 0:表示S有这么多的资源可以使用
S = 0:表示没有资源可以使用,也没有进程在该信号量上等待资源
S < 0:表示有|S|个进程在等待该资源
P原语:
p(s)
{
s.value--;
{
将该进程只为等待状态
将该进程的task_struct插入到相应的等待队列
}
}
V原语:
v(s)
{
s.value++;
if(s.value <= 0)
{
将等待队列上的进程唤醒(一般为队头进程)
将其改为就绪状态
放入就绪队列
}
}
PV原语表示司机售票员问题:
售票机:Sem(1)
while(1)
{
P(Sem);
if(p > 0) p--;
V(Sem);
}
死锁:是指两个或两个以上的进程(或线程)在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
死锁发生的四个条件:1、互斥条件:进程对资源的访问是排他性的,如果一个线程对占用了某资源,那么其他线程必须处于等待状态,直到资源被释放。
2、请求和保持条件:进程T1至少已经保持了一个资源R1占用,但又提出对另一个资源R2请求,而此时,资源R2被其他进程T2占用,于是该进程T1也必须等待,但又对自己保持的资源R1不释放。
3、不剥夺条件:进程已获得的资源,在未使用完之前,不能被其他进程剥夺,只能在使用完以后由自己释放。
4、环路等待条件:在死锁发生时,必然存在一个“进程-资源环形链”,即:{p0,p1,p2,...pn},进程p0(或线程)等待p1占用的资源,p1等待p2占用的资源,pn等待p0占用的资源。(最直观的理解是,p0等待p1占用的资源,而p1而在等待p0占用的资源,于是两个进程就相互等待)
预防死锁:
1.破坏请求保持条件。
1> 一次性分配给进程它在运行期间所需要的全部资源。缺点:造成资源的浪费
2> 允许一个进程只获得运行初期所需要的资源后,便开始运行,进程运行过程中再逐步释放已分配给自己的,并且已用毕的全部资源,然后请求新的所需要的资源。
2.破坏不可抢占条件。
当一个进程保持了某些不可被强占资源的进程,提出新的资源请求而不能被满足时,它必须释放已经保持得所有资源,待以后需要时再重新申请。也就是说已占有的资源被暂时的释放,或者说被抢占
3.破坏循环等待条件
规定每个进程必须按序请求资源
一般的死锁避免算法:(银行家算法)
银行家算法:
1.当顾客的资金需求小于银行家现有的资金总量,接纳该客户
2.顾客可以分期贷款,但贷款总额不能超过最大需求量
3.当银行家现有的资金不能满足顾客的需求,可以推迟支付,但总能在有限的时间内让顾客得到贷款
4.当顾客使用完全部贷款,一定能够在有限的时间内归还所有资金
哲学家就餐问题:典型的死锁问题
while(1)
{
思考;
if(饿)
{
拿左筷子;
拿右筷子;
吃;
放左筷子;
放右筷子;
}
}
1. 为了克服死锁风险,可以再添加5根筷子,或者教会哲学家仅用一根筷子吃饭。
2. 另一种方法是添加一个服务员,他只允许4位哲学家同时进入餐厅,由于最多有四位哲学家就坐,则至少有一位哲学家拿到可两根筷子.