几种常用的进程间通信的方式,通信特点和通信方式的优缺点

由于不同的进程运行在各自不同的内存空间中.一方对于变量的修改另一方是无法感知的.因此.进程之间的信息传递不可能通过变量或其它数据结构直接进行,只能通过进程间通信来完成。

进程到进程之间的通信 进程之间的通信方法进程到进程之间的通信 进程之间的通信方法


进程到进程之间的通信 进程之间的通信方法


进程到进程之间的通信 进程之间的通信方法


根据进程通信时信息量大小的不同,可以将进程通信划分为两大类型:控制信息的通信和大批数据信息的通信.前者称为低级通信,后者称为高级通信。

主要用于进程之间的同步、互斥、终止、挂起等等控制信息的传递。

主要用于进程间数据块的交换和共享 常见的高级通信有管道(PIPE)、消息队列(MESSAGE)、共享内存(SHARED MEM0RY)等。

两个进程利用管道进行通信时.发送信息的进程称为写进程.接收信息的进程称为读进程。管道通信方式的中间介质就是文件.通常称这种文件为管道文件.它就像管道一样将一个写进程和一个读进程连接在一起,实现两个进程之间的通信。写进程通过写入端(发送端)往管道文件中写入信息;读进程通过读出端(接收端)从管道文件中读取信息。两个进程协调不断地进行写和读,便会构成双方通过管道传递信息的流水线。

利用系统调用PIPE()可以创建一个无名管道文件,通常称为无名管道或PIPE;利用系统调用MKNOD()可以创建一个有名管道文件.通常称为有名管道或FIFO。无名管道是一种非性的管道通信机构.当它访问的进程全部终止时,它也将随之被撤消。无名管道只能用在具有家族联系的进程之间。有名管道可以长期存在于系统之中.而且提供给任意关系的进程使用,但是使用不当容易导致出错.所以作系统将命名管道的管理权交由系统来加以控制管道文件被创建后,可以通过系统调用WRITE()和READ()来实现对管道的读写作;通信完后,可用CLOSE()将管道文件关闭。

多个的进程之间可以通过消息缓冲机制来相互通信.这种通信的实现是以消息缓冲区为中间介质.通信双方的发送和接收作均以消息为单位。在存储器中,消息缓冲区被组织成队列,通常称之为消息队列。消息队列一旦创建后即可由多进程共享.发送消息的进程可以在任意时刻发送任意个消息到指定的消息队列上,并检查是否有接收进程在等待它所发送的消息。若有则唤醒它:而接收消息的进程可以在需要消息的时候到指定的消息队列上获取消息.如果消息还没有到来.则转入睡眠状态等待。

针对消息缓冲需要占用CPU进行消息的缺点.OS提供了一种进程间直接进行数据交换的通信方式一共享内存 顾名思义.这种通信方式允许多个进程在外部通信协议或同步,互斥机制的支持下使用同一个内存段(作为中间介质)进行通信.它是一种有效的数据通信方式,其特点是没有中间环节.直接将共享的内存页面通过附接.映射到相互通信的进程各自的虚拟地址空间中.从而使多个进程可以直接访问同一个物理内存页面.如同访问自己的私有空间一样(但实质上不是私有的而是共享的)。因此这种进程间通信方式是在同一个计算机系统中的诸进程间实现通信的快捷的方法.而它的局限性也在于此.即共享内存的诸进程必须共处同一个计算机系统.有物理内存可以共享才行。

1.无名管道简单方便.但局限于单向通信的工作方式.并且只能在创建它的进程及其子孙进程之间实现管道的共享:有名管道虽然可以提供给任意关系的进程使用.但是由于其长期存在于系统之中,使用不当容易出错。

2.消息缓冲可以不再局限于父子进程.而允许任意进程通过共享消息队列来实现进程间通信.并由系统调用函数来实现消息发送和接收之间的同步.从而使得用户在使用消息缓冲进行通信时不再需要考虑同步问题.使用方便,但是信息的需要额外消耗CPU的时间.不适宜于信息量大或作频繁的场合。

3.共享内存针对消息缓冲的缺点改而利用内存缓冲区直接交换信息,无须,快捷、信息量大是其优点。但是共享内存的通信方式是通过将共享的内存缓冲区直接附加到进程的虚拟地址空间中来实现的.因此,这些进程之间的读写作的同步问题作系统无法实现。必须由各进程利用其他同步工具解决。另外,由于内存实体存在于计算机系统中.所以只能由处于同一个计算机系统中的诸进程共享。不方便网络通信。

linux 进程间通信的几种方式

1管道(Pipe)及有名管道(named pipe):管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信;

