银行安全传输平台(四)共享内存实现

打印 上一主题 下一主题

主题 1976|帖子 1976|积分 5928


媒介

共享内存是一种高效的进程间通信的方式,那么我们接下来解释共享内存在这个项目里面起到什么作用。
首先我们考虑这个项目的流程,第一步客户端发起通信请求,然后服务器担当请求天生通信密钥,加密后发送给客户端,这是密钥协商的流程。那么我们协商好了密钥之后,怎样让网点真正使用到这个密钥来加密通信内容呢,这时候就需要进程间通信了,从而引出了我们接下来要先容的共享内存。
大致流程是如许的,待服务器天生密钥之后,服务器将密钥写入共享内存(包括密钥id、服务器id等一些必要的信息)然后网点从共享内存读取协商好的密钥,就可以用这个密钥进行对称加密,从而满足我们的安全需求。
一、共享内存的使用流程

首先向内核申请一块指定大小的内存,这时候如果我们有两个进程(比方进程A和进程B)需要同行,可以用这块共享内存来完成。这两个进程分别和共享内存进行关联,拿到共享内存的地点(首地点)。接着两个进程就可以通过这个首地点对共享内存进行读或者写操纵。如果这个进程不再使用这块共享内存,那么需要断开关联,不能只退出进程,因为进程退出对共享内存没有任何影响。最后,如果我们不再使用共享内存,需要将共享内存烧毁。
二、共享内存的操纵函数

2.1 创建或打开一块共享内存区

  1. // 创建共享内存
  2. // 共享内存已经存在, 打开共享内存
  3. // 可以创建多块共享内存
  4. int shmget(key_t key, size_t size, int shmflg);
  5.         参数:
  6.                 - key: 通过这个key记录共享内存在内核中的位置, 需要是一个>0的整数, ==0不行
  7.                         随便指定一个数就可以, 后边会介绍一个函数ftok
  8.                 - size: 创建共享内存的时候, 指定共享内存的大小
  9.                         - 如果是打开一个已经存在的共享内存, size写0就可以
  10.                 - shmflg: 创建共享内存的时候使用, 类似于open函数的flag
  11.                         - IPC_CREAT: 创建共享内存
  12.                                 - 创建的时候需要给共享内存一个操作权限
  13.                                         - IPC_CREAT | 0664
  14.                         - IPC_CREAT | IPC_EXCL: 检测共享内存是否存在
  15.                                 - 如果存在函数返回-1
  16.                 - 不存在, 返回0
  17.         返回值:
  18.                 成功: 创建/打开成功, 得到一个整形数 -> 对应这块共享内存
  19.                 失败: -1
  20. // 应用
  21. // 1. 创建共享内存
  22. int shmid = shmget(100, 4096,  IPC_CREAT | 0664);
  23. int shmid = shmget(200, 4096,  IPC_CREAT | 0664);
  24. // 2. 打开共享内存
  25. int shmid = shmget(100, 0, 0);
复制代码
2.2 将当进步程和共享内存关联到一起

  1. // 进程和共享内存产生关系
  2. void *shmat(int shmid, const void *shmaddr, int shmflg);
  3.         参数:
  4.                 - shmid: 通过这个参数访问共享内存, shmget()函数的返回值
  5.                 - shmaddr: 指定共享内存在内核中的位置, 写NULL -> 委托内核区指定
  6.                 - shmflg: 关联成功之后对共享内存的操作权限
  7.                         - SHM_RDONLY: 只读
  8.                         - 0: 读写
  9.         返回值:
  10.                 成功: 共享内存的地址 (起始地址)
  11.         失败:  (void *) -1
  12. // 函数调用:
  13. void* ptr = shmat(shmid, NULL, 0);
  14. // 写内存
  15. memcpy(ptr, "xxxx", len);
  16. // 读内存
  17. printf("%s", (char*)prt);
复制代码
2.3 将共享内存和当进步程分离

  1. // 进程和共享内存分离 -> 二者就没有关系了
  2. int shmdt(const void *shmaddr);
  3.         参数: 共享内存的起始地址, shmat()返回值
  4.         返回值:
  5.                 - 成功: 0
  6.         - 失败: -1
