文章目录
- 0. 代码仓库
- 1. 使用流程
- 案例代码:
- 2. API解析
- 2.1 创建或打开一块共享内存区
- 2.2 将当前进程和共享内存关联到一起
- 2.3 将共享内存和当前进程分离
- 2.4 共享内存操作 -( 删除共享内存 )
- 3. 思考问题
- 3. ftok函数
- 4. 共享内存API封装-以本项目为例
0. 代码仓库
https://github.com/Chufeng-Jiang/OpenSSL_Secure_Data_Transmission_Platform
1. 使用流程
- 向内核申请一块内存 -> 指定大小
- 如果有两个进程, 需要通信, 可以使用这块共享内存来完成, 先创建出这两个进程
- 进程A
- 进程B
- 进程A和进程B分别和共享内存进行关联
- 拿到共享内存的地址 -> 首地址
- 两个进程可以通过这个首地址对共享内存进行读/写操作
- 如果这个进程不再使用这块共享内存, 需要和共享内存断开关联
- 进程退出, 对共享内存是没有任何影响的
- 当不再使用共享内存的时候, 需要将共享内存销毁
案例代码:
https://github.com/Chufeng-Jiang/Linux-System-Programming/tree/main/0110%20Shared%20Memory
2. API解析
2.1 创建或打开一块共享内存区
// 创建共享内存
// 共享内存已经存在, 打开共享内存
// 可以创建多块共享内存
int shmget(key_t key, size_t size, int shmflg);
参数:
- key: 通过这个key记录共享内存在内核中的位置, 需要是一个>0的整数, ==0不行
随便指定一个数就可以, 后边会介绍一个函数ftok
- size: 创建共享内存的时候, 指定共享内存的大小
- 如果是打开一个已经存在的共享内存, size写0就可以
- shmflg: 创建共享内存的时候使用, 类似于open函数的flag
- IPC_CREAT: 创建共享内存
- 创建的时候需要给共享内存一个操作权限
- IPC_CREAT | 0664
- IPC_CREAT | IPC_EXCL: 检测共享内存是否存在
- 如果存在函数返回-1
- 不存在, 返回0
返回值:
成功: 创建/打开成功, 得到一个整形数 -> 对应这块共享内存
失败: -1
// 应用
// 1. 创建共享内存
int shmid = shmget(100, 4096, IPC_CREAT | 0664);
int shmid = shmget(200, 4096, IPC_CREAT | 0664);
// 2. 打开共享内存
int shmid = shmget(100, 0, 0);
2.2 将当前进程和共享内存关联到一起
// 进程和共享内存产生关系
void *shmat(int shmid, const void *shmaddr, int shmflg);
参数:
- shmid: 通过这个参数访问共享内存, shmget()函数的返回值
- shmaddr: 指定共享内存在内核中的位置, 写NULL -> 委托内核区指定
- shmflg: 关联成功之后对共享内存的操作权限
- SHM_RDONLY: 只读
- 0: 读写
返回值:
成功: 共享内存的地址 (起始地址)
失败: (void *) -1
// 函数调用:
void* ptr = shmat(shmid, NULL, 0);
// 写内存
memcpy(ptr, "xxxx", len);
// 读内存
printf("%s", (char*)prt);
2.3 将共享内存和当前进程分离
// 进程和共享内存分离 -> 二者就没有关系了
int shmdt(const void *shmaddr);
参数: 共享内存的起始地址, shmat()返回值
返回值:
- 成功: 0
- 失败: -1
2.4 共享内存操作 -( 删除共享内存 )
// fcntl
// setsockopt
// getsockopt
// 对共享内存进程操作
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数:
- shmid: 通过这个参数访问共享内存, shmget()函数的返回值
- cmd: 对共享内存的操作
- IPC_STAT: 获取共享内存的状态
- IPC_SET: 设置共享内存状态
- IPC_RMID: 标记共享内存要被销毁
- buf: 为第二个参数服务的
cmd==IPC_STAT: 获取共享内存具体状态信息
cmd==IPC_SET: 自定义共享内存状态, 设置到内核的共享内存中
cmd==IPC_RMID: 这个参数没有用了, 指定为NULL
返回值:
成功: 0
失败: -1
// 删除共享内存
shmctl(shmid, IPC_RMID, NULL);
3. 思考问题
-
问题1: 操作系统如何知道一块共享内存被多少进程关联?
- 共享内存维护了一个结构体
struct shmid_ds
这个结构体中有一个成员shm_nattch
shm_nattch
中记录了关联的进程的个数
- 共享内存维护了一个结构体
-
问题2: 是不是可以对共享内存进行多次删除 -> 多次调用
shmctl
- 可以多次操作
-
因为
shmctl
函数是标记删除共享内存, 部署直接删除- 什么时候被真正删除了?
- 当关联这块共享内存进程个数 == 0 的时候, 真正被删除了
shm_nattch== 0
3. ftok函数
ftok函数是IPC中常用的一个函数,它是由Unix系统提供的一个应用程序编程接口(API)。它的作用是根据一个指定的文件名和一个整数,生成一个不重复的键值(key)。
key_t ftok(const char *pathname, int proj_id);
首先根据一个任意存在的pathname绝对路径
提取其所属文件系统的特有信息,包括设备号(stat.st_dev)和inode号(stat.st_ino),其获取方法参见上述程序中的sata结构,然后再结合ftok()函数的proj_id参数,按照如下规则计算key:
key1 = stat.st_ino & 0xffff; // 保留低16位
key2 = stat.st_dev & 0xff; // 保留低8位
key2 << = 16; // 左移16位
key3 = proj_id & 0xff; // 保留低8位
key3 << = 24; // 左移24位
key = key1|key2|key3; // 三者进行或运算
本例中,pathname=’/tmp‘,ftok()函数提取的设备号为0x804,inode号为0x280001,proj_id为0x01,根据以上运算过程,key1=0x01,key2=0x40000,key3=0x1000000,三者求或得到key=0x1040001,与程序输出的结果一致。
4. 共享内存API封装-以本项目为例
//shmget
int shmget(key_t key, size_t size, int shmflg);
class BaseShm
{
public:
BaseShm(int key); // 根据key打开共享内存
BaseShm(string path); // 根据string path-> int key 打开共享内存
BaseShm(int key, int size); // 根据key创建共享内存
BaseShm(string path, int size); // 根据string path-> int key 创建共享内存
void* mapshm()
{
m_ptr = shmat(shmid);
return m_ptr;
}
int unmapshm()
{
shmdt(m_ptr);
}
int delshm()
{
shmctl(shmid);
}
private:
int m_shmid; // shmget返回值
void* m_ptr;
}