2信号(Signal):信号是比较复杂的通信方式,用于通知接受进程有某种发生,除了用于进程间通信外,进程还可以发送信号给进程本身;linux除了支持Unix早期信号语义函数sigal外,还支持语义符合Posix.1标准的信号函数sigaction(实际上,该函数是基于BSD的,BSD为了实现可靠信号机制,又能够统一对外接口,用sigaction函数重新实现了signal函数);

3报文(Message)队列(消息队列):消息队列是消息的链接表,包括Posix消息队列 V消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺点。

4共享内存:使得多个进程可以访问同一块内存空间,是快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制,如信号量结合使用,来达到进程间的同步及互斥。

5信号量(semaphore):主要作为进程间以及同一进程不同线程之间的同步手段。

6套接口(Socket):更为一般的进程间通信机制,可用于不同机器之间的进程间通信。起初是由Unix系统的BSD分支开发出来的,但现在一般可以移植到其它类Unix系统上:Linux和System V的变种都支持套接字。

作系统之 进程间通信的方式有哪些

进程能够单独运行并且完成一些任务,但是也经常免不了和其他进程传输数据或互相通知消息,即需要进行通信,本文将简单介绍一些进程之间相互通信的技术--进程间通信(InterProcess Communication,IPC)。由于篇幅有限,本文不会对每一种进行详细介绍。

进程间通信常见方式如下:

管道

FIFO

消息队列

信号量

共享内存

UNXI域套接字

套接字(Socket)

管道是一种古老的IPC通信形式。它有两个特点:

半双工,即不能同时在两个方向上传输数据。有的系统可能支持全双工。

只能在父子进程间。经典的形式就是管道由父进程创建,进程fork子进程之后,就可以在父子进程之间使用了。

()函数虽然也能够执行系统命令,但是无法获取执行状态码,而执行系统命令本质上就需要创建子进程来完成,因此利用管道可以很方便的获取子进程的输出内容。本文不详细展开。

FIFO也被称为命名管道,与管道不同的是,不相关的进程也能够进行数据交换。

而FIFO也常常有以下两个用途:

无需创建中间临时文件,输出流

多客户-服务进程应用中,通过FIFO作为汇聚点,传输客户进程和服务进程之间的数据

两个没有亲缘关系的进程可以通过FIFO进行通信。

消息队列可以认为是一个消息链表,存储在内核中,进程可以从中读写数据。与管道和FIFO不同,进程可以在没有另外一个进程等待读的情况下进行写。另外一方面,管道和FIFO一旦相关进程都关闭并退出后,里面的数据也就没有了,但是对于消息队列,一个进程往消息队列中写入数据后退出,另外一个进程仍然可以打开并读取消息。消息队列与后面介绍的UNIX域套接字相比,在速度上没有多少优势。

信号量是一个计数器,它主要用在多个进程需要对共享数据进行访问的时候。考虑这一的情况,不能同时有两个进程对同一数据进行访问,那么借助信号量就可以完成这样的事情。

它的主要流程如下:

检查控制该资源的信号量

如果信号量值大于0,则资源可用,并且将其减1,表示当前已被使用

如果信号量值为0,则进程休眠直至信号量值大于0

也就是说,它实际上是提供了一个不同进程或者进程的不同线程之间访问同步的手段。

共享内存允许多个进程共享一个给定的存储区,由于它们是共享一块内存数据,因此其速度非常快。但是需要另外提供手段来保证共享内存的同步访问,例如它可以用到前面所提到的信号量来实现访问同步。

UNIX域套接字和套接字很相似,但是它有更高的效率,因为它不需要执行协议处理,例如计算校验和,发送确认报文等等,它仅仅数据。

当然,它也只适用于同一台计算机上的进程间通信。

例如redis服务配置unixsocket启动后,通过redis-cli的-s参数就可以指定UNIX域套接字,连接到redis。

它会比使用网络套接字的速度要快。

这个不用多说,它利用网络进行通信,与前面所提到的通信方式不同的是,它能用于不同计算机之间的不同进程间通信。

本文简单介绍了进程间通信的常见方式,其中对管道和命名管道我们使用了一个例子来简单说明,因为我们可能会经常见到它。对于FIFO,后一个引用它的进程终止时,留在FIFO的数据也将会被删除,而对于消息队列却不是这样,它会一直留到被显示删除或者系统自举,另外消息队列于其他方式相比并没有特别的优势。而信号量实际上常用于共享数据的同步访问。共享内存在进程间传递数据非常高效,但是系统没有对访问进行同步,因此还需要另外实现数据的访问同步。套接字(socket)是应该目前应用广泛的进程间通信方式。