复制代码
2.4 共享内存操纵 -( 删除共享内存 )

  1. // fcntl
  2. // setsockopt
  3. // getsockopt
  4. // 对共享内存进程操作
  5. int shmctl(int shmid, int cmd, struct shmid_ds *buf);
  6.         参数:
  7.                 - shmid: 通过这个参数访问共享内存, shmget()函数的返回值
  8.                 - cmd: 对共享内存的操作
  9.                         - IPC_STAT: 获取共享内存的状态
  10.                         - IPC_SET: 设置共享内存状态
  11.                         - IPC_RMID: 标记共享内存要被销毁
  12.                 - buf: 为第二个参数服务的
  13.                         cmd==IPC_STAT: 获取共享内存具体状态信息
  14.                         cmd==IPC_SET: 自定义共享内存状态, 设置到内核的共享内存中
  15.                         cmd==IPC_RMID: 这个参数没有用了, 指定为NULL
  16.         返回值:
  17.                 成功: 0
  18.         失败: -1
  19. // 删除共享内存
  20. shmctl(shmid, IPC_RMID, NULL);
复制代码
2.5 test

创建一块写共享内存:
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <string.h>
  5. #include <sys/ipc.h>
  6. #include <sys/shm.h>
  7. int main()
  8. {
  9.     // 1. 创建共享内存
  10.     int shmid = shmget(100, 4096, IPC_CREAT|0664);
  11.     // 2. 和当前进程关联
  12.     void* ptr = shmat(shmid, NULL, 0);
  13.     // 3. 写共享内存
  14.     const char* pt = "这是一块共享内存...";
  15.     memcpy(ptr, pt, strlen(pt)+1);
  16.     printf("按任意键继续\n");
  17.     getchar();
  18.     // 4. 解除关联
  19.     shmdt(ptr);
  20.     // 5. 删除共享内存
  21.     shmctl(shmid, IPC_RMID, NULL);
  22.     return 0;
  23. }
复制代码
创建一块读共享内存:
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <string.h>
  5. #include <sys/ipc.h>
  6. #include <sys/shm.h>
  7. int main()
  8. {
  9.     // 1. open共享内存
  10.     int shmid = shmget(100, 0, 0);
  11.     // 2. 和当前进程关联
  12.     void* ptr = shmat(shmid, NULL, 0);
  13.     // 3. read共享内存
  14.     printf("%s\n",(char*)ptr);
  15.     //const char* pt = "这是一块共享内存...";
  16.     //memcpy(ptr, pt, strlen(pt)+1);
  17.     printf("按任意键继续\n");
  18.     getchar();
  19.     // 4. 解除关联
  20.     shmdt(ptr);
  21.     // 5. 删除共享内存
  22.     shmctl(shmid, IPC_RMID, NULL);
  23.     return 0;
  24. }
复制代码
测试结果如图:

图片上最后两个标记有点问题,就是说当key为0时,这时候时写进程退出而且这个时候标记为删除了,最后一个是排除读进程的关联。
三、在项目中的使用

项目中通过两个类实现共享内存管理:
BaseShm:基础共享内存操纵(创建、关联、释放)。
SecKeyShm:继续自 BaseShm,用于存储和查询密钥信息。
3.1 BaseShm.h

  1. #pragma once
  2. #include <iostream>
  3. using namespace std;
  4. class BaseShm
  5. {
  6. public:
  7.         // 通过key打开共享内存
  8.         BaseShm(int key);
  9.         // 通过传递进来的key创建/打开共享内存
  10.         BaseShm(int key, int size);
  11.         // 通过路径打开共享内存
  12.         BaseShm(string name);
  13.         // 通过路径创建/打开共享内存
  14.         BaseShm(string name, int size);
  15.         void* mapShm();
  16.         int unmapShm();
  17.         int delShm();
  18.         ~BaseShm();
  19. private:
  20.         int getShmID(key_t key, int shmSize, int flag);
  21. private:
  22.         int m_shmID;
  23. protected:
  24.         void* m_shmAddr = NULL;
  25. };
