ToB企服应用市场:ToB评测及商务社交产业平台

标题: 【Linux】System V 共享内存 [打印本页]

作者: 大连密封材料    时间: 2024-6-27 03:25
标题: 【Linux】System V 共享内存

共享内存区是最快的 IPC 形式。一旦如许的内存映射到共享它的历程的地址空间,这些历程间数据传递不再涉及到内核,换句话说是历程不再通过执行进入内核的系统调用来传递相互的数据。
1. 共享内存表示图


2. 共享内存数据结构

  1. struct shmid_ds
  2. {
  3.     struct ipc_perm shm_perm;    /* operation perms */
  4.     int shm_segsz;               /* size of segment (bytes) */
  5.     __kernel_time_t shm_atime;   /* last attach time */
  6.     __kernel_time_t shm_dtime;   /* last detach time */
  7.     __kernel_time_t shm_ctime;   /* last change time */
  8.     __kernel_ipc_pid_t shm_cpid; /* pid of creator */
  9.     __kernel_ipc_pid_t shm_lpid; /* pid of last operator */
  10.     unsigned short shm_nattch;   /* no. of current attaches */
  11.     unsigned short shm_unused;   /* compatibility */
  12.     void *shm_unused2;           /* ditto - used by DIPC */
  13.     void *shm_unused3;           /* unused */
  14. };
复制代码
3. 共享内存函数

shmget

  1. 功能:用来创建共享内存
  2. 原型
  3.         int shmget(key_t key, size_t size, int shmflg);
  4. 参数
  5.         key:这个共享内存段名字
  6.         size:共享内存大小
  7.         shmflg:由九个权限标志构成,它们的用法和创建文件时使用的 mode 模式标志是一样的
  8. 返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回-1
复制代码
shmat

  1. 功能:将共享内存段连接到进程地址空间
  2. 原型
  3.         void* shmat(int shmid, const void* shmaddr, int shmflg);
  4. 参数
  5.         shmid:共享内存标识
  6.         shmaddr:指定连接的地址
  7.         shmflg:它的两个可能取值是 SHM_RND 和 SHM_RDONLY
  8. 返回值:成功返回一个指针,指向共享内存的地址;失败返回-1
复制代码

  1. shmaddr 为 NULL,核心自动选择一个地址。
  2. shmaddr 不为 NULL 且 shmflg 无 SHM_RND 标记,则以 shmaddr 为连接地址。
  3. shmaddr 不为 NULL 且 shmflg 设置了 SHM_RND 标记,则连接的地址会自动向下调整为 SHMLBA 的整数倍;
  4.         公式:shmaddr - (shmaddr % SHMLBA)。
  5. shmflg = SHM_RDONLY,表示连接操作用来只读共享内存。
复制代码
shmdt

  1. 功能:将共享内存段与当前进程脱离
  2. 原型
  3.         int shmdt(const void* shmaddr);
  4. 参数
  5.         shmaddr:由 shmat 所返回的指针
  6. 返回值:成功返回0;失败返回-1
  7. 注意:将共享内存段与当前进程脱离不等于删除共享内存段
复制代码
shmctl

  1. 功能:用于控制共享内存
  2. 原型
  3.         int shmctl(int shmid, int cmd, struct shmid_ds* buf);
  4. 参数
  5.         shmid:由 shmget 返回的共享内存标识码
  6.         cmd:将要采取的动作(有三个可取值)
  7.         buf:指向一个保存着共享内存的模式状态和访问权限的数据结构
  8. 返回值:成功返回0;失败返回-1
复制代码

4. 实例代码测试共享内存


代码结构
  1. $ ls
  2. Comm.hpp  Fifo.hpp  Makefile  ShmClient.cc  ShmServer.cc
  3. $ cat Makefile
  4. .PHONY:all
  5. all:shm_client shm_server
  6. shm_server:ShmServer.cc
  7.         g++ -o $@ $^ -std=c++11
  8. shm_client:ShmClient.cc
  9.         g++ -o $@ $^ -std=c++11
  10. .PHONY:clean
  11. clean:
  12.         rm -f shm_client shm_server