什么层是实现进程到进程之间的通信

应用层。

进程的定义:

进程是指在系统中正在运行的一个应用程序。一个进程至少包括一个线程,通常将该线程称为主线程。一个进程从主线程的执行开始进而创建一个或多个附加线程,就是所谓基于多线程的多任务。

应用层:

应用层(Application layer)是七层OSI模型的第七层。应用层直接和应用程序接口并提供常见的网络应用服务。应用层也向表示层发出请求。

应用层是开放系统的层,是直接为应用进程提供服务的。

进程间通信方式

在作系统中,一个进程可以理解为是关于计算机资源的一次运行活动,其就是一个正在执行的程序的实例。从概念上来说,一个进程拥有它自己的虚拟CPU和虚拟地址空间,任何一个进程对于彼此而言都是相互的,这也引入了一个问题 —— 如何让进程之间互相通信?

由于进程之间是互相的,没有任何手段直接通信,因此我们需要借助作系统来辅助它们。举个通俗的例子,如A与B之间是的,不能彼此联系,如果它们想要通信的话可以借助第三方C,比如A将信息交给C,C再将信息转交给B —— 这就是进程间通信的主要思想 —— 共享资源。

这里要解决的一个重要的问题就是如何避免竞争,即避免多个进程同时访问临界区的资源。

共享内存是进程间通信中简单的方式之一。共享内存允许两个或更多进程访问同一块内存。当一个进程改变了这块地址中的内容的时候,其它进程都会察觉到这个更改。

你可能会想到,我直接创建一个文件,然后进程不就都可以访问了?

是的,但这个方法有几个缺陷:

Linux下采用共享内存的方式来使进程完成对共享资源的访问,它将磁盘文件到内存,并创建虚拟地址到该内存的映射,就好像该资源本来就在进程空间之中,此后我们就可以像作本地变量一样去作它们了,实际的写入磁盘将由系统选择佳方式完成,例如作系统可能会批量处理加排序,从而大大提高IO速度。

如同上图一样,进程将共享内存映射到自己的虚拟地址空间中,进程访问共享进程就好像在访问自己的虚拟内存一样,速度是非常快的。

共享内存的模型应该是比较好理解的:在物理内存中创建一个共享资源文件,进程将该共享内存绑定到自己的虚拟内存之中。

这里要解决的一个问题是如何将同一块共享内存绑定到自己的虚拟内存中,要知道在不同进程中使用 malloc 函数是会顺序分配空闲内存,而不会分配同一块内存,那么要如何去解决这个问题呢?

Linux作系统已经想办法帮我们解决了这个问题,在 #include 和 #include 头文件下,有如下几个shm系列函数:

通过上述几个函数,每个的进程只要有统一的共享内存标识符便可以建立起虚拟地址到物理地址的映射,每个虚拟地址将被翻译成指向共享区域的物理地址,这样就实现了对共享内存的访问。

还有一种相像的实现是采用mmap函数,mmap通常是直接对磁盘的映射——因此不算是共享内存,存储量非常大,但访问慢; shmat与此相反,通常将资源保存在内存中创建映射,访问快,但存储量较小。

不过要注意一点,作系统并不保证任何并发问题,例如两个进程同时更改同一块内存区域,正如你和你的朋友在线编辑同一个文档中的同一个标题,这会导致一些不好的结果,所以我们需要借助信号量或其他方式来完成同步。

信号量是迪杰斯特拉提出的一种为解决 同步不同执行线程问题 的一种方法,进程与线程抽象来看大同小异,所以 信号量同样可以用于同步进程间通信 。

信号量 s 是具有非负整数值的全局变量,由两种特殊的 原子作 来实现,这两种原子作称为 P 和 V :

信号量并不用来传送资源,而是用来保护共享资源,理解这一点是很重要的,信号量 s 的表示的含义为 同时允许访问资源的进程数量 ,它是一个全局变量。来考虑一个上面简单的例子:两个进程同时修改而造成错误,我们不考虑读者而仅仅考虑写者进程,在这个例子享资源多允许一个进程修改资源,因此我们初始化 s 为1。

开始时,A率先写入资源,此时A调用P(s),将 s 减一,此时 s = 0,A进入共享区工作。

此时,进程B也想进入共享区修改资源,它调用P(s)发现此时s为0,于是挂起进程,加入等待队列。

A工作完毕,调用V(s),它发现s为0并检测到等待队列不为空,于是它随机唤醒一个等待进程,并将s加1,这里唤醒了B。

B被唤醒,继续执行P作,此时s不为0,B成功执行将s置为0并进入工作区。