复制代码
3.2 BaseShm.cpp

  1. #include "BaseShm.h"
  2. #include <sys/ipc.h>
  3. #include <sys/shm.h>
  4. #include <string.h>
  5. const char RandX = 'x';
  6. BaseShm::BaseShm(int key)
  7. {
  8.         getShmID(key, 0, 0);
  9. }
  10. BaseShm::BaseShm(int key, int size)
  11. {
  12.         getShmID(key, size, IPC_CREAT | 0664);
  13. }
  14. BaseShm::BaseShm(string name)
  15. {
  16.         key_t key = ftok(name.data(), RandX);
  17.         getShmID(key, 0, 0);
  18. }
  19. BaseShm::BaseShm(string name, int size)
  20. {
  21.         key_t key = ftok(name.data(), RandX);
  22.         // 创建共享内存
  23.         getShmID(key, size, IPC_CREAT | 0664);
  24. }
  25. void * BaseShm::mapShm()
  26. {
  27.         // 关联当前进程和共享内存
  28.         m_shmAddr = shmat(m_shmID, NULL, 0);
  29.         if (m_shmAddr == (void*)-1)
  30.         {
  31.                 return NULL;
  32.         }
  33.         return m_shmAddr;
  34. }
  35. int BaseShm::unmapShm()
  36. {
  37.         int ret = shmdt(m_shmAddr);
  38.         return ret;
  39. }
  40. int BaseShm::delShm()
  41. {
  42.         int ret = shmctl(m_shmID, IPC_RMID, NULL);
  43.         return ret;
  44. }
  45. BaseShm::~BaseShm()
  46. {
  47. }
  48. int BaseShm::getShmID(key_t key, int shmSize, int flag)
  49. {
  50.         cout << "share memory size: " << shmSize << endl;
  51.         m_shmID = shmget(key, shmSize, flag);
  52.         if (m_shmID == -1)
  53.         {
  54.                 // 写log日志
  55.                 cout << "shmget 失败" << endl;
  56.         }
  57.         return m_shmID;
  58. }
复制代码
3.3 SecKeyShm.h

  1. #pragma once
  2. #include "BaseShm.h"
  3. #include <string.h>
  4. #include "SeckKeyNodeInfo.h"
  5. class SecKeyShm : public BaseShm
  6. {
  7. public:
  8.         SecKeyShm(int key, int maxNode);
  9.         SecKeyShm(string pathName, int maxNode);
  10.         ~SecKeyShm();
  11.         void shmInit();
  12.         int shmWrite(NodeSecKeyInfo* pNodeInfo);
  13.         NodeSecKeyInfo shmRead(string clientID, string serverID);
  14. private:
  15.         int m_maxNode;
  16. };
