进程之间的通信方式「建议收藏」

2022-11-10 15:42:42 浏览数 (1)

进程间通信方式一般有以下几种: 1、管道,匿名管道,命名管道 2、信号 3、信号量 4、消息队列 5、共享内存 6、socket

管道

  • 管道数据只能单向流动,所以如果要实现双向通信,就要创建2个管道
  • 管道分为匿名管道和命名管道
    • 匿名管道只能在父子进程关系之间使用
    • 命名管道,可以在不关联的两个进程之间使用,因为它创建了一个类型为管道的设备文件,使用这个设备文件就可以通信。
  • 管道只能承载无格式的字节流

信号

信号是进程之间唯一的异步通信机制,信号的主要来源主要有硬件来源(入键盘操作ctrl C) 和软件来源(如kill命令),信号传递的信息比较少,主要用于通知进程某个时间已经发生。比如利用kill pid,可以让系统优雅停机。

信号量

信号量是一个计数器,可以用来控制多个进程对资源的访问,通常作为一种锁机制,防止某个进程正在访问共享资源,其他进程也访问资源

消息队列

消息队列克服了信号传递信息少、管道只能承载无格式的字节流,消息到了就放进去,需要的时候去取。与命名管道相比:消息队列的优势在于,它独立于发送和接收线程,消除了在同步命名管道的打开和关闭时可能产生的一些困难。

共享内存

共享内存就是映射一段能被进程之间共享的内存,这段内存由一个进程创建,但是多个进程都可以共享访问,是最快的一种进程间通信的方式(不需要从用户态到内核态的切换),它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。

socket

socket套接字,不仅仅可以用于本地进程通信,还可以用于不通主机进程之间的通信。

关于管道:

匿名管道的创建,需要通过下面这个系统调用: intpipe(int fd[2]) 这里表示创建一个匿名管道,并返回了两个描述符,一个是管道的读取端描述符 fd[0],另一个是管道的写入端描述符 fd[1]。注意,这个匿名管道是特殊的文件,只存在于内存,不存于文件系统中。

其实,所谓的管道,就是内核里面的一串缓存。从管道的一段写入的数据,实际上是缓存在内核中的,另一端读取,也就是从内核中读取这段数据。另外,管道传输的数据是无格式的流且大小受限。 看到这,你可能会有疑问了,这两个描述符都是在一个进程里面,并没有起到进程间通信的作用,怎么样才能使得管道是跨过两个进程的呢? 我们可以使用 fork 创建子进程,创建的子进程会复制父进程的文件描述符,这样就做到了两个进程各有两个「 fd[0] 与 fd[1]」,两个进程就可以通过各自的 fd 写入和读取同一个管道文件实现跨进程通信了。

管道只能一端写入,另一端读出,所以上面这种模式容易造成混乱,因为父进程和子进程都可以同时写入,也都可以读出。那么,为了避免这种情况,通常的做法是: 父进程关闭读取的 fd[0],只保留写入的 fd[1]; 子进程关闭写入的 fd[1],只保留读取的 fd[0];

所以说如果需要双向通信,则应该创建两个管道。 到这里,我们仅仅解析了使用管道进行父进程与子进程之间的通信,但是在我们 shell 里面并不是这样的。 在 shell 里面执行 A | B命令的时候,A 进程和 B 进程都是 shell 创建出来的子进程,A 和 B 之间不存在父子关系,它俩的父进程都是 shell。

所以说,在 shell 里通过「|」匿名管道将多个命令连接在一起,实际上也就是创建了多个子进程,那么在我们编写 shell 脚本时,能使用一个管道搞定的事情,就不要多用一个管道,这样可以减少创建子进程的系统开销。

我们可以得知,对于匿名管道,它的通信范围是存在父子关系的进程。因为管道没有实体,也就是没有管道文件,只能通过 fork 来复制父进程 fd 文件描述符,来达到通信的目的。

另外,对于命名管道,它可以在不相关的进程间也能相互通信。因为命令管道,提前创建了一个类型为管道的设备文件,在进程里只要使用这个设备文件,就可以相互通信。

不管是匿名管道还是命名管道,进程写入的数据都是缓存在内核中,另一个进程读取数据时候自然也是从内核中获取,同时通信数据都遵循先进先出原则,不支持 lseek 之类的文件定位操作。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/183208.html原文链接:https://javaforall.cn

0 人点赞