此时C想要进入工作区......

可以发现,在无论何时只有一个进程能够访问共享资源,这就是信号量做的事情,他控制进入共享区的进程数量,这取决于初始化s的值。此后,在进入共享区之前调用P作,出共享区后调用V作,这就是信号量的思想。

在Linux下并没有直接的P&V函数,而是需要我们根据这几个基本的sem函数族进行封装:

正如其名,管道就如同生活中的一根管道,一端输送,而另一端接收,双方不需要知道对方,只需要知道管道就好了。

管道是一种 基本的进程间通信机制。 管道由pipe函数来创建: 调用pipe函数,会在内核中开辟出一块缓冲区用来进行进程间通信,这块缓冲区称为管道,它有一个读端和一个写端。管道被分为匿名管道和有名管道。

匿名管道通过pipe函数创建,这个函数接收一个长度为2的Int数组,并返回1或0表示成功或者失败:

int pipe(int fd[2])

这个函数打开两个文件描述符,一个读端文件,一个写端,分别存入fd[0]和fd[1]中,然后可以作为参数调用 write 和 read 函数进行写入或读取,注意fd[0]只能读取文件,而fd[1]只能用于写入文件。

你可能有个疑问,这要怎么实现通信?其他进程又不知道这个管道,因为进程是的,其他进程看不到某一个进程进行了什么作。

是的,‘其他’进程确实是不知道,但是它的子进程却可以!这里涉及到fork派生进程的相关知识,一个进程派生一个子进程,那么子进程将会父进程的内存空间信息,注意这里是而不是共享,这意味着父子进程仍然是的,但是在这一时刻,它们所有的信息又是相等的。因此子进程也知道该全局管道,并且也拥有两个文件描述符与管道挂钩,所以 匿名管道只能在具有亲缘关系的进程间通信。

还要注意,匿名管道内部采用环形队列实现,只能由写端到读端,由于设计技术问题,管道被设计为半双工的,一方要写入则必须关闭读描述符,一方要读出则必须关闭写入描述符。因此我们说 管道的消息只能单向传递。

注意管道是堵塞的,如何堵塞将依赖于读写进程是否关闭文件描述符。如果读管道,如果读到空时,设此时写端口还没有被完全关闭,那么作系统会设还有数据要读,此时读进程将会被堵塞,直到有新数据或写端口被关闭;如果管道为空,且写端口也被关闭,此时作系统会认为已经没有东西可读,会直接退出,并关闭管道。

对于写一个已经满了的管道同理而言。

管道内部由内核管理,在半双工的条件下,保证数据不会出现并发问题。

了解了匿名管道之后,有名管道便很好理解了。在匿名管道的介绍中,我们说其他进程不知道管道和文件描述符的存在,所以匿名管道只适用于具有亲缘关系的进程,而命名管道则很好的解决了这个问题 —— 现在管道有一个的名称了,任何进程都可以访问这个管道。

注意,作系统将管道看作一个抽象的文件,但管道并不是普通的文件,管道存在于内核空间中而不放置在磁盘(有名管道文件系统上有一个标识符,没有数据块),访问速度更快,但存储量较小,管道是临时的,是随进程的,当进程销毁,所有端口自动关闭,此时管道也是不存在的,作系统将所有IO抽象的看作文件,例如网络也是一种文件,这意味着我们可以采用任何文件方法作管道,理解这种抽象是很重要的,命名管道就利用了这种抽象。

Linux下,采用mkfifo函数创建,可以传入要指定的‘文件名’,然后其他进程就可以调用open方法打开这个特殊的文件,并进行write和read作(那肯定是字节流对吧)。

注意,命名管道适用于任何进程,除了这一点不同外,其余大多数都与匿名管道相同。

消息队列亦称报文队列,也叫做信箱,是Linux的一种通信机制,这种通信机制传递的数据会被拆分为一个一个的数据块,也叫做消息体,消息体中可以定义类型与数据,克服了无格式承载字节流的缺陷(现在收到void后可以知道其原本的格式惹):

同管道类似,它有一个不足就是每个消息的长度是有上限的,整个消息队列也是长度限制的。

内核为每个IPC对象维护了一个数据结构struct ipc_perm,该数据结构中有指向链表头与链表尾部的指针,保证每一次插入取出都是O(1)的时间复杂度。

一个进程可以发送信号给另一个进程,一个信号就是一条消息,可以用于通知一个进程组发送了某种类型的,该进程组中的进程可以采取处理程序处理。

Linux下 unistd.h 头文件下定义了如图中的常量,当你在shell命令行键入 ctrl + c 时,内核就会前台进程组的每一个进程发送 SIGINT 信号,中止进程。

