1、常用的进程间通信方式
1、管道
2、消息队列
3、内存映射
4、信号量
5、共享内存
1. 管道 (Pipe) 和 有名管道 (FIFO)
工作机制:管道和有名管道也是基于内核的通信机制,数据在一个进程写入时进入内核缓冲区,另一个进程从内核缓冲区读取数据。因此,数据传输同样需要多次用户态与内核态的切换。
优点:管道非常简单,适合父子进程间的通信。
缺点:由于涉及内核缓存和用户态/内核态切换,效率不如共享内存高。
2. 消息队列 (Message Queue)
工作机制:消息队列是在内核中维护的一种数据结构,用于存储多个进程发送的消息。当进程发送消息时,数据会从用户态切换到内核态,内核将消息放入队列中;当进程接收消息时,内核将消息从队列中取出,再切换回用户态。整个过程需要通过系统调用来进行,因此涉及到多次用户态与内核态的切换。
优点:消息队列可以跨越多个进程,支持有序且可靠的数据传输,适合小型数据传输。
缺点:由于数据需要经过内核缓存,消息在传递过程中涉及到内核态与用户态的切换以及数据拷贝,性能相对共享内存较低。
3. 内存映射 mmap
内存映射通过将文件或设备的一部分(或全部)映射到进程的虚拟内存空间,使得进程可以像访问普通内存一样直接读取或写入该文件内容。
内存映射主要有两种类型:
文件映射(File Mapping):
将文件中的数据映射到进程的虚拟内存中,文件的内容可以像内存一样进行访问。当进程对映射区域进行读写操作时,内核负责在需要时将数据从文件加载到内存,并在需要时将修改的数据写回文件。
常用于访问大文件时提高效率。通过将文件映射到内存,避免频繁的磁盘 I/O 操作
,可以快速访问文件内容。
匿名映射(Anonymous Mapping):
匿名映射不与文件关联,通常用于为进程分配一块未初始化的内存。它通常用于共享内存的实现,比如在多个进程之间共享一块内存空间。
常用于进程间通信(IPC)和内存分配。
4. 信号 (Signals)
工作机制:信号是用于通知进程某些事件的机制。内核负责发送和处理信号,当进程接收到信号时,内核会通知用户态的进程处理对应的信号。因此,信号机制中也涉及内核态与用户态的切换,但信号通常用于简单的通知和事件处理,数据量非常小。
优点:信号机制适合用于进程间的简单通信或事件通知。
缺点:信号机制无法传递大量数据,只适合通知类的通信。
5. 套接字 (Socket)
工作机制:套接字是一种非常通用的 IPC 机制,既可以用于本地进程间通信,也可以用于网络通信。套接字的通信通常依赖于操作系统内核进行数据传递,因此涉及到内核态与用户态的切换。尤其是在本地套接字通信中,数据从一个进程传递到内核,再从内核传递给另一个进程。
优点:套接字适合复杂的网络和进程间通信,灵活性强。
缺点:由于数据经过内核,并且通常有更多的协议开销,性能不如共享内存高。
6. 共享内存 (Shared Memory)
工作机制:共享内存是通过多个进程将同一段物理内存映射到各自的用户态地址空间,进程直接通过用户态访问共享的内存区域,因此不涉及内核态与用户态的频繁切换。所有的读写操作都在用户态完成,只在创建和销毁共享内存段时涉及内核态。
优点:共享内存是最快的 IPC 机制,特别适合大量数据的高效传输。
缺点:需要进程自己管理同步问题(例如使用信号量、互斥锁),否则会出现数据竞争。
总结
管道、消息队列、内存映射、信号量相对慢速,主要源于内核态与用户态的切换和数据拷贝,而共享内存由于在通信过程中绕过了这些切换,性能更优。
内存映射更加适合频繁I/O的引用场景,共享内存更适合多线程之间进行大规模数据通信。
2、epoll使用的是mmap进行用户态和内核态的数据拷贝,为什么不用共享内存呢?
-
epoll 的设计目的
epoll 的主要目的是为高效的 I/O 事件通知提供支持。epoll 需要在内核和用户空间之间传递事件信息(如文件描述符的状态变化) -
mmap 与共享内存的区别
mmap 是一种将文件或设备的内容映射到进程地址空间的机制。它不仅可以用于文件,也可以用于将内核态的内存区域直接映射到用户态。
共享内存是两个或多个进程之间共享内存段的一种机制,通常用于大规模数据的快速传输
。共享内存段在多个进程的地址空间中可见,但共享内存本质上是用于进程间通信的,而 epoll 的核心任务并不是进程间通信,而是高效地传递内核与用户态之间的 I/O 事件信息。 -
mmap 更适合 epoll 的需求
简化数据传输、避免复杂的同步机制(共享内存需要引入复杂的同步机制,以确保多进程在访问共享内存时不会产生数据竞争冲突,而epoll 数据传递通常是单向的)