一、简介
共享内存允许两个进程访问同一块内存区域,它们使用同一个 key
值标记。
二、特点
优点:
通信方便,两个进程也是直接访问同一块内存区域,减少了数据复制的操作,速度上也有明显优势。
缺点:
没有提供同步机制,往往需要我们使用其它(例如信号)等手段实现进程的同步工作。
三、API 说明
1. 头文件
#include <sys/shm.h>
2. 创建内存共享区
int shmget(key_t key, size_t size, int shmflg);
- key : 一个非零整数,两个进程需保持一致,即两个进程之间通信的钥匙
- size : 申请的共享区的大小,单位是字节。需要是内存页大小的整数倍
- shmflg : 同
open
函数的 mode 参数,设置文件访问权限,这里额外多出一个IPC_CREAT
和IPC_EXCL
,可与 mode 进行或操作。IPC_CREAT
表示共享区不存在则创建,IPC_EXCL
和IPC_CREAT
共同使用,表示共享区已存在则返回错误。如0644 | IPC_CREAT
- 返回值 : 返回共享区域的 id 值,用于唯一识别该区域
3. 映射内存共享区
void *shmat(int shmid, const void *shmaddr, int shmflg);
at 是 attach 的意思。创建内存共享后,还不能被任何进程使用,需要使用该函数启动对共享内存的访问。
- shmid : 创建时候返回的 id 值
- shmaddr : 将共享内存映射到指定地址,可以为 NULL,此时系统将自动分配地址
- shmflg : 同 shmget 函数中的参数,通常为 0
- 返回值 : 成功执行后,返回该地址的起始地址,失败返回 -1
4. 撤销映射
int shmdt(const void *shmaddr);
dt 是 detach 的意思。用于将共享内存从该进程中分离,但并不会删除共享内存。
- shmaddr : shmat 函数返回的地址
- 返回值 : 成功返回 0,失败返回 -1,errno 将被设置为相应的值
5. 删除内存共享区
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
ctl 是 control 的意思。该函数用于查询、更新、删除共享区域。共享内存不使用后必须删除,以便回收内存。
- shmid : 创建时候返回的 id 值
- cmd : 控制命令。
IPC_STAT
状态查询IPC_SET
在权限允许下,将共享内存状态更新为 buf 中的数据IPC_RMID
删除共享内存
- 返回值 : 成功返回 0,失败返回 -1,errno 将被设置为相应的值
四、示例
1. 写端
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main() {
void *shmaddr = NULL;
const char data[] = "Hello World\n";
int shmid;
key_t key = (key_t) 666;
long page_size = sysconf(_SC_PAGESIZE);
int data_size = (strlen(data) + page_size - 1) & (~(page_size - 1));
printf("data size: %d, page size: %ld\n", data_size, page_size);
// 1. create shared memory
shmid = shmget(key, data_size, 0644 | IPC_CREAT);
if (shmid == -1) {
perror("shmget failed\n");
exit(EXIT_FAILURE);
}
// 2. attach shared memory
shmaddr = shmat(shmid, NULL, 0);
if (shmaddr == (void *)-1) {
perror("shmat failed\n");
exit(EXIT_FAILURE);
}
// 3. write data to shared memory
memset(shmaddr, 0, data_size);
memcpy(shmaddr, &data, strlen(data));
// 4. detach shared memory
if (shmdt(shmaddr) == -1) {
perror("shmdt failed\n");
exit(EXIT_FAILURE);
}
printf("write done !\n");
return 0;
}
2. 读端
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main() {
void *shmaddr = NULL;
const char data[] = "Hello World\n";
int shmid;
key_t key = (key_t) 666;
long page_size = sysconf(_SC_PAGESIZE);
int data_size = (strlen(data) + page_size - 1) & (~(page_size - 1));
printf("data size: %d, page size: %ld\n", data_size, page_size);
// 1. create shared memory
shmid = shmget(key, data_size, 0644 | IPC_CREAT);
if (shmid == -1) {
perror("shmget failed\n");
exit(EXIT_FAILURE);
}
// 2. attach shared memory
shmaddr = shmat(shmid, NULL, 0);
if (shmaddr == (void *)-1) {
perror("shmat failed\n");
exit(EXIT_FAILURE);
}
// 3. read data to shared memory
printf("read form shead memory: %s\n", (char *)shmaddr);
// 4. detach shared memory
if (shmdt(shmaddr) == -1) {
perror("shmdt failed\n");
exit(EXIT_FAILURE);
}
// 5. delete shared memory
if (shmctl(shmid, IPC_RMID, 0) == -1) {
perror("shmctl delete shared memory failed\n");
exit(EXIT_FAILURE);
}
return 0;
}
3. 执行结果
第一次执行写进程,往 shared memory 中写入了 “Hello World”,再执行读进程,读出并打印 shared memory 中的内容,并删除掉共享内存。