在Linux进程间通信(IPC)中,共享内存是效率最高的方式之一。它通过让多个进程直接访问同一块物理内存区域,避免了数据拷贝的开销,适用于高吞吐量的数据交换场景。本文将详细拆解共享内存编程的核心函数(shm_open、mmap等),结合函数原型、参数解析和实战示例,帮助大家快速掌握共享内存的使用方法。
一、共享内存核心函数详解
共享内存的实现依赖一组系统调用,从创建共享内存对象、设置大小,到内存映射和释放,每个步骤都有对应的核心函数。
1. 共享内存对象的创建与删除:shm_open() & shm_unlink()
共享内存对象本质是/dev/shm目录下的临时文件,通过shm_open创建/打开,shm_unlink删除。
头文件
#include <sys/mman.h>
shm_open():创建/打开共享内存对象
int shm_open(const char *name, int oflag, mode_t mode);
-
参数说明:
-
name:共享内存对象名称,无需指定/dev/shm路径(系统自动映射)。 -
oflag:打开模式,支持多选项拼接(如O_CREAT | O_RDWR):-
O_CREAT:对象不存在时创建。 -
O_RDONLY/O_RDWR:只读/读写模式。 -
O_EXCL:与O_CREAT配合,避免覆盖已存在对象。 -
O_TRUNC:截断对象至0长度(仅O_RDWR模式有效)。
-
-
mode:创建新对象时的权限位(类似文件权限),常用0644(所有者读写、其他只读)。
-
-
返回值:成功返回文件描述符,失败返回
-1。
shm_unlink():删除共享内存对象
int shm_unlink(const char *name);
-
参数说明:
name为要删除的共享内存对象名称。 -
返回值:成功返回
0,失败返回-1。 -
注意:此函数仅删除对象的目录项,已映射的内存需通过
munmap释放。
2. 共享内存大小设置:truncate() & ftruncate()
创建共享内存对象后,需通过这两个函数设置其大小(默认大小为0,无法直接使用)。
头文件
#include <unistd.h>
#include <sys/types.h>
函数原型与差异
|
函数 |
作用 |
核心差异 |
|
|
按路径缩放文件大小 |
需指定文件路径,仅支持文件系统中的实际文件 |
|
|
按文件描述符缩放大小 |
接收文件描述符,支持 |
-
参数说明:
-
path/fd:目标文件路径或文件描述符。 -
length:目标大小(字节数)。
-
-
返回值:成功返回
0,失败返回-1。 -
特性:文件缩小则截断部分数据丢失,放大则扩展部分填充
\0,文件偏移量不变。
3. 内存映射与释放:mmap() & munmap()
通过mmap将共享内存对象映射到进程的虚拟地址空间,进程可直接读写该内存;munmap用于取消映射。
头文件
#include <sys/mman.h>
mmap():将对象映射到虚拟内存
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
-
参数说明:
-
addr:期望的映射起始地址,设为NULL让系统自动分配(推荐)。 -
length:映射内存的字节长度(需与共享内存对象大小一致)。 -
prot:内存保护标志(可组合):-
PROT_READ:允许读取。 -
PROT_WRITE:允许写入。 -
PROT_EXEC:允许执行。 -
PROT_NONE:不可访问。
-
-
flags:映射选项(核心选项二选一):-
MAP_SHARED:共享映射,修改会同步到共享内存(进程间通信必选)。 -
MAP_PRIVATE:私有映射,修改仅对当前进程有效。 -
其他常用选项:
MAP_ANONYMOUS(匿名映射,不关联文件)、MAP_FIXED(强制指定映射地址)。
-
-
fd:被映射的文件/共享内存描述符(匿名映射传-1)。 -
offset:映射起始偏移量(共享内存映射设为0)。
-
-
返回值:成功返回映射起始地址(常用
char*接收),失败返回MAP_FAILED(即(void*)-1)。
munmap():取消内存映射
int munmap(void *addr, size_t length);
-
参数说明:
-
addr:mmap返回的映射起始地址。 -
length:映射内存的字节长度(需与mmap的length一致)。
-
-
返回值:成功返回
0,失败返回-1。 -
注意:取消映射后,原地址不可再访问,否则会触发段错误。
二、共享内存实战示例:父子进程通信
下面通过一个完整示例,实现父进程写入数据、子进程读取数据的共享内存通信流程。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/wait.h>
#define SHM_NAME "/my_shared_mem" // 共享内存对象名称
#define SHM_SIZE 1024 // 共享内存大小(1KB)
int main() {
int shm_fd;
char *shm_addr;
// 1. 创建共享内存对象(读写模式,权限0644)
shm_fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0644);
if (shm_fd == -1) {
perror("shm_open failed");
exit(EXIT_FAILURE);
}
// 2. 设置共享内存大小
if (ftruncate(shm_fd, SHM_SIZE) == -1) {
perror("ftruncate failed");
shm_unlink(SHM_NAME);
exit(EXIT_FAILURE);
}
// 3. 映射共享内存到虚拟地址空间
shm_addr = (char *)mmap(NULL, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
if (shm_addr == MAP_FAILED) {
perror("mmap failed");
shm_unlink(SHM_NAME);
exit(EXIT_FAILURE);
}
// 关闭共享内存文件描述符(映射后可关闭,不影响映射关系)
close(shm_fd);
// 4. 创建子进程
pid_t pid = fork();
if (pid == -1) {
perror("fork failed");
munmap(shm_addr, SHM_SIZE);
shm_unlink(SHM_NAME);
exit(EXIT_FAILURE);
}
if (pid == 0) { // 子进程:读取共享内存数据
printf("子进程:等待父进程写入数据...\n");
sleep(2); // 等待父进程写入
// 读取共享内存内容
printf("子进程:从共享内存读取到:%s\n", shm_addr);
// 子进程取消映射
if (munmap(shm_addr, SHM_SIZE) == -1) {
perror("munmap failed (child)");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
} else { // 父进程:写入数据到共享内存
const char *msg = "Hello, 共享内存!这是父进程的消息";
strncpy(shm_addr, msg, SHM_SIZE - 1); // 留一个字节存'\0'
shm_addr[SHM_SIZE - 1] = '\0'; // 确保字符串结束符
printf("父进程:已写入数据到共享内存\n");
// 等待子进程结束
wait(NULL);
// 父进程取消映射
if (munmap(shm_addr, SHM_SIZE) == -1) {
perror("munmap failed (parent)");
exit(EXIT_FAILURE);
}
// 删除共享内存对象
if (shm_unlink(SHM_NAME) == -1) {
perror("shm_unlink failed");
exit(EXIT_FAILURE);
}
printf("父进程:共享内存已释放\n");
}
return 0;
}
编译与运行
1.编译命令(需链接rt库,部分系统要求):
![]()
2.运行结果:

三、注意事项与常见问题
-
权限问题:
shm_open的mode参数需合理设置(如0644),否则其他进程可能无法访问。 -
对象名称规范:共享内存对象名称需以
/开头(如/my_shm),否则shm_open会失败。 -
内存释放顺序:先通过
munmap取消映射,再通过shm_unlink删除对象,避免内存泄漏。 -
数据同步:共享内存无默认同步机制,多进程并发读写时需配合信号量、互斥锁等同步手段,避免数据竞争。
-
大小匹配:
mmap的length需与ftruncate设置的共享内存大小一致,否则可能出现访问越界。
四、总结
共享内存是Linux IPC中效率最高的方式,核心依赖shm_open(创建对象)、ftruncate(设置大小)、mmap(内存映射)三个关键步骤。通过本文的函数解析和实战示例,相信大家已掌握共享内存的基本使用方法。
实际开发中,共享内存常与同步机制结合使用,适用于大数据量、低延迟的进程间通信场景(如实时数据传输、多进程协作计算等)。如果在使用过程中遇到问题,可通过man 2 函数名(如man 2 mmap)查看系统手册获取更详细的说明。


1万+

被折叠的 条评论
为什么被折叠?



