【操作系统】进程间通信

竞争条件

两个或多个进程读写某些共享数据,而最后的结果取决于进程运行的精确时序,称为竞争条件。凡涉及到资源的共享时就容易发生这样的事情。解决的办法是设立临界区,让进程互斥地访问共享资源。一个好的避免竞争条件的方案,必须满足4个条件:

  1. 任何两个进程不能同时处于临界区。
  2. 不应对CPU的速度和数量做任何假设。
  3. 临界区外运行的进程不得阻塞其它进程。
  4. 不得让进程无限期等待进入临界区。

忙等待互斥

1.屏蔽中断

进程在进入临界区时屏蔽中断(包括时钟中断),离开临界区时打开中断。这使得CPU无法切换到其它进程。这种方案的缺点在于,将中断屏蔽的进程可能不再将中断打开,导致CPU永远无法切换进程;而且屏蔽的只是该进程对应的CPU的中断,其它没有被屏蔽中断的CPU仍然可以访问共享资源。在多核系统中,这种方法并不适用。

2、锁变量

进程进入临界区之前必须先持有锁,然后将锁占有,其它进程得不到所而在临界区外等待。但这种方案的缺陷在于,进程在判断锁可用到占有锁之间可能会被调度,另一个进程同样发现锁可用并进入临界区。这会导致两个进程同时进入临界区。

3、严格轮换法

进程等待某个变量被置位后才能进入临界区,如下图所示:

进程a在turn变为0之前循环等待;进程b在turn变为1之前循环等待。这属于忙等待,很显然浪费了CPU时间。用于忙等待的锁称为自旋锁。这种方案的问题在于,两个进程必须按照严格的顺序交替进入临界区,这会降低速度较快的进程的执行效率。也就是违反了上述条件3.

4、Peterson算法

该算法非常简单且有效,仅由两个C函数构成:

关键的一点在于enter_region函数中的while循环,当两个进程同时进入enter_region函数时,能够确保先进入该函数的进程进入临界区,而后进入的在while循环出等待。

5、TSL/XCHG指令

TSL指令的形式如下:

TSL RX, LOCK    # 将LOCK读入寄存器RX并将1写入LOCK所在内存,读和写是一个不可分割的原子操作

执行TSL的CPU将锁住总线,这使得其它任何CPU都无法访问共享内存,相当于屏蔽中断的改进版。使用TSL指令的使用方法如下所示:

另一条指令XCHG原子性地交换两个位置的内容,所以它可作为TSL指令的替代品控制进程进入临界区,原理实际上和上图是一样的,如下图所示。所有的Intel x86 CPU在底层同步中使用了XCHG指令。

休眠与唤醒

上述方案有一个共同的缺点就是进程在无法进入临界区时,处于忙等待状态,这浪费了CPU时间。要使进程在无法进入临界区时阻塞,而不是等待,可以使用进程间通信原语。例如:sleep、wakeup等。以下是另外一些进程间互斥、同步的方案。

1、信号量

由大神Dijkstra提出,它包括down(P,表示尝试)和up(V,表示增加)两种操作,所以又称PV操作:

  • down:检查信号量的值是否大于0,大于0则减1并继续,等于0就休眠进程,整个操作不可分割。
  • up:对信号量值增1,唤醒由于down操作而休眠的进程,使其继续执行未完成的down操作,整个操作不可分割。

信号量可用于进程间的互斥和同步。

  • 互斥:在同一时刻只有一个进程能够进行操作。例如互斥地进入临界区。
  • 同步:进程间的运行需要按照某种先后顺序。例如生产者发现缓冲区满时要停止;消费者发现缓冲区空时要停止。

使用信号量解决生产者-消费者的例子:

其中,empty和full是用来实现同步的信号量;mutex是用来实现互斥地信号量。

2、互斥量

无计数能力,是信号量的一个简化版本。互斥量包含两个状态:解锁(0)和加锁(1)。线程加锁和解锁函数mutex_lock、mutex_unlock的实现如下:

这里的mutex_lock和上面的enter_region的区别在于:调用mutex_lock的线程无法进入临界区时会释放CPU(thread_yield函数)执行另外的线程;而调用enter_region会不断循环测试(忙等待),直到临界区可用。

3、管程

使用信号量和互斥量存在一个问题:死锁。例如上面的信号量部分代码中,如果将down(&empty)和down(&mutex)顺序对调,那么就有可能发生死锁。原因在于当生产者先锁住mutex,然后empty为0休眠后,消费者由于得不到mutex锁而休眠,这样两个进程将永远休眠下去。使用管程可解决这一问题。管程由过程、变量、数据结构等组成的一个模块,进程间必须互斥地访问这个模块中的过程,如下图所示。管程的重要特性是,在任一时刻管程中只能有一个活跃进程。

上图有一个名为example的管程,包含一个整型变量i、一个条件变量c和两个过程。管程为进程的互斥访问提供了环境,接下来要解决的是同步问题。解决方法是使用条件变量:当管程中的过程发现自己无法继续运行时(例如生产者发现缓冲区满),会在某个条件变量身上执行wait操作阻塞自身并将其它进程调入管道;另一个进程(如消费者)对缓冲区进行消费后,可以调用signal向条件变量发送信号以唤醒因调用wait而阻塞的进程(如生产者),然后自身退出管程,被唤醒的进程进入管程。

4、消息传递

通过两条原语send和receive在进程间进行通信,send为发送消息而receive为接收消息(有可能发生阻塞),也就是让消息称为共享资源的载体。使用消息传递机制解决生产者-消费者问题的代码如下:

5、屏障

一种应用于进程组的同步机制。它规定,所有进程都完成了第n阶段,才能进入第n+1阶段。也就是说,当有进程完了第n阶段而另一些进程没有完成第n阶段时,完成的那些进程是需要阻塞等待未完成进程的,如下图所示。这可以在每个阶段的末尾添加屏障来实现这一功能。

参考:

《现代操作系统》 P66-P82.

【操作系统】进程间通信

时间: 2024-08-27 06:02:20

【操作系统】进程间通信的相关文章

操作系统--进程间通信(一)

原文引用https://www.dazhuanlan.com/2019/08/26/5d632d2a07f58/ IPC 运用范畴 配图与本文无关,纯属有趣 相对于做点什么,我们更想睡懒觉,但是相对于睡觉,我想写总结!今天把博客的 git 分支搞混了,浪费了一点时间.那么今天介绍一下进程间通信(IPC) .进程间通信,我们从 << UNIX网络编程卷2:进程间通信 >> 最后一章 Sun RPC 谈起,那为什么呢? 因为我们去掌握一个东西,肯定是因为它有价值,我们才去学习,如果一些

操作系统--进程间通信

(1) 共享内存:通常由一个进程创建,其余进程对这块内存区进行读写.共享内存区域是被多个进程共享的一部分物理内存.如果多个进程都把该内存区域映射到自己的虚拟地址空间,则这些进程就都可以直接访问该共享内存区域,从而可以通过该区域进行通信.共享内存是进程间共享数据的一种最快的方法,一个进程向共享内存区域写入了数据,共享这个内存区域的所有进程就可以立刻看到其中的内容. (2) 管道:管道是单向的.先进先出的.无结构的.固定大小的字节流,它把一个进程的标准输出和另一个进程的标准输入连接在一起.写进程在管

反应堆模式

反应堆模式 he Reactor:An Object-Oriented Wrapper for Event-Driven Port Monitoring and Service Demultiplexing 反应堆模式:一种应用于事件驱动的端口监控和服务多路化的面向对象封装器 Douglas C. Schmidt An earlier version of this paper appeared in the February 1993 issue of the C++ Report. 这篇文章

反应堆模式最牛的那篇论文--由solidmango执笔翻译

The Reactor:An Object-Oriented Wrapper for Event-Driven Port Monitoring and Service Demultiplexing 反应堆模式:一种应用于事件驱动的端口监控和服务多路化的面向对象封装器 Douglas C. Schmidt An earlier version of this paper appeared in the February 1993 issue of the C++ Report. 这篇文章的早期版本

