进程间通信

进程间常见的通信方式:

  1. 管道pipe:管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程或者兄弟进程之间。
  2. 命名管道FIFO:也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
  3. 消息队列MessageQueue:消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
  4. 共享内存SharedMemory:共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC(进程间通信) 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。
  5. 信号量Semaphore:信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
  6. 信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
  7. 套接字Socket:套接字也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同设备及其间的进程通信。

1、管道

管道允许在进程之间按先进先出的方式传送数据,是进程间通信的一种常见方式。
管道,通常指无名管道,是UNIX系统IPC(进程间通信)最古老的形式。它具有如下特点:

(1)它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端。
(2)它只能用于具有亲缘关系的进程之间的通信(也是父子进程或者兄弟进程之间)。
(3)它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write 等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。

当一个管道建立时,它会创建两个文件描述符:fd[0]为读取端描述符,fd[1]为写入端描述符。如下图:

在这里插入图片描述
要关闭管道只需将这两个文件描述符关闭即可。
:单个进程中的管道几乎没有任何用处。所以,通常在调用 pipe 的进程后接着调用 fork,这样就创建了父进程与子进程之间的 IPC 通道。如下图所示:
在这里插入图片描述若要数据流从父进程流向子进程,则关闭父进程的读端(fd[0])与子进程的写端(fd[1]);反之,则可以使数据流从子进程流向父进程。

2、命名管道

另外一种管道是命名管道(FIFO),除了建立、打开、删除的方式不同外,这两种管道几乎是一样的。命名管道的特点如下:
(1)它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端。
(2)FIFO可以在无关的进程之间交换数据,与无名管道不同。 FIFO有路径名与之相关联,它以一 种特殊设备文件形式存在于文件系统中。

3、消息队列

消息队列,是消息的链接表,存放在内核中。一个消息队列由一个标识符(即队列ID)来标识,用户进程可以向消息队列添加消息,也可以向消息队列读取消息。它的特点如下:

(1)消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级
(2)消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会被删除。
(3)消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取。

可以把消息看做一个记录,具有特定的格式以及特定的优先级。对消息队列有写权限的进程可以向消息队列中按照一定的规则添加新消息,对消息队列有读权限的进程可以从消息队列中读取消息。

消息队列的常用函数如下表:

在这里插入图片描述
进程间通过消息队列通信的过程,主要是:创建或打开消息队列,添加消息,读取消息和控制消息队列。
例如:用函数msget创建消息队列,调用msgsnd函数,把输入的字符串添加到消息队列中,然后调用msgrcv函数,读取消息队列中的消息并打印输出,最后再调用msgctl函数,删除系统内核中的消息队列。

4、共享内存

共享内存(Shared Memory),指两个或多个进程共享一个给定的存储区。它具有如下特点:
(1)共享内存是最快的一种 IPC,因为进程是直接对内存进行存取。
(2)因为多个进程可以同时操作,所以需要进行同步。 信号量+共享内存通常结合在一起使用,信号量用来同步对共享内存的访问。

共享内存允许两个或多个进程共享一个给定的存储区,这一段存储区可以被两个或两个以上的进程映射至自身的地址空间中,一个进程写入共享内存的信息,可以被其他使用这个共享内存的进程,通过一个简单的内存读取读出,从而实现了进程间的通信。
采用共享内存进行通信的一个主要好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝。

为什么效率高?
对于像管道和消息队列等通信方式,则需要在内核和用户空间进行四次的数据拷贝,而共享内存则只拷贝两次:一次从输入文件到共享内存区,另一次从共享内存到输出文件。

共享内存区是最快的可用IPC形式,一旦这样的内存区映射到共享它的进程的地址空间,这些进程间数据的传递就不再通过执行任何进入内核的系统调用来传递彼此的数据,节省了时间。

共享内存和消息队列,FIFO,管道传递消息的区别:后者,消息队列,FIFO,管道的消息传递方式一般为:
1:服务器得到输入
2:通过管道,消息队列写入数据,通常需要从进程拷贝到内核。
3:客户从内核拷贝到进程
4:然后再从进程中拷贝到输出文件
上述过程通常要经过4次拷贝,才能完成文件的传递。而共享内存只需要:
1:从输入文件到共享内存区
2:从共享内存区输出到文件
上述过程不涉及到内核的拷贝,所以花的时间较少。

在这里插入图片描述

5、信号量

