Linux:进程间通信技术

一、什么是共享内存?
共享内存就像是一块大家都能直接访问的公共黑板,不同的进程可以直接在这块黑板上读写数据。和传统的进程间通信方式相比,共享内存不需要在内核和进程之间反复拷贝数据,大大减少了数据传输的开销。想象一下,传统方式是把资料从一个部门复印一份再送到另一个部门,而共享内存则是让两个部门直接看同一份资料,效率自然高得多。所以,在实时数据处理、数据库缓存、图像处理这些对数据传输速度要求极高的场景中,共享内存就成了开发者们的首选。
二、共享内存是如何工作的?
以 Linux
系统为例,常见的共享内存实现方式有System V
共享内存和POSIX
共享内存,它们各有特点,下面我们分别来看其实现步骤。
1. System V 共享内存
创建或获取共享内存段:首先,我们需要用ftok
函数生成一个唯一的 “钥匙”(键值),它根据一个已存在的文件路径和一个项目 ID 来生成,这个键值就像进入共享内存房间的钥匙。例如:
1 | #include <sys/types.h> |
有了 “钥匙” 后,就可以用shmget
函数创建或获取共享内存段。如果是创建新的,需要指定共享内存的大小和权限:
1 | #include <sys/ipc.h> |
这里1024表示共享内存大小为 1024 字节,IPC_CREAT
| 0666
表示如果不存在就创建,并且设置读写权限为 0666。
附加共享内存段:创建好共享内存后,还需要用shmat
函数把它 “连接” 到当前进程的地址空间,这样进程就能访问这块内存了:
1 | #include <sys/types.h> |
shmid
是shmget
返回的共享内存标识符,NULL表示让系统自动分配映射地址,0表示读写权限。
访问共享内存:连接成功后,就可以像操作普通内存一样,直接通过指针在共享内存里读写数据。
分离共享内存段:当进程不再需要使用共享内存时,要用shmdt
函数把它从进程地址空间中分离出来。如果想要彻底删除共享内存段,可以使用shmctl
函数,IPC_RMID
命令会标记共享内存段为删除状态,等所有进程都分离后,系统就会真正删除它。
2. POSIX 共享内存
创建或打开共享内存对象:使用shm_open
函数,通过一个名称来创建或打开共享内存对象,同时需要指定权限和文件模式:
1 | #include <fcntl.h> |
这里"/my_shared_memory"是共享内存对象的名称,O_CREAT | O_RDWR表示如果不存在则创建且以读写模式打开,0666是文件权限。
设置共享内存大小:使用ftruncate
函数设置共享内存的大小:
1 | ftruncate(shm_fd, 1024); |
这里将共享内存大小设置为 1024 字节。
映射共享内存到进程地址空间:使用mmap
函数将共享内存对象映射到进程的地址空间:
1 | void *shmaddr = mmap(0, 1024, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0); |
0表示让系统自动选择映射地址,1024是共享内存大小,PROT_READ
| PROT_WRITE
表示可读可写,MAP_SHARED
表示共享映射,shm_fd
是shm_open
返回的文件描述符,最后的0是偏移量。
访问共享内存:映射成功后,即可对共享内存进行读写操作。
解除映射与关闭共享内存对象:使用munmap
函数解除共享内存的映射,使用shm_unlink
函数删除共享内存对象名称,当所有进程都解除映射后,系统会自动释放共享内存资源:
1 | munmap(shmaddr, 1024); |
三、System V 与 POSIX 共享内存对比
对比项 | System V 共享内存 | POSIX 共享内存 |
---|---|---|
创建方式 | 通过ftok 生成键值,再用shmget 创建,依赖文件系统路径和项目 ID |
直接用shm_open 通过名称创建,类似文件操作,更直观 |
函数接口 | 函数命名风格较传统,操作步骤多,如shmget 、shmat 等多个函数配合 |
函数接口更接近文件操作函数,如shm_open 、mmap ,概念更清晰 |
生命周期管理 | 删除需显式调用shmctl 设置IPC_RMID ,且要等待所有进程分离 |
使用shm_unlink 删除名称,所有进程解除映射后自动释放,管理更简单 |
同步机制 | 本身不提供同步机制,需额外借助信号量等 | 可结合 POSIX 信号量、互斥锁等,与共享内存接口兼容性更好 |
跨平台性 | 主要在 UNIX 和 Linux 系统,跨平台性差 | 在 Linux、Mac 等多种系统广泛支持,跨平台性好 |
四、共享内存的优势与应用场景
优势
高效性:少了数据在内核和进程之间拷贝的步骤,读写速度极快,能大幅提升数据传输效率。
大数据量友好:对于需要传输大量数据的场景,共享内存完全能轻松应对,不会出现性能瓶颈。
灵活自由:作为公共缓冲区,进程可以根据自己的需求随意读写,方便进程间协作。
应用场景
实时数据处理:在股票交易、期货行情分析、传感器数据采集等实时性要求高的场景中,共享内存能快速传递数据,保证系统及时响应。
图像处理与视频处理:多个进程需要共享图像或视频数据时,共享内存能让数据共享变得高效,加快处理速度。
高性能计算:在大型模型训练、AI 计算等大规模数据共享场景下,共享内存能显著提升计算效率。
数据库系统:数据库通过共享内存可以快速交换数据,减少对硬盘的频繁访问,提升整体性能。
五、使用共享内存的注意事项
虽然共享内存很强大,但使用时也有不少 “坑” 需要注意:
数据同步与互斥:因为多个进程都能访问共享内存,就像多个人同时用一块黑板写字,如果不加以控制,就会出现数据混乱。所以需要使用信号量、互斥锁等同步机制,保证数据的一致性,避免竞态条件。
内存管理:共享内存的创建、连接、分离和删除都要妥善处理,不然容易出现内存泄漏,浪费系统资源,甚至引发程序崩溃。
六、示例代码
1. System V 共享内存示例代码
1 | #include <iostream> |
2. POSIX 共享内存示例代码
1 | #include <iostream> |