复制代码
3.4 SecKeyShm.cpp

  1. #include "SecKeyShm.h"
  2. #include <string.h>
  3. #include <iostream>
  4. using namespace std;
  5. SecKeyShm::SecKeyShm(int key, int maxNode)
  6.         : BaseShm(key, maxNode * sizeof(NodeSecKeyInfo))
  7.         , m_maxNode(maxNode)
  8. {
  9. }
  10. SecKeyShm::SecKeyShm(string pathName, int maxNode)
  11.         : BaseShm(pathName, maxNode * sizeof(NodeSecKeyInfo))
  12.         , m_maxNode(maxNode)
  13. {
  14. }
  15. SecKeyShm::~SecKeyShm()
  16. {
  17. }
  18. void SecKeyShm::shmInit()
  19. {
  20.         if (m_shmAddr != NULL)
  21.         {
  22.                 memset(m_shmAddr, 0, m_maxNode * sizeof(NodeSecKeyInfo));
  23.         }
  24. }
  25. int SecKeyShm::shmWrite(NodeSecKeyInfo * pNodeInfo)
  26. {
  27.         int ret = -1;
  28.         // 关联共享内存
  29.         NodeSecKeyInfo* pAddr = static_cast<NodeSecKeyInfo*>(mapShm());
  30.         if (pAddr == NULL)
  31.         {
  32.                 return ret;
  33.         }
  34.         // 判断传入的网点密钥是否已经存在
  35.         NodeSecKeyInfo        *pNode = NULL;
  36.         for (int i = 0; i < m_maxNode; i++)
  37.         {
  38.                 // pNode依次指向每个节点的首地址
  39.                 pNode = pAddr + i;
  40.                 cout << i << endl;
  41.                 cout << "clientID 比较: " << pNode->clientID << ", " << pNodeInfo->clientID << endl;
  42.                 cout << "serverID 比较: " << pNode->serverID << ", " << pNodeInfo->serverID << endl;
  43.                 cout << endl;
  44.                 if (strcmp(pNode->clientID, pNodeInfo->clientID) == 0 &&
  45.                         strcmp(pNode->serverID, pNodeInfo->serverID) == 0)
  46.                 {
  47.                         // 如果找到了该网点秘钥已经存在, 使用新秘钥覆盖旧的值
  48.                         memcpy(pNode, pNodeInfo, sizeof(NodeSecKeyInfo));
  49.                         unmapShm();
  50.                         cout << "写数据成功: 原数据被覆盖!" << endl;
  51.                         return 0;
  52.                 }
  53.         }
  54.         // 若没有找到对应的信息, 找一个空节点将秘钥信息写入
  55.         int i = 0;
  56.         NodeSecKeyInfo tmpNodeInfo; //空结点
  57.         for (i = 0; i < m_maxNode; i++)
  58.         {
  59.                 pNode = pAddr + i;
  60.                 if (memcmp(&tmpNodeInfo, pNode, sizeof(NodeSecKeyInfo)) == 0)
  61.                 {
  62.                         ret = 0;
  63.                         memcpy(pNode, pNodeInfo, sizeof(NodeSecKeyInfo));
  64.                         cout << "写数据成功: 在新的节点上添加数据!" << endl;
  65.                         break;
  66.                 }
  67.         }
  68.         if (i == m_maxNode)
  69.         {
  70.                 ret = -1;
  71.         }
  72.         unmapShm();
  73.         return ret;
  74. }
  75. NodeSecKeyInfo SecKeyShm::shmRead(string clientID, string serverID)
  76. {
  77.         int ret = 0;
  78.         // 关联共享内存
  79.         NodeSecKeyInfo *pAddr = NULL;
  80.         pAddr = static_cast<NodeSecKeyInfo*>(mapShm());
  81.         if (pAddr == NULL)
  82.         {
  83.                 cout << "共享内存关联失败..." << endl;
  84.                 return NodeSecKeyInfo();
  85.         }
  86.         cout << "共享内存关联成功..." << endl;
  87.         //遍历网点信息
  88.         int i = 0;
  89.         NodeSecKeyInfo info;
  90.         NodeSecKeyInfo        *pNode = NULL;
  91.         // 通过clientID和serverID查找节点
  92.         cout << "maxNode: " << m_maxNode << endl;
  93.         for (i = 0; i < m_maxNode; i++)
  94.         {
  95.                 pNode = pAddr + i;
  96.                 cout << i << endl;
  97.                 cout << "clientID 比较: " << pNode->clientID << ", " << clientID.data() << endl;
  98.                 cout << "serverID 比较: " << pNode->serverID << ", " << serverID.data() << endl;
  99.                 if (strcmp(pNode->clientID, clientID.data()) == 0 &&
  100.                         strcmp(pNode->serverID, serverID.data()) == 0)
  101.                 {
  102.                         // 找到的节点信息, 拷贝到传出参数
  103.                         info = *pNode;
  104.                         cout << "++++++++++++++" << endl;
  105.                         cout << info.clientID << " , " << info.serverID << ", "
  106.                                 << info.seckeyID << ", " << info.status << ", "
  107.                                 << info.seckey << endl;
  108.                         cout << "===============" << endl;
  109.                         break;
  110.                 }
  111.         }
  112.         unmapShm();
  113.         return info;
  114. }
复制代码
这段代码重要将对称加密密钥以结构化数据(NodeSecKeyInfo)形式存入共享内存,然后通过客户端 ID和服务端 ID快速查找对应密钥,最后允许多个进程访问同一块共享内存,实现密钥数据的共享以达到进程间通信的结果。
四、一些关于共享内存的问题



  • 操纵体系怎样知道一块共享内存被多少进程关联?

    • 共享内存维护了一个结构体struct shmid_ds这个结构体中有一个成员shm_nattch
      shm_nattch中记载了关联的进程的个数

  • 是不是可以对共享内存进行多次删除(多次调用shmctl)

    • 可以多次操纵,因为shmctl函数是标记删除共享内存, 不是直接删除

  • 什么时候被真正删除了?

    • 当关联这块共享内存进程个数 == 0 的时候, 真正被删除了(shm_nattch== 0)

  • 当共享内存的key == 0的时候

    • 表明共享内存被标记删除(但是并不肯定被真正删除,可能尚有进程关联这块共享内存),当key==0时, 现在还没有没有和共享内存进程关联的进程, 就不允许进行关联了,现在只为已经关联乐成的进程服务。如果被标记删除的进程都取消关联,此时才说明共享内存真正被删除。


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

立山

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表