Java面试题集

前几天,有朋友去面试之前问我关于后端架构相关的问题,但奈于我去年很多其它的工作是在移动SDK开发上,对此有所遗忘,实属无奈,后面准备总结下. 今天要谈的主题是关于求职.求职是在每一个技术人员的生涯中都要经历多次,对于我们大部分人而言,在进入自己心仪的公司之前少不了准备工作,有一份全面仔细面试题将帮助我们降低很多麻烦.在跳槽季来临之前,特地做这个系列的文章,一方面帮助自己巩固下基础,还有一方面也希望帮助想要换工作的朋友. 从12年開始,我先后做过爬虫,搜索,机器学习,javaEE及Android等

转自52 梦回凉亭的她 Java常见问题,面试题

收集整理分享 # 相关概念## 面向对象的三个特征封装,继承,多态.这个应该是人人皆知.有时候也会加上抽象. ## 多态的好处允许不同类对象对同一消息做出响应,即同一消息可以根据发送对象的不同而采用多种不同的行为方式(发送消息就是函数调用).主要有以下优点: 1. 可替换性:多态对已存在代码具有可替换性.2. 可扩充性:增加新的子类不影响已经存在的类结构.3. 接口性:多态是超类通过方法签名,向子类提供一个公共接口,由子类来完善或者重写它来实现的.4. 灵活性:5. 简化性: ### 代码中如何

java工程师面试常问的多线程问题【推荐】

思考题:1.说说进程,线程,协程之间的区别 思考题:希望大家积极的思考,并且可以踊跃的说出自己的想法,想法不管对与错,只要说出来就是一种提高,所以,希望小伙伴们可以把自己的想法在留言区给出,这样大家也可以相互学习,有启发的作用,扩展知识面,提高面试能力~ 2.你了解守护线程吗?它和非守护线程有什么区别 程序运行完毕,jvm会等待非守护线程完成后关闭,但是jvm不会等待守护线程.守护线程最典型的例子就是GC线程 3.什么是多线程上下文切换 多线程的上下文切换是指CPU控制权由一个已经正在运行的线程

bat等大公司常考java多线程面试题

1.说说进程,线程,协程之间的区别 简而言之,进程是程序运行和资源分配的基本单位,一个程序至少有一个进程,一个进程至少有一个线程.进程在执行过程中拥有独立的内存单元,而多个线程共享内存资源,减少切换次数,从而效率更高.线程是进程的一个实体,是cpu调度和分派的基本单位,是比程序更小的能独立运行的基本单位.同一进程中的多个线程之间可以并发执行. 2.你了解守护线程吗?它和非守护线程有什么区别 程序运行完毕,jvm会等待非守护线程完成后关闭,但是jvm不会等待守护线程.守护线程最典型的例子就是GC线

Keep面经汇总

一.Java 线程如何终止 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止. 使用stop方法强行终止线程. 使用interrupt方法中断线程. 如何用一个cancel方法停止两个线程 泛型原理.使用场景.优缺点 原理:泛型的实现是靠类型擦除技术,类型擦除是在编译期完成的,在编译期,编译器会将泛型的类型参数都擦除成它的限定类型,如果没有则擦除为object类型之后在获取的时候再强制类型转换为对应的类型. 使用场景:参数类型可以用在类.接口和方法的创建中,分别称为泛型类.泛型接口

史上最全Java面试题(带全部答案)

今天要谈的主题是关于求职,求职是在每个技术人员的生涯中都要经历多次.对于我们大部分人而言,在进入自己心仪的公司之前少不了准备工作,有一份全面细致面试题将帮助我们减少许多麻烦.在跳槽季来临之前,特地做这个系列的文章,一方面帮助自己巩固下基础,另一方面也希望帮助想要换工作的朋友. 相关概念 面向对象的三个特征 封装,继承,多态,这个应该是人人皆知,有时候也会加上抽象. 多态的好处 允许不同类对象对同一消息做出响应,即同一消息可以根据发送对象的不同而采用多种不同的行为方式(发送消息就是函数调用).主要