共享内存-systemV
01. 共享内存简述
共享内存是一个允许多个进程直接访问同一块物理内存区域的进程通信工具,因其本身不涉及用户态与核心态之间转换,故效率最佳。为了使用一个共享内存段,一般需要以下几个步骤:
- 调用
shmget()
创建一个新共享内存段或获取已有共享内存段的标识符。调用完成返回一个后续需要用到的共享内存标识符 - 调用
shmat()
用将shmid
(shmget
生成的)标识的共享内存段附加到调用进程的虚拟地址空间中 - 到此,程序可以将其与其他内存一样对待。
shmat()
系统调用会返回该段在虚拟地址空间的始址 - 调用
shmdt()
分离共享内存段,这样进厂将无法继续引用这段空间(可选) - 调用
shmctl()
删除共享内存段,只有一个进程执行,当所有附加该段到虚拟地址空间的进程都分离之后才能将其销毁
02. 共享内存接口
2.1 共享内存数据结构
struct shmid_ds {struct ipc_perm shm_perm; // 权限信息size_t shm_segsz; // 段大小(字节)time_t shm_atime; // 最后附加时间time_t shm_dtime; // 最后分离时间time_t shm_ctime; // 最后修改时间pid_t shm_cpid; // 创建者PIDpid_t shm_lpid; // 最后操作PIDshmatt_t shm_nattch;// 当前附加计数
};
2.2 生成键值key
#include <sys/types.h>#include <sys/ipc.h>
//将`路径名`和`项目ID`转换成与之对应的`IPC`键值key_t ftok(const char *pathname, int proj_id);
**返回值:**蔡成功返回一个key_t
值,失败返回-1
2.3 创建/获取共享内存段
#include <sys/ipc.h>#include <sys/shm.h>int shmget(key_t key, size_t size, int shmflg);
参数:
-
key:
IPC_PRIVATE
(创建新内存)- 使用
ftok()
生成的键值(用于进程间共享)
-
size
:共享内存大小>0
-
shmflg
:IPC_CREAT
(不存在时创建)IPC_EXCL
(若存在则失败)- 权限位(如
0666
)
返回值:成功返回共享内存标识符 shmid
,失败返回 -1
//part of code
key_t key = ftok("../test", 'a');int shmid = shmget(key, 1024, IPC_CREAT | 0666); // 不存在时创建 cout <<"shmid is:"<< shmid << endl;
2.4 连接共享内存
#include <sys/types.h>#include <sys/shm.h>
//将共享内存段映射到进程的地址空间void *shmat(int shmid, const void *shmaddr, int shmflg);
参数:
shmid
: 由shmget()
返回的共享内存标识符shmaddr
:请求的附加地址:NULL
(由系统自动选择地址)- 显示指定(不推荐)
shmflg
:SHM_RDONLY
(只读)SHM_REMAP
(Linux下覆盖现有映射)0
默认行为
返回值:成功返回共享内存的起始地址指针,失败返回 (void *) -1
//同一进程,没啥用 ,只是验证
char*ptr=(char*)shmat(shmid, NULL, 0);sprintf(ptr, "Hello!");cout << ptr << endl;
2.5 控制共享内存
#include <sys/ipc.h>#include <sys/shm.h>int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数:
-
shmid
:共享内存标识符 -
cmd
(可选):IPC_RMID
(标记删除,最后一个进程分离后生效)IPC_STAT
(获取状态到buf
)IPC_SET
(通过buf
设置参数)
-
buf
:用于存储或修改共享内存属性的结构体(cmd
后两个选项)
返回值:成功返回0,失败返回 -1
2.6 分离共享内存
#include <sys/types.h>#include <sys/shm.h>
//解除进程与共享内存段的映射关系int shmdt(const void *shmaddr);//0 success - failed
参数:
shmaddr
:由shmat()
返回的共享内存始址
返回值:成功返回0,失败返回 -1`
注: 分离一个共享内存段与删除它是不同的。删除是通过shmctl()选项IPC_ RMID
操作来完成的。
03. C/S简单通信
3.2 server.cpp
#include "comm.h"
int main(){key_t key = ftok(PATH_NAME, PROJ_ID);if(key < 0){perror("ftok");return 1;}int shmId = shmget(key, SIZE,IPC_CREAT | IPC_EXCL | 0666);//创建全新的shm,如果和系统已经存在ID冲突,就出错返回if(shmId < 0){perror("shmget");return 2;}printf("key->%u, shmId->%d\n", key, shmId);//sleep(1);char* mem = (char*)shmat(shmId, NULL, 0);//建立关联printf("attaches shm success\n");//sleep(15);//开始逻辑实现的部分while(1){sleep(1);printf("%s\n", mem);} shmdt(mem);//去关联printf("detaches shm success\n");shmctl(shmId, IPC_RMID, NULL);//sleep(5);printf("key->0x%u, shmId->%d shm delete success\n", key, shmId);//sleep(10);return 0;
}
3.2 client.c
#include "comm.h"
int main(){key_t key = ftok(PATH_NAME, PROJ_ID);if(key < 0){perror("ftok");return 1;}printf("%u\n",key);//client这里只需要获取即可int shmid = shmget(key, SIZE, IPC_CREAT);if(shmid < 0){perror("shmget");return 1;}char* mem = (char*)shmat(shmid, NULL, 0);//建立关联//sleep(5);printf("client process attaches success!\n");//这里是进行通信的部分char c = 'A';while(c <= 'Z'){mem[c - 'A'] = c;c++;mem[c - 'A'] = 0;sleep(2);}shmdt(mem);//移除关联//slee(5); printf("client process detaches success!\n");return 0;
}