信号量(semaphore)与已经介绍过的 IPC 结构不同,它是一个计数器。信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据。信号量+共享内存通常结合在一起使用。它具有如下特点:
(1)信号量用于进程间同步,若要在进程间传递数据需要结合共享内存
(2)信号量基于操作系统的 PV 操作,程序对信号量的操作都是原子操作
(3)每次对信号量的 PV 操作不仅限于对信号量值加 1 或减 1,而且可以加减任意正整数。
(4)支持信号量组。

面试题:介绍一下信号量
(以下是从进程和线程的角度对信号量的总结)
信号量(Semaphore),是在多线程环境下使用的一种措施,可以用来保证两个或多个关键代码段不被并发调用。在进入一个关键代码段之前,线程必须获取一个信号量;一旦该关键代码段完成了,那么该线程必须释放信号量。其它想进入该关键代码段的线程必须等待直到第一个线程释放信号量。
信号量是一个非负整数,所有通过它的线程/进程都会将该整数减一(通过它当然是为了使用资源),当该整数值为零时,所有试图通过它的线程都将处于等待状态。在信号量上我们定义两种操作: Wait(等待) 和 Release(释放)。当一个线程调用 Wait 操作时,它要么得到资源然后将信号量减 1,要么一直等下去(指放入阻塞队列),直到信号量大于等于 1 时。Release(释放)实际上是在信号量上执行加操作。

6、信号

信号跟信号量虽然名字相似度 66.66%,但两者⽤途完全不⼀样,就好像 Java 和 JavaScript 的区别。

概念:

  1. 信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式
  2. 信号可以直接进行用户空间进程和内核进程之间的交互,内核进程也可以利用它来通知用户空间进程发生了哪些系统事件。
  3. 如果该进程当前并未处于执行态,则该信号就由内核保存起来,直到该进程恢复执行再传递给它;如果一个信号被进程设置为阻塞,则该信号的传递被延迟,直到其阻塞被取消时才被传递给进程。

2.信号的发送

1)用户按某些按键时产生信号。
2)硬件产生异常时产生信号。
3)进程用kill函数将信号发送给另一个进程。
4)用户用kill系统命令将信号发送给其他进程。

3.信号的处理:

1)忽略信号:对信号不做任何处理,但是有两个信号不能忽略:即SIGKILL及SIGSTOP。
2)捕捉信号:定义信号处理函数,当信号发生时,执行相应的处理函数。
3)执行缺省操作:Linux对每种信号都规定了默认操作

4.常见信号:

SIGINT:ctrl+c 终止信号
SIGQUIT:ctrl+\ 退出信号
SIGSTOP:ctrl+z 暂停信号
SIGSCONT:ctrl+z 继续信号
SIGALRM:闹钟信号 收到此信号后定时结束,结束进程
SIGCHLD:子进程状态改变,父进程收到信号
SIGKILL:杀死信号

参考博客:进程间通信之 信号(signal)

7、socket

前⾯提到的管道、消息队列、共享内存、信号量和信号都是在同⼀台主机上进⾏进程间通信,那要想跨⽹络与不同主机上的进程之间通信,就需要 Socket 通信了。
实际上,Socket 通信不仅可以跨⽹络与不同主机的进程间通信,还可以在同主机上进程间通信。

可参考博客 进程间通信之socket

总结:

七种进程间通信方式的总结:
(1)管道:速度慢,容量有限,只有父子进程能通讯。
(2)命名管道:任何进程间都能通讯,但速度慢。
(3)消息队列:容量受到系统限制,且要注意第一次读的时候,要考虑上一次没有读完数据的问题。
(4)共享内存区:能够很容易控制容量,速度快,但要保持同步,比如一个进程在写的时候,另一个进程要注意读写的问题,相当于线程中的线程安全,当然,共享内存区同样可以用作线程间通讯,不过没这个必要,线程间本来就已经共享了同一进程内的一块内存。
(5)信号量:不能传递复杂消息,只能用来同步。
(6)信号:信号是进程间通信机制中唯⼀的异步通信机制,信号可以在应⽤进程和内核之间直接交互,内核也可以利⽤信号来通知⽤户空间的进程发⽣了哪些系统事件
(7)socket:套接字和信号支持不同主机上的两个进程IPC。

以上,就是进程间通信的主要机制了。你可能会问了,那线程通信间的⽅式呢?

同个进程下的线程之间都是共享进程的资源,只要是共享变量都可以做到线程间通信,⽐如全局变量,所以对于线程间关注的不是通信⽅式,⽽是关注多线程竞争共享资源的问题,信号量也同样可以在线程间实现互斥与同步:

  • 互斥的⽅式,可保证任意时刻只有⼀个线程访问共享资源;
  • 互斥的⽅式,可保证任意时刻只有⼀个线程访问共享资源;
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值