操作系统之进程间通信

在进程间进行通信的时候,我们常常需要考虑几个问题。
进程之间的信息如何进行传递,进程之间在进行资源竞争的时候如何保证不会出现交叉和如何保证访问资源之间存在依赖的时候确保正确的访问顺序。

  • 竞争条件
    在操作系统中,多个进程间进行相互协作,往往需要对某些共享数据进行访问读写,而在多个进程进行数据读写的时候,最终结果往往取决进程运行的时序,叫做竞争条件,然而这是不可控的,因此我们需要阻止多个进程同时对共享数据进行读写,实现互斥。
  • 临界区
    为了避免竞争条件,我们把对共享变量进行访问的程序片段称为临界区,只要我们能够在两个进程不能同时出现在临界区,即可实现互斥,避免竞争条件,但是这不能保证并发程序能够正确高效的进行协作。因此,需要满足以下的几个条件。

    • 任何两个进程不能同时处于其临界区中。
    • 不应该对硬件设备有所要求,即与硬件无关,不能对CPU的速度或者数量做出假设。
    • 处在临界区之外的进程不能对其他的进程进行阻塞。
    • 处在临界区外的进程不能无限期的等待,即不会发生死锁和饥饿。
  • 互斥
    • 屏蔽中断
      在单处理器系统中,由于并发进程不能重叠,只能交替,我们可以通过屏蔽中断的方法来实现互斥,即在进程进入临界区的时候,屏蔽中断,这样CPU将不会切换到其他进程,在离开临界区的时候,在打开中断,就可以实现互斥。但是屏蔽中断会导致进程只能交替进行,执行效率会降低,同时,如果是在多处理器系统中,屏蔽中断只是对执行disable指令的CPU有效,其他CPU继续运行,然后可以访问临界区,不能保证互斥。
    • 锁变量
      我们可以通过锁变量来实现互斥,锁变量初始值为1,进程访问共享数据,进入临界区的时候,对锁变量进行检查,如果锁变量为负值,进程就被挂起,如果值为一的话,锁变量减为零,进程就立即进入临界区,接着之后的每一个进程试图去访问临界区都会让锁变量减一,然后添加到等待队列中,当进程从临界区退出后,锁变量加一,然后唤醒等待队列中一个被阻塞的线程,进程置于就绪状态,在下一次调度的时候,就可以进入临界区了。但是如果一个进程在发现锁变量为1,试图进入临界区并对锁变量进行减一操作的时候,另一个进程调度运行,也发现锁变量为1,试图进入临界区,这样的话就会导致有两个进程可以访问临界区。
    • 严格轮换法
      严格轮换法通过turn变量,用来记录那个进程进入临界区,当一个进程0检查turn变量的时候,发现其值为0,就进入临界区,另一个进程试图访问临界区的时候,发现turn变量为0,它就会不停的循环去测试变量什么时候变量为某个值,称为忙等待。忙等待会一直循环, 损耗CPU资源,浪费CPU时间,一般用于等待时间比较短的情况,如果一个进程比另一个进程慢很多的话,就不应该轮流进入临界区。
  • 睡眠与唤醒
    睡眠是一个引起调用进程阻塞的系统调用,即将挂起,直到另一个进程将其唤醒,唤醒会将睡眠进程加入到运行队列中,直到下次调用器调度的时候才会被运行。

信号量

信号量使用整型变量来累计唤醒次数,取值为0表示没有保存下来的唤醒操作,或者正值表示有一个或者多个唤醒操作。
信号量有两种操作:sleep和wakeup,对信号量执行sleep操作,首先对信号量的值进行检查,如果大于0,就将值减一,继续执行操作,否则等于0的话,进程就会睡眠。检查变量、修改变量值和可能会发生的睡眠操作都是原子操作,在操作完成之前,其他进程对信号量的操作都是不允许的。执行wakeup操作,对信号量的值加一,如果之前有进程在信号量上睡眠了,会选择一个进程进行唤醒,继续执行剩余的操作,因此,对一个有进程睡眠的信号量来说,执行wwakeup操作,不会修改信号量的值,只是在其上睡眠的进程少了一个。
信号量除了可以用来实现互斥,也可以用来实现同步,用来保证只有一个进程能对临界区的共享数据进行读写访问。

互斥量 

互斥量是信号量的简化版本,没有信号量的计数功能,互斥量是处于两态之一的变量:解锁或者加锁,一般情况下使用整型变量来表示,0表示解锁,其他的值则表示加锁。当一个进程访问临界区的时候,需要对互斥量进行检验,如果当前的互斥量是解锁的,则访问成功,可以对临界区进行访问,否则互斥量已经加锁的话,调用进程就会阻塞,直到进入临界区的进程退出临界区为止,如果有多个进程阻塞在临界区外,则随机选择一个进程唤醒获取到锁。

管程 

管程可以看成是一种程序结构,他包括:

  • 多个可以彼此交互并共享资源的进程。
  • 多个与资源使用有关的条件变量
  • 一个互斥锁
  • 一个用来避免静态条件的不变量

管程提供了一种机制,可以让进程临时放弃互斥访问,等到条件变量满足之后,重新获得执行权恢复互斥访问,如果每个执行的进程在放弃互斥锁之前都能保证不变量成立,就是说所有的进程都不会导致竞态条件成立,当一个进程执行管程中的子程序时,即占用了管程,管程能保证在一个时间点之内,最多只有一个进程占用管程,即实现了互斥。
如果我们需要调用一个在管程中的子程序的时候,需要等到已经没有其他进程在执行该管程的子程序。每一个管程对象都一个把互斥锁,初始状态为解锁,在管程的子程序的入口给互斥锁加锁,在出口给互斥锁解锁。管程还提供条件变量为进程在无法继续执行的时候阻塞,并把另一个在管程外的进程调入到管程中。

消息传递 

在操作系统中,如果两个进程之间不存在可以直接访问的共享空间,必须利用操作系统提供的消息传递方法实现进程通信,通过send和receive原语进行数据传输。

  • 直接通信方式:如果发送进程可以把数据直接发送到接收进程,就可以把数据挂在接收进程的消息缓冲队列上,接收进程则可以通过从缓冲队列中获取数据了。
  • 间接通信方式:可以通过创建一个一定数量消息进程缓冲的地方,叫做信箱,在使用信箱进程数据传输的时候,send和receive元素指定的地址不再是源地址或者目的地址,而是信息地址。缓冲机制主要是用来容纳那些已经发送但是还没有被接收进程接收的消息。当时我们也可以取消缓冲,调用send方法时发送进程会被阻塞,直到receive调用,这种行为可以称为rendezvous。

屏障 

屏障主要是用于进程组,如果应用要求所有的进程都到达一个阶段就绪,否则任何进程都不能进入下一个阶段,我们可以在每一个阶段的结尾放置一个屏障来实现这种行为,当一个进程到达屏障时,也就被屏障阻拦,主要所有的进程达到屏障,才能打破屏障,到达下一个阶段。

COMMENT AND SHARE

  • PAGE 1 OF 1

冼毅俊

Xupter√Java√德桌迷√虐心控√mugen爱好者√音乐杂食党√小说发烧友√基本色√鱼迷√stan√bitch√daydreamer


Java研发


长安