我们可以看到上述只有30个信号,因此作系统会为每一个进程维护一个int类型变量sig,利用其中30位代表是否有对应信号,每一个进程还有一个int类型变量block,与sig对应,其30位表示是否堵塞对应信号(不调用处理程序)。如果存在多个相同的信号同时到来,多余信号会被存储在一个等待队列中等待。

我们要理解进程组是什么,每个进程属于一个进程组,可以有多个进程属于同一个组。每个进程拥有一个进程ID,称为 pid ,而每个进程组拥有一个进程组ID,称为 pgid ,默认情况下,一个进程与其子进程属于同一进程组。

软件方面(诸如检测键盘输入是硬件方面)可以利用kill函数发送信号,kill函数接受两个参数,进程ID和信号类型,它将该信号类型发送到对应进程,如果该pid为0,那么会发送到属于自身进程组的所有进程。

接收方可以采用signal函数给对应添加处理程序,一旦发生,如果未被堵塞,则调用该处理程序。

Linux下有一套完善的函数用以处理信号机制。

Socket套接字是用与网络中不同主机的通信方式,多用于客户端与之间,在Linux下也有一系列C语言函数,诸如socket、connect、bind、listen与accept,我们无需花太多时间研究这些函数,因为我们可能一辈子都不会与他们打交道,对于原理的学习,后续我会对Ja中的套接字socket源码进行剖析。

对于工作而言,我们可能一辈子都用不上这些作,但作为对于作系统的学习,认识到进程间是如何通信还是很有必要的。

面试的时候对于这些方法我们不需要掌握到很深的程度,但我们必须要讲的来有什么通信方式,这些方式都有什么特点,适用于什么条件,大致是如何作的,能说出这些,基本足以让面试官对你十分满意了。

进程间的通信有哪些

进程间通信是指在不同进程之间进行数据交换和信息传递的机制。常见的进程间通信方式包括:

1、管道 (Pipe):一种单向通信的机制,只能在有亲缘关系的进程之间使用。

2、命名管道 (named pipe 或 FIFO):允许多个进程之间可以同时读、写同一个管道。

3、信号 (Signal):一种异步通信机制,用于通知目标进程发生了某个。

4、共享内存 (Shared Memory):多个进程共享同一块物理内存,可以实现高速的数据传输。

5、消息队列 (Message Queue):多个进程可以通过队列来传输消息,可以异步地进行通信。

6、套接字 (Socket):用于在网络上进行进程间通信和远程过程调用。

7、信号量 (Semaphore):用于控制多个进程对共享资源的访问,并防止竞态条件。

8、文件锁 (File Lock):用于协调多个进程对同一个文件进行访问的机制,可以避免竞争和数据损坏。

进程间通信的主要应用领域包括:

1、作系统:IPC 是作系统中各个进程之间进行通信、数据交换和资源共享的基础。

2、网络编程:进程间通信在网络编程中也是必不可少的。通过套接字和网络协议,各个应用程序可以在网络上实现通信和数据交换。

3、分布式系统:在分布式系统中,IPC 对于实现不同之间的协调、信息共享、任务协同等方面起到了重要的作用。

4、数据库管理系统:数据库管理系统(DBMS)中,多个进程之间也需要进行通信,以共享数据库的信息和实现并发控制等功能。

5、图形用户界面:图形用户界面(GUI)程序中,用户界面和后台数据处理进程之间也需要进行通信,以实现数据交换和用户界面的更新。

6、并行计算:在并行计算中,各个进程需要共享数据和相互协调,从而实现高效的任务分配和计算。

进程间的通信方式:

进程间通信(IPC,Inter-Process Communication)是指在不同进程间传播和交换信息。

常见的进程间的通信方式为7种:

按照通信类型划分:

1,共享存储系统

2,管道通信系统。

3,消息传递系统。

4,客户机系统。

把一个进程连接到另外一个进程的一个数据流成为管道,通常一个进程的输出作为另外一个进程的输入。本质是内核的一块缓存。Linux的管道主要有两种:无名管道和有名管道。

基本特性:

消息队列使用例子详细介绍

特点:

1,信号量用于进程间同步,若要在进程之间传递需要结合共享内存。

2,信号量是基于作系统的PV作,程序对信号量的作都是原子作。

3,每次对信号量的PV作不仅限于对信号量的值加1或减1,而且可以加减任意正整数。

4,支持信号量组。

信号量函数介绍及其实践例子

共享存储的介绍及其实践

套接字的介绍及其实践

信号介绍

信号的介绍及其实践