进程间通信&死锁&信号量及PV原语

Posted 清水寺扫地僧

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了进程间通信&死锁&信号量及PV原语相关的知识,希望对你有一定的参考价值。


进程间通信(Inter-Process Communication, IPC)

顺序程序与并发程序的特征

  • 顺序程序:顺序性;封闭性(运行环境的封闭性);确定性;可再现性;
  • 并发程序:共享性;并发性;随机性;

进程互斥、临界资源、临界区

⑴ 进程的互斥:由于各进程要求共享资源,而且有些资源需要互斥使用,因此各进程间竞争使用这些资源,进程的这种关系为进程的互斥;
⑵ 临界资源:系统中某些资源一次只允许一个进程使用,称这样的资源为 临界资源 或 互斥资源;
⑶ 临界区:在进程中涉及到互斥资源的程序段叫 临界区;

同步的细分

⑴ 同步,协作。相互通知,两个进程共同完成一个功能或业务;
⑵ 互斥,矛盾。两个进程对一个共享资源的有序访问,不可同时访问同一个共享资源;多个进程排他性的使用同一个资源;

且⑴⑵都可以使用信号量来实现。

进程间通信目的

⑴ 数据传输:一个进程需要将它的数据发送给另一个进程;
⑵ 资源共享:多个进程之间共享同样的资源;
⑶ 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程);
⑷ 进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。

进程间通信发展

⑴ 管道:匿名管道,父子有血缘关系的进程间通信;命名管道,可以实现无血缘关系的进程间通信;
⑵ System V进程间通信;
⑶ POSIX进程间通信(Portable Operating System Interface,缩写为POSIX)

进程间通信分类

⑴ 文件:作为两个通信进程的数据中转,来实现进程间通信;
⑵ 文件锁:如读写锁;
⑶ 管道(pipe)和命名管道(FIFO);
⑷ 信号(signal):一个进程通过信号向另一个进程发送通知事件,可实现进程控制的目的;
⑸ 消息队列:用于数据传递;
⑹ 共享内存:进程间共享数据;
⑺ 信号量(semaphore):实现进程间对数据的同步访问和互斥访问,可实现互斥量;
⑻ 互斥量:
⑼ 条件变量:
⑽ 读写锁:
⑾ 套接字:域内通信,domain;

其中⑸⑹⑺在 SystemV和 POSIX 当中具有相应的实现,而⑻⑼⑽ 是 POSIX 独有,
若是 SystemV 想用则需使用已有的进行实现。

进程间共享信息的三种方式

  • 随进程持续:一直存在直到打开的最后一个进程结束。(如pipe和FIFO);
  • 随内核持续:一直存在直到内核自举(机器重启)或显式删除。(如System V消息队列、共享内存、信号量);
  • 随文件系统持续:一直存在直到显式删除,即使内核自举还存在。(POSIX消息队列、共享内存、信号量如果是使用映射文件来实现);

死锁及死锁产生条件

死锁是指多个进程之间相互等待对方的资源,而在得到对方资源之前又不释放自己的资源,这样,造成循环等待的一种现象。如果所有进程都在等待一个不可能发生的事,则进程就死锁了。

死锁产生的四个必要条件:

  • 互斥条件:进程对资源进行排他性使用,即在一段时间内某资源仅为一个进程所占用;
  • 请求和保持条件:当进程因请求资源而阻塞时,对已获得的资源保持不放;
  • 不可剥夺条件:进程已获得的资源在未使用完之前,不能被剥夺,只能在使用完时由自己释放;
  • 环路等待条件:各个进程组成封闭的环形链,每个进程都等待下一个进程所占用的资源释放;

防止死锁的三个办法:

  • 资源一次性分配:破坏请求和保持条件,一个进程占有资源有且仅有在所有所需资源都可获得的情况下才给予分配;
  • 可剥夺资源:破坏不可剥夺条件,可剥夺一个进程已经占有的资源;
  • 资源有序分配法:破坏循环/环路等待条件;

