文章目录
- 一、共享内存
进程具有独立性,因此进程间通信的前提是两个进程能看到同一份资源
一、共享内存
在内存中申请一块空间,并将起始地址分别映射到两个进程的虚拟地址空间上,便可以让两个进程看到同一份资源
操作系统为了管理共享内存,除了创建共享内存外,还会创建包含共享内存信息的结构体
系统调用 shmget,头文件 sys/ipc.h、sys/shm.h
// 创建或获取共享内存
int shmget(key_t key, size_t size, int shmflg)
返回值:返回共享内存标识符 (供用户使用),出错返回 -1,并且 errno 被设置为相应的出错信息
参数:
-
key 用于创建或获取共享内存
由于进程具有独立性,为了让两个进程可以找到同一个共享内存,通信双方的进程约定,创建共享内存的进程将 key 值设置到共享内存对应的结构体中,获取共享内存的进程通过相同的 key 来查找共享内存 -
size 表示共享内存的大小
操作系统实际申请的共享内存大小为 size 向上调整到 PAGE_SIZE(4KB) 的整数倍 -
shmflag 位图,传递多个标志位时,标志位之间用 | 连接
常用的标志位:
- IPC_CREAT 表示如果 key 值对应的共享内存不存在则创建,存在则获取
- IPC_EXCL 不能单独使用,需要和 IPC_CREAT 一起传,表示如果 key 值对应的共享内存不存在则创建,存在则出错,即保证获取的共享内存一定是最新的
- 0ddd 八进制数,表示共享内存的拥有者、所属组及 other 的操作权限
系统调用 ftok,头文件 sys/types.h、sys/ipc.h,生成创建共享内存时所需要的 key 值
// key_t 类型就是 int 的 typedef
key_t ftok(const char *pathname, int proj_id)
系统调用 shmat / shmdt,头文件 sys/types.h、sys/shm.h
// 将标识符为 shmid 的共享内存与进程虚拟地址 shmaddr 关联,如果 shmaddr 指定 NULL,表示由操作系统自主决定与进程关联的虚拟地址
// shmflg 指定 0 表示以读写方式关联共享内存,指定 SHM_RDONLY 表示以读方式关联共享内存,不存在以写方式关联共享内存,此时进程必须具有共享内存相应的操作权限
// 成功返回与共享内存关联的虚拟地址,失败返回 (void*) -1
void *shmat(int shmid, const void *shmaddr, int shmflg)// 解除进程虚拟地址 shmaddr 和共享内存的关联
// 成功返回 0,失败返回 -1
int shmdt(const void *shmaddr)
共享内存的生命周期随操作系统,因此创建共享内存的进程退出后,并不会删除共享内存,需要通过系统调用或者指令删除共享内存
系统调用 shmctl,头文件 sys/ipc.h、sys/shm.h
// 对标识符为 shmid 的共享内存执行 cmd 指定的操作,buf 用来设置或者获取共享内存的属性,不需要则指定为 NULL
// cmd 指定 IPC_RMID 表示将共享内存标记为销毁,当共享内存的关联数为 0 时,共享内存会自动销毁
// 成功返回 0,失败返回 -1
int shmctl(int shmid, int cmd, struct shmid_ds *buf)
server 从共享内存中读取,client 向共享内存中写入
// shm.hpp
#ifndef __SHARED_MEMORY__
#define __SHARED_MEMORY__#include <iostream>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>#define PATHNAME "."
#define PROID 0x666
#define CREATSHM (IPC_CREAT | IPC_EXCL)
#define GETSHM IPC_CREAT
#define CREAT 0 // 创建者
#define GET 1 // 获取者const size_t gsize = 4096;namespace starrycat
{class SharedMemory{public:SharedMemory(int flag, mode_t mode, size_t size = gsize, const char *pathname = PATHNAME, int proid = PROID): _type((flag == CREATSHM) ? CREAT : GET){umask(0);// 生成唯一的 keykey_t key = ftok(pathname, proid);if (key < 0){std::cout << "key 生成失败: " << errno << " " << strerror(errno) << std::endl;exit(1);}// CREATSHM 表示用 key 值创建共享内存,GETSHM 表示通过 key 值获取共享内存_shmid = shmget(key, size, flag | mode);if (_shmid < 0){std::cout << "共享内存创建失败: " << errno << " " << strerror(errno) << std::endl;exit(2);}// 关联_shmaddr = static_cast<char*>(shmat(_shmid, nullptr, 0));if (_shmaddr == (char*)-1){std::cout << "共享内存关联失败: " << errno << " " << strerror(errno) << std::endl;exit(3);}}SharedMemory(const SharedMemory& shm) = delete;SharedMemory& operator=(const SharedMemory& shm) = delete;~SharedMemory(){// 去关联shmdt(_shmaddr);// 销毁if (_type == CREAT) shmctl(_shmid, IPC_RMID, nullptr);}char* getAddr() { return _shmaddr; }private:int _type;int _shmid;char* _shmaddr;};
}#endif// server.cc
#include "shm.hpp"
#include <iostream>
#include <unistd.h>using namespace std;int main()
{starrycat::SharedMemory shm(CREATSHM, 0666);char* start = shm.getAddr();// 通信for (int i = 0; i < 5; ++i){cout << "从共享内存中读取: " << start << endl;sleep(1);}return 0;
}// client.cc
#include "shm.hpp"
#include <iostream>
#include <unistd.h>using namespace std;int main()
{starrycat::SharedMemory shm(GETSHM, 0666);char* start = shm.getAddr();// 通信for (int i = 0; i < 3; ++i){start[i] = 'A' + i;sleep(1);}return 0;
}
先启动 myserver,在启动 myclient
- ipcs -m
功能:查看共享内存 - ipcmk -M 大小 -p 权限
功能:创建共享内存 - ipcrm -M/-m key/shmid
功能:通过 key / shmid 删除共享内存
共享内存映射到进程的虚拟地址后,进程不需要通过系统调用来读取或写入数据,因此共享内存是所有进程间通信中速度最快的