复制代码
Fifo.hpp
  1. #ifndef __COMM_HPP__
  2. #define __COMM_HPP__
  3. #include <iostream>
  4. #include <string>
  5. #include <cerrno>
  6. #include <cstring>
  7. #include <sys/types.h>
  8. #include <sys/stat.h>
  9. #include <unistd.h>
  10. #include <fcntl.h>
  11. #include <cassert>
  12. using namespace std;
  13. #define Mode 0666
  14. #define Path "./fifo"
  15. class Fifo
  16. {
  17. public:
  18.     Fifo(const string &path = Path)
  19.         : _path(path)
  20.     {
  21.         umask(0);
  22.         int n = mkfifo(_path.c_str(), Mode);
  23.         if (n == 0)
  24.         {
  25.             cout << "mkfifo success" << endl;
  26.         }
  27.         else
  28.         {
  29.             cerr << "mkfifo failed, errno: " << errno << ", errstring: " << strerror(errno) << endl;
  30.         }
  31.     }
  32.     ~Fifo()
  33.     {
  34.         int n = unlink(_path.c_str());
  35.         if (n == 0)
  36.         {
  37.             cout << "remove fifo file " << _path << " success" << endl;
  38.         }
  39.         else
  40.         {
  41.             cerr << "remove failed, errno: " << errno << ", errstring: " << strerror(errno) << endl;
  42.         }
  43.     }
  44. private:
  45.     string _path; // 文件路径 + 文件名
  46. };
  47. class Sync
  48. {
  49. public:
  50.     Sync()
  51.         : rfd(-1), wfd(-1)
  52.     {
  53.     }
  54.     void OpenReadOrDie()
  55.     {
  56.         rfd = open(Path, O_RDONLY);
  57.         if (rfd < 0)
  58.             exit(1);
  59.     }
  60.     void OpenWriteOrDie()
  61.     {
  62.         wfd = open(Path, O_WRONLY);
  63.         if (wfd < 0)
  64.             exit(1);
  65.     }
  66.     bool Wait()
  67.     {
  68.         bool ret = true;
  69.         uint32_t c = 0;
  70.         ssize_t n = read(rfd, &c, sizeof(uint32_t));
  71.         if (n == sizeof(uint32_t))
  72.         {
  73.             std::cout << "server wakeup, begin read shm..." << std::endl;
  74.         }
  75.         else if (n == 0)
  76.         {
  77.             ret = false;
  78.         }
  79.         else
  80.         {
  81.             return false;
  82.         }
  83.         return ret;
  84.     }
  85.     void Wakeup()
  86.     {
  87.         uint32_t c = 0;
  88.         ssize_t n = write(wfd, &c, sizeof(c));
  89.         assert(n == sizeof(uint32_t));
  90.         std::cout << "wakeup server..." << std::endl;
  91.     }
  92.     ~Sync()
  93.     {
  94.     }
  95. private:
  96.     int rfd;
  97.     int wfd;
  98. };
  99. #endif
复制代码
Comm.hpp:
  1. #pragma once
  2. #include <iostream>
  3. #include <cerrno>
  4. #include <cstring>
  5. #include <cstdlib>
  6. #include <string>
  7. #include <sys/ipc.h>
  8. #include <sys/shm.h>
  9. #include <sys/types.h>
  10. using namespace std;
  11. const char *pathname = "/home/ubuntu";
  12. const int proj_id = 0x66;
  13. // 在内核中,共享内存的大小是以4KB为基本单位的,你只能用你申请的大小。建议申请大小是N*4KB
  14. const int defaultsize = 4096; // 单位是字节
  15. std::string ToHex(key_t k)
  16. {
  17.     char buffer[1024];
  18.     snprintf(buffer, sizeof(buffer), "0x%x", k);
  19.     return buffer;
  20. }
  21. key_t GetShmKeyOrDie()
  22. {
  23.     key_t k = ftok(pathname, proj_id);
  24.     if (k < 0)
  25.     {
  26.         std::cerr << "ftok error, errno: " << errno << ", error string: " << strerror(errno) << std::endl;
  27.         exit(1);
  28.     }
  29.     return k;
  30. }
  31. int CreateShmOrDie(key_t key, int size, int flag)
  32. {
  33.     int shmid = shmget(key, size, flag);
  34.     if (shmid < 0)
  35.     {
  36.         std::cerr << "shmget error, errno: " << errno << ", error string: " << strerror(errno) << std::endl;
  37.         exit(2);
  38.     }
  39.     return shmid;
  40. }
  41. int CreateShm(key_t key, int size)
  42. {
  43.     // IPC_CREAT: 不存在就创建,存在就获取
  44.     // IPC_EXCL: 没有意义
  45.     // IPC_CREAT | IPC_EXCL: 不存在就创建,存在就出错返回
  46.     return CreateShmOrDie(key, size, IPC_CREAT | 0666);
  47. }
  48. int GetShm(key_t key, int size)
  49. {
  50.     return CreateShmOrDie(key, size, IPC_CREAT);
  51. }
  52. void DeleteShm(int shmid)
  53. {
  54.     int n = shmctl(shmid, IPC_RMID, nullptr);
  55.     if (n < 0)
  56.     {
  57.         std::cerr << "shmctl error" << std::endl;
  58.     }
  59.     else
  60.     {
  61.         std::cout << "shmctl delete shm success, shmid: " << shmid << std::endl;
  62.     }
  63. }
  64. void ShmDebug(int shmid)
  65. {
  66.     struct shmid_ds shmds;
  67.     int n = shmctl(shmid, IPC_STAT, &shmds);
  68.     if (n < 0)
  69.     {
  70.         std::cerr << "shmctl error" << std::endl;
  71.         return;
  72.     }
  73.     std::cout << "shmds.shm_segez: " << shmds.shm_segsz << std::endl;
  74.     std::cout << "shmds.shm_nattch: " << shmds.shm_nattch << std::endl;
  75.     std::cout << "shmds.shm_ctime: " << shmds.shm_ctime << std::endl;
  76.     std::cout << "shmds.shm_perm.__key: " << ToHex(shmds.shm_perm.__key) << std::endl;
  77. }
  78. void *ShmAttach(int shmid)
  79. {
  80.     // 核心自动选择一个地址
  81.     void *addr = shmat(shmid, nullptr, 0);
  82.     if ((long long int)addr == -1)
  83.     {
  84.         std::cerr << "shmat error" << std::endl;
  85.         return nullptr;
  86.     }
  87.     return addr;
  88. }
  89. void ShmDetach(void *addr)
  90. {
  91.     int n = shmdt(addr);
  92.     if (n < 0)
  93.     {
  94.         std::cerr << "shmdt error" << std::endl;
  95.     }
  96. }
复制代码
ShmServer:
  1. #include "Comm.hpp"
  2. #include "Fifo.hpp"
  3. int main()
  4. {
  5.     // 1. 获取key
  6.     key_t key = GetShmKeyOrDie();
  7.     std::cout << "key: " << ToHex(key) << std::endl;
  8.     // 2. 创建共享内存
  9.     int shmid = CreateShm(key, defaultsize);
  10.     std::cout << "shmid: " << shmid << std::endl;
  11.     // 4. 将共享内存和进程进行挂接(关联)
  12.     char *addr = (char *)ShmAttach(shmid);
  13.     std::cout << "Attach shm success, addr: " << ToHex((uint64_t)addr) << std::endl;
  14.     // 0. 先引入管道
  15.     Fifo fifo;
  16.     Sync syn;
  17.     syn.OpenReadOrDie();
  18.     // 可以进行通信了
  19.     for (;;)
  20.     {
  21.         if (!syn.Wait())
  22.             break;
  23.         std::cout << "shm content: " << addr << std::endl;
  24.     }
  25.     ShmDetach(addr);
  26.     std::cout << "Detach shm success, addr: " << ToHex((uint64_t)addr) << std::endl;
  27.     // 3. 删除共享内存
  28.     DeleteShm(shmid);
  29.     return 0;
  30. }
复制代码
ShmClient:
  1. #include "Comm.hpp"
  2. #include "Fifo.hpp"
  3. int main()
  4. {
  5.     key_t key = GetShmKeyOrDie();
  6.     std::cout << "key: " << ToHex(key) << std::endl;
  7.     int shmid = GetShm(key, defaultsize);
  8.     std::cout << "shmid: " << shmid << std::endl;
  9.     char *addr = (char *)ShmAttach(shmid);
  10.     std::cout << "Attach shm success, addr: " << ToHex((uint64_t)addr) << std::endl;
  11.     memset(addr, 0, defaultsize);
  12.     Sync syn;
  13.     syn.OpenWriteOrDie();
  14.     // 可以进行通信了
  15.     for (char c = 'A'; c <= 'Z'; c++) // shm没有使用系统调用
  16.     {
  17.         addr[c - 'A'] = c;
  18.         syn.Wakeup();
  19.         sleep(1);
  20.     }
  21.     ShmDetach(addr);
  22.     std::cout << "Detach shm success, addr: " << ToHex((uint64_t)addr) << std::endl;
  23.     sleep(5);
  24.     return 0;
  25. }
复制代码
结果演示:


5. 共享内存相关命令


  1. # 查看共享内存
  2. ipcs -m
  3. # 删除指定共享内存
  4. ipcrm -m [shmid]
复制代码
  1. # 实机演示
  2. $ ipcs -m
  3. ------ Shared Memory Segments --------
  4. key        shmid      owner      perms      bytes      nattch     status      
  5. 0x00000000 0          root       644        80         2                       
  6. 0x00000000 1          root       644        16384      2                       
  7. 0x00000000 2          root       644        280        2                       
  8. 0x6602fc97 10         ubuntu     666        4096       1
  9. $ ipcrm -m 10        # 指定shmid即可删除该共享内存资源
复制代码
留意:不是必须通过手动来删除,这里只是为了演示相关指令,删除共享内存资源是历程该做的变乱。
留意:共享内存没有历程同步与互斥!
6. System V 消息队列(了解)


7. System V 信号量(了解)



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




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4