预防死锁的几种策略,会严重地损害系统性能。因此在避免死锁时,要施加较弱的限制,从而获得较满意的系统性能。

由于在避免死锁的策略中,允许进程动态地申请资源。因而,系统在进行资源分配之前预先计算资源分配的安全性。若此次分配不会导致系统进入不安全状态,则将资源分配给进程;否则,进程等待。其中最具有代表性的避免死锁算法时银行家算法


哲学家就餐问题

五个哲学家围在一个圆桌就餐,每个人都必须拿起两把叉子才能就餐。每个哲学家的行为伪代码如下:

while(1) {
	思考
	if(饿){
		拿左叉子
		拿右叉子
		用餐
		放下左叉子
		放下右叉子
	}
}

当五个哲学家同时拿起了其左边的叉子,导致满足了死锁的四个条件,造成死锁。解决死锁思路:

  • 服务生解法:服务生作为统一管理者,负责叉子的分配;
  • 最多四个哲学家:抽屉原则,资源多于哲学家所需;
  • 仅当一个哲学家两边筷子都可用时才允许拿叉子:一次性分配资源;
  • 给所有哲学家编号,奇数号哲学家必须先拿左边的叉子,偶数号的必须先拿右边的:打破环路等待条件;

信号量(Semaphore)及PV原语

信号量和PV原语由Dijkstra提出,Dijkstra的贡献:

  • 程序设计:结构化程序设计之父,提出goto语句是有害的
  • 操作系统:信号量,PV原语;
  • 计算机网络:单源最短路算法,进行路由表维护;

信号量

  • 互斥:P、V在同一个进程中;
  • 同步:P、V在不同进程中;

信号量值含义

  • S > 0 S > 0 S>0 S S S表示可用资源个数
  • S = 0 S = 0 S=0:表示无可用资源,无等待进程;
  • S < 0 S < 0 S<0 ∣ S ∣ |S| S表示等待队列中进程个数;

信号量实现的数据结构

struct semaphore {
	int value; //表示剩余资源个数
	pointer_PCB queue; //表示等待资源的队列,指向PCB,表示有哪些进程在等待资源
}

P原语和V原语

PV原语均是原子性的,不可被打断,可禁用打断即关闭中断的方式来实现原子性。

/************************************************************************/
P(s) {                                                                                             
	s.value = s.value--;
	//若是s.value大于等于0,则说明资源可申请到,撇开if代码
	//若是s.value小于0,则说明在申请资源时已无可用资源,
	//当前进程应加入等待队列
	if(s.value < 0)   {
		将该进程状态置为等待状态;
		将该进程的PCB插入相应的等待队列s.queue末尾
	}
}
/************************************************************************/
V(s) {
	s.value = s.value++;
	//若是s.value大于0,则说明之前并没有进程在等待资源,简单将资源增加即可
	//若是s.value小于等于0,则说明所归还资源是有进程在等待的,则唤醒等待队列中
	//最先等待的进程,分配资源,并将其插入就绪队列
	if(s.value <= 0) {
		唤醒相应等待队列s.queue当中等待的一个进程;
		改变其状态为就绪态;
		并将其插入就绪队列;
	}
}
/************************************************************************/

PV原语解决实际问题

  • 解决司机与售票员问题,协助关系。同一个信号量存在于不同进程之间,解决同步问题。
  • 解决民航售票问题,互斥关系。同一个信号量存在于一个进程当中,解决互斥问题。
  • 解决汽车租赁问题,两部敞篷车四个顾客租。
解决司机与售票员问题
解决民航售票问题
解决汽车租赁问题

以上是关于进程间通信&死锁&信号量及PV原语的主要内容,如果未能解决你的问题,请参考以下文章

信号量机制和PV操作

python开发线程:死锁和递归锁&信号量&定时器&线程queue&事件evevt

pv操作与信号量

为啥在操作系统中引入信号量及P、V操作?

PV操作

进程间通信—信号量