linux共享内存解析
1. 共享内存的基本概念
共享内存是一种允许不同进程直接访问同一块内存区域的机制。与其他IPC机制(如管道、消息队列)相比,共享内存具有以下优点:
- 高效性:无需进行数据拷贝,数据可以直接在内存中共享,减少了系统调用的开销。
- 灵活性:多个进程可以同时读写共享内存,适用于需要频繁数据交换的场景。
2. 共享内存的实现方式
在Linux中,共享内存主要通过以下两种方式实现:
2.1 System V 共享内存
System V 共享内存是最早的共享内存实现方式,主要通过以下系统调用进行操作:
shmget:创建或获取一个共享内存段。
- 参数:
key
:共享内存的键值,唯一标识一个共享内存段size
:共享内存大小(需为系统页大小的整数倍)shmflg
:标志位(如IPC_CREAT
、IPC_EXCL
、权限掩码)
- 返回值:成功返回共享内存 ID,失败返回 - 1
shmat:将共享内存段附加到进程的地址空间。
- 参数:
shmid
:共享内存 ID(由 shmget 返回)shmaddr
:指定映射地址(通常设为 NULL,由系统自动分配)shmflg
:标志位(如SHM_RDONLY
)
- 返回值:成功返回映射的虚拟地址,失败返回 - 1
shmdt:将共享内存段从进程的地址空间分离。
- 参数:
shmaddr
为 shmat 返回的映射地址 - 返回值:成功返回 0,失败返回 - 1
shmctl:控制共享内存段,如删除、设置权限等。
- 参数:
cmd
:操作命令(如IPC_RMID
删除共享内存段)buf
:存储共享内存元数据的结构体
- 返回值:成功返回 0,失败返回 - 1
例子:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>#define SHM_SIZE 1024 // 共享内存大小
#define KEY 0x1234 // 共享内存键值// 共享内存结构体
typedef struct {int data[10]; // 数据缓冲区int count; // 数据计数int read_idx; // 读索引int write_idx; // 写索引
} SharedData;int main(int argc, char *argv[]) {int shmid;SharedData *shm_ptr;// 创建共享内存段shmid = shmget(KEY, sizeof(SharedData), IPC_CREAT | 0666);if (shmid == -1) {perror("shmget failed");exit(1);}// 映射共享内存到当前进程地址空间shm_ptr = (SharedData *)shmat(shmid, NULL, 0);if (shm_ptr == (void *)-1) {perror("shmat failed");exit(1);}// 初始化共享数据if (argc > 1 && strcmp(argv[1], "producer") == 0) {printf("Producer started\n");for (int i = 0; i < 5; i++) {// 等待缓冲区有空间(实际应使用信号量同步)while (shm_ptr->count >= 10) sleep(1);// 写入数据shm_ptr->data[shm_ptr->write_idx] = i * 10;shm_ptr->write_idx = (shm_ptr->write_idx + 1) % 10;shm_ptr->count++;printf("Produced: %d\n", shm_ptr->data[shm_ptr->write_idx - 1]);sleep(1);}} else if (argc > 1 && strcmp(argv[1], "consumer") == 0) {printf("Consumer started\n");for (int i = 0; i < 5; i++) {// 等待缓冲区有数据(实际应使用信号量同步)while (shm_ptr->count <= 0) sleep(1);// 读取数据int data = shm_ptr->data[shm_ptr->read_idx];shm_ptr->read_idx = (shm_ptr->read_idx + 1) % 10;shm_ptr->count--;printf("Consumed: %d\n", data);sleep(1);}}// 解除映射(生产者/消费者都需要执行)if (shmdt(shm_ptr) == -1) {perror("shmdt failed");exit(1);}// 生产者删除共享内存段if (argc > 1 && strcmp(argv[1], "producer") == 0) {if (shmctl(shmid, IPC_RMID, NULL) == -1) {perror("shmctl failed");exit(1);}printf("Shared memory deleted\n");}return 0;
}
3、共享内存使用注意事项
-
同步机制
- 共享内存不提供任何同步机制,需配合信号量、互斥锁等使用(如上例应添加信号量控制读写)
- 典型组合:
共享内存 + 信号量
实现高效同步通信
-
内存映射与权限
- 映射后需确保进程有读写权限(通过 shmget 的权限掩码控制)
- 进程退出时会自动解除映射,但共享内存段需显式删除
-
数据一致性
- 多进程同时修改数据可能导致竞争条件,需通过同步原语保证原子性
- 建议将共享数据封装为结构体,避免碎片化访问
-
系统限制
- 共享内存大小受限于
/proc/sys/kernel/shmmax
(默认通常为 32MB) - 可通过
sysctl -w kernel.shmmax=...
动态调整,或修改/etc/sysctl.conf
永久生效
- 共享内存大小受限于