媒介
共享内存是一种高效的进程间通信的方式,那么我们接下来解释共享内存在这个项目里面起到什么作用。
首先我们考虑这个项目的流程,第一步客户端发起通信请求,然后服务器担当请求天生通信密钥,加密后发送给客户端,这是密钥协商的流程。那么我们协商好了密钥之后,怎样让网点真正使用到这个密钥来加密通信内容呢,这时候就需要进程间通信了,从而引出了我们接下来要先容的共享内存。
大致流程是如许的,待服务器天生密钥之后,服务器将密钥写入共享内存(包括密钥id、服务器id等一些必要的信息)然后网点从共享内存读取协商好的密钥,就可以用这个密钥进行对称加密,从而满足我们的安全需求。
一、共享内存的使用流程
首先向内核申请一块指定大小的内存,这时候如果我们有两个进程(比方进程A和进程B)需要同行,可以用这块共享内存来完成。这两个进程分别和共享内存进行关联,拿到共享内存的地点(首地点)。接着两个进程就可以通过这个首地点对共享内存进行读或者写操纵。如果这个进程不再使用这块共享内存,那么需要断开关联,不能只退出进程,因为进程退出对共享内存没有任何影响。最后,如果我们不再使用共享内存,需要将共享内存烧毁。
二、共享内存的操纵函数
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);
复制代码 2.5 test
创建一块写共享内存:
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <string.h>
- #include <sys/ipc.h>
- #include <sys/shm.h>
- int main()
- {
- // 1. 创建共享内存
- int shmid = shmget(100, 4096, IPC_CREAT|0664);
- // 2. 和当前进程关联
- void* ptr = shmat(shmid, NULL, 0);
- // 3. 写共享内存
- const char* pt = "这是一块共享内存...";
- memcpy(ptr, pt, strlen(pt)+1);
- printf("按任意键继续\n");
- getchar();
- // 4. 解除关联
- shmdt(ptr);
- // 5. 删除共享内存
- shmctl(shmid, IPC_RMID, NULL);
- return 0;
- }
复制代码 创建一块读共享内存:
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <string.h>
- #include <sys/ipc.h>
- #include <sys/shm.h>
- int main()
- {
- // 1. open共享内存
- int shmid = shmget(100, 0, 0);
- // 2. 和当前进程关联
- void* ptr = shmat(shmid, NULL, 0);
- // 3. read共享内存
- printf("%s\n",(char*)ptr);
- //const char* pt = "这是一块共享内存...";
- //memcpy(ptr, pt, strlen(pt)+1);
- printf("按任意键继续\n");
- getchar();
- // 4. 解除关联
- shmdt(ptr);
- // 5. 删除共享内存
- shmctl(shmid, IPC_RMID, NULL);
- return 0;
- }
复制代码 测试结果如图:
图片上最后两个标记有点问题,就是说当key为0时,这时候时写进程退出而且这个时候标记为删除了,最后一个是排除读进程的关联。
三、在项目中的使用
项目中通过两个类实现共享内存管理:
BaseShm:基础共享内存操纵(创建、关联、释放)。
SecKeyShm:继续自 BaseShm,用于存储和查询密钥信息。
3.1 BaseShm.h
- #pragma once
- #include <iostream>
- using namespace std;
- class BaseShm
- {
- public:
- // 通过key打开共享内存
- BaseShm(int key);
- // 通过传递进来的key创建/打开共享内存
- BaseShm(int key, int size);
- // 通过路径打开共享内存
- BaseShm(string name);
- // 通过路径创建/打开共享内存
- BaseShm(string name, int size);
- void* mapShm();
- int unmapShm();
- int delShm();
- ~BaseShm();
- private:
- int getShmID(key_t key, int shmSize, int flag);
- private:
- int m_shmID;
- protected:
- void* m_shmAddr = NULL;
- };
复制代码 3.2 BaseShm.cpp
- #include "BaseShm.h"
- #include <sys/ipc.h>
- #include <sys/shm.h>
- #include <string.h>
- const char RandX = 'x';
- BaseShm::BaseShm(int key)
- {
- getShmID(key, 0, 0);
- }
- BaseShm::BaseShm(int key, int size)
- {
- getShmID(key, size, IPC_CREAT | 0664);
- }
- BaseShm::BaseShm(string name)
- {
- key_t key = ftok(name.data(), RandX);
- getShmID(key, 0, 0);
- }
- BaseShm::BaseShm(string name, int size)
- {
- key_t key = ftok(name.data(), RandX);
- // 创建共享内存
- getShmID(key, size, IPC_CREAT | 0664);
- }
- void * BaseShm::mapShm()
- {
- // 关联当前进程和共享内存
- m_shmAddr = shmat(m_shmID, NULL, 0);
- if (m_shmAddr == (void*)-1)
- {
- return NULL;
- }
- return m_shmAddr;
- }
- int BaseShm::unmapShm()
- {
- int ret = shmdt(m_shmAddr);
- return ret;
- }
- int BaseShm::delShm()
- {
- int ret = shmctl(m_shmID, IPC_RMID, NULL);
- return ret;
- }
- BaseShm::~BaseShm()
- {
- }
- int BaseShm::getShmID(key_t key, int shmSize, int flag)
- {
- cout << "share memory size: " << shmSize << endl;
- m_shmID = shmget(key, shmSize, flag);
- if (m_shmID == -1)
- {
- // 写log日志
- cout << "shmget 失败" << endl;
- }
- return m_shmID;
- }
复制代码 3.3 SecKeyShm.h
- #pragma once
- #include "BaseShm.h"
- #include <string.h>
- #include "SeckKeyNodeInfo.h"
- class SecKeyShm : public BaseShm
- {
- public:
- SecKeyShm(int key, int maxNode);
- SecKeyShm(string pathName, int maxNode);
- ~SecKeyShm();
- void shmInit();
- int shmWrite(NodeSecKeyInfo* pNodeInfo);
- NodeSecKeyInfo shmRead(string clientID, string serverID);
- private:
- int m_maxNode;
- };
复制代码 3.4 SecKeyShm.cpp
- #include "SecKeyShm.h"
- #include <string.h>
- #include <iostream>
- using namespace std;
- SecKeyShm::SecKeyShm(int key, int maxNode)
- : BaseShm(key, maxNode * sizeof(NodeSecKeyInfo))
- , m_maxNode(maxNode)
- {
- }
- SecKeyShm::SecKeyShm(string pathName, int maxNode)
- : BaseShm(pathName, maxNode * sizeof(NodeSecKeyInfo))
- , m_maxNode(maxNode)
- {
- }
- SecKeyShm::~SecKeyShm()
- {
- }
- void SecKeyShm::shmInit()
- {
- if (m_shmAddr != NULL)
- {
- memset(m_shmAddr, 0, m_maxNode * sizeof(NodeSecKeyInfo));
- }
- }
- int SecKeyShm::shmWrite(NodeSecKeyInfo * pNodeInfo)
- {
- int ret = -1;
- // 关联共享内存
- NodeSecKeyInfo* pAddr = static_cast<NodeSecKeyInfo*>(mapShm());
- if (pAddr == NULL)
- {
- return ret;
- }
- // 判断传入的网点密钥是否已经存在
- NodeSecKeyInfo *pNode = NULL;
- for (int i = 0; i < m_maxNode; i++)
- {
- // pNode依次指向每个节点的首地址
- pNode = pAddr + i;
- cout << i << endl;
- cout << "clientID 比较: " << pNode->clientID << ", " << pNodeInfo->clientID << endl;
- cout << "serverID 比较: " << pNode->serverID << ", " << pNodeInfo->serverID << endl;
- cout << endl;
- if (strcmp(pNode->clientID, pNodeInfo->clientID) == 0 &&
- strcmp(pNode->serverID, pNodeInfo->serverID) == 0)
- {
- // 如果找到了该网点秘钥已经存在, 使用新秘钥覆盖旧的值
- memcpy(pNode, pNodeInfo, sizeof(NodeSecKeyInfo));
- unmapShm();
- cout << "写数据成功: 原数据被覆盖!" << endl;
- return 0;
- }
- }
- // 若没有找到对应的信息, 找一个空节点将秘钥信息写入
- int i = 0;
- NodeSecKeyInfo tmpNodeInfo; //空结点
- for (i = 0; i < m_maxNode; i++)
- {
- pNode = pAddr + i;
- if (memcmp(&tmpNodeInfo, pNode, sizeof(NodeSecKeyInfo)) == 0)
- {
- ret = 0;
- memcpy(pNode, pNodeInfo, sizeof(NodeSecKeyInfo));
- cout << "写数据成功: 在新的节点上添加数据!" << endl;
- break;
- }
- }
- if (i == m_maxNode)
- {
- ret = -1;
- }
- unmapShm();
- return ret;
- }
- NodeSecKeyInfo SecKeyShm::shmRead(string clientID, string serverID)
- {
- int ret = 0;
- // 关联共享内存
- NodeSecKeyInfo *pAddr = NULL;
- pAddr = static_cast<NodeSecKeyInfo*>(mapShm());
- if (pAddr == NULL)
- {
- cout << "共享内存关联失败..." << endl;
- return NodeSecKeyInfo();
- }
- cout << "共享内存关联成功..." << endl;
- //遍历网点信息
- int i = 0;
- NodeSecKeyInfo info;
- NodeSecKeyInfo *pNode = NULL;
- // 通过clientID和serverID查找节点
- cout << "maxNode: " << m_maxNode << endl;
- for (i = 0; i < m_maxNode; i++)
- {
- pNode = pAddr + i;
- cout << i << endl;
- cout << "clientID 比较: " << pNode->clientID << ", " << clientID.data() << endl;
- cout << "serverID 比较: " << pNode->serverID << ", " << serverID.data() << endl;
- if (strcmp(pNode->clientID, clientID.data()) == 0 &&
- strcmp(pNode->serverID, serverID.data()) == 0)
- {
- // 找到的节点信息, 拷贝到传出参数
- info = *pNode;
- cout << "++++++++++++++" << endl;
- cout << info.clientID << " , " << info.serverID << ", "
- << info.seckeyID << ", " << info.status << ", "
- << info.seckey << endl;
- cout << "===============" << endl;
- break;
- }
- }
- unmapShm();
- return info;
- }
复制代码 这段代码重要将对称加密密钥以结构化数据(NodeSecKeyInfo)形式存入共享内存,然后通过客户端 ID和服务端 ID快速查找对应密钥,最后允许多个进程访问同一块共享内存,实现密钥数据的共享以达到进程间通信的结果。
四、一些关于共享内存的问题
- 操纵体系怎样知道一块共享内存被多少进程关联?
- 共享内存维护了一个结构体struct shmid_ds这个结构体中有一个成员shm_nattch
shm_nattch中记载了关联的进程的个数
- 是不是可以对共享内存进行多次删除(多次调用shmctl)
- 可以多次操纵,因为shmctl函数是标记删除共享内存, 不是直接删除
- 什么时候被真正删除了?
- 当关联这块共享内存进程个数 == 0 的时候, 真正被删除了(shm_nattch== 0)
- 当共享内存的key == 0的时候
- 表明共享内存被标记删除(但是并不肯定被真正删除,可能尚有进程关联这块共享内存),当key==0时, 现在还没有没有和共享内存进程关联的进程, 就不允许进行关联了,现在只为已经关联乐成的进程服务。如果被标记删除的进程都取消关联,此时才说明共享内存真正被删除。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |