经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » C 语言 » 查看文章
C 共享内存封装
来源:cnblogs  作者:喜欢兰花山丘  时间:2019/1/7 9:47:42  对本文有异议

引言 - 背景

  2016 年写过一篇关于 linux 共享内存 shm api 扫盲文. C扩展 从共享内存shm到memcache外部内存

比较简单. 没有深入分析(能力有限, 也深入分析不了). 3年(2019)过去了. 本质而言共享内存这种编程活化石般

双刃剑, 像 "redis" 这种分布式内存数据库完全可以替代它做想做的业务(硬件过剩).  这里为什么继续鞭尸呢?

想要为这种最快的数据交互 IPC 方式, 做个多平台移植实战代码. 更加详细底层原理和面试问题, 请自行搜索.

好的疑惑和思索可以分享个博主, 共同提升.

 

前言 - shm api 介绍

  先带大家熟悉 linux 和 winds 共享内存 shm 相关的 api 介绍.  编程很简单, 无外乎学习和实践. 当然不写

代码, 狂看原理的, 当我没说. 毕竟身居高位, 指点江山, 都曾经有过梦想, 也流过无数智慧 ~

1. linux shm 相关 api 熟悉

  linux shm 有两套 api 这里还是只说 System V 标准的 4 个系统 api

  1. #include <sys/types.h>
  2. #include <sys/ipc.h>
  3. #include <sys/shm.h>
  4.  
  5. /*
  6. * System V 风格的 IPC 函数实现共享内存接口
  7. */
  8.  
  9. //
  10. // shmctl - 共享内存控制操作
  11. // __shmid : 共享内存标识符, shmget () 返回返回值
  12. // __cmd : IPC_STAT -> 得到共享内存的状态, 把共享内存的 shmid_ds 结构复制到 buf 中
  13. // : IPC_SET -> 改变共享内存的状态, 把 buf 复制到共享内存的 shmid_ds 结构内
  14. // : IPC_RMID -> 删除这片共享内存
  15. // __buf : 共享内存管理结构体
  16. // return : 0 正确, 出错 -1, 错误原因查 errno 表
  17. // : EINVAL -> 参数 size 小于 SHMMIN 或大于 SHMMAX
  18. // : EEXIST -> 预建立 key 所致的共享内存, 但已经存在
  19. // : EIDRM -> 参数 key 所致的共享内存已经删除
  20. // : ENOSPC -> 超过了系统允许建立的共享内存的最大值 SHMALL
  21. // : ENOENT -> key 所指的共享内存不存在, 参数 shmflg 也未设 IPC_CREAT 位
  22. // : EACCES -> 没有权限
  23. // : ENOMEM -> 核心内存不足
  24. //
  25. //
  26. extern int shmctl (int __shmid, int __cmd, struct shmid_ds *__buf) __THROW;
  27. //
  28. // shmget - 得到共享内存段
  29. // __key : 新建或者获取已经存在的共享内存的 key
  30. // __size : 创建共享内存的大小, 获取设置默认 0
  31. // __shmflg : 标志集合
  32. // : IPC_CREAT -> 若不存在则创建, 需要在 shmflg 中 '|权限信息' |0664 若存在则打开
  33. // : IPC_EXCL -> 与 IPC_CREAT 搭配使用, 若存在则创建失败 errno == EEXIST
  34. // : 0 -> 获取已经存在的共享内存
  35. // return : __shmid, 出错 -1
  36. //
  37. extern int shmget (key_t __key, size_t __size, int __shmflg) __THROW;
  38. //
  39. // shmat - 附加共享内存段
  40. // __shmid : 共享内存标识符, shmget () 返回返回值
  41. // __shmaddr: NULL 表示由系统选择
  42. // : 非 NULL 且 shmflg 是 SHM_RND, 会按照页对齐的原则从 shmaddr 开始
  43. // 找最近的地址开始分配, 否则 shmaddr 指定的地址必须是页对齐的
  44. // __shmflg : 0 -> 默认操作标志
  45. // : SHM_RDONLY -> 表示挂接到该共享内存的进程必须有读权限
  46. // : SHM_REMAP -> 表示如果要映射的共享内存已经有现存的内存, 那么就将旧的替换
  47. // return : 成功返回映射内存的地址, 失败返回 (void *)-1 设 errno
  48. //
  49. extern void *shmat (int __shmid, const void *__shmaddr, int __shmflg) __THROW;
  50. //
  51. // shmdt - 分离共享内存段
  52. // __shmaddr: 共享内存标识符, shmat() 返回值
  53. // return : 成功返回 0, 失败返回 -1 设 errno
  54. //
  55. extern int shmdt (const void *__shmaddr) __THROW;

对于 shm_ctl 用到的相关结构和宏截取部分如下

  1. /* Mode bits for `msgget', `semget', and `shmget'. */
  2. #define IPC_CREAT 01000 /* Create key if key does not exist. */
  3. #define IPC_EXCL 02000 /* Fail if key exists. */
  4. #define IPC_NOWAIT 04000 /* Return error on wait. */
  5.  
  6. /* Control commands for `msgctl', `semctl', and `shmctl'. */
  7. #define IPC_RMID 0 /* Remove identifier. */
  8. #define IPC_SET 1 /* Set `ipc_perm' options. */
  9. #define IPC_STAT 2 /* Get `ipc_perm' options. */
  10. #ifdef __USE_GNU
  11. # define IPC_INFO 3 /* See ipcs. */
  12. #endif
  13.  
  14. /* Special key values. */
  15. #define IPC_PRIVATE ((__key_t) 0) /* Private key. */
  16.  
  17.  
  18. /* Data structure used to pass permission information to IPC operations. */
  19. struct ipc_perm
  20. {
  21. __key_t __key; /* Key. */
  22. __uid_t uid; /* Owner's user ID. */
  23. __gid_t gid; /* Owner's group ID. */
  24. __uid_t cuid; /* Creator's user ID. */
  25. __gid_t cgid; /* Creator's group ID. */
  26. unsigned short int mode; /* Read/write permission. */
  27. unsigned short int __pad1;
  28. unsigned short int __seq; /* Sequence number. */
  29. unsigned short int __pad2;
  30. __syscall_ulong_t __glibc_reserved1;
  31. __syscall_ulong_t __glibc_reserved2;
  32. };
  33. /* Permission flag for shmget. */
  34. #define SHM_R 0400 /* or S_IRUGO from <linux/stat.h> */
  35. #define SHM_W 0200 /* or S_IWUGO from <linux/stat.h> */
  36.  
  37. /* Flags for `shmat'. */
  38. #define SHM_RDONLY 010000 /* attach read-only else read-write */
  39. #define SHM_RND 020000 /* round attach address to SHMLBA */
  40. #define SHM_REMAP 040000 /* take-over region on attach */
  41. #define SHM_EXEC 0100000 /* execution access */
  42.  
  43. /* Commands for `shmctl'. */
  44. #define SHM_LOCK 11 /* lock segment (root only) */
  45. #define SHM_UNLOCK 12 /* unlock segment (root only) */
  46. __BEGIN_DECLS
  47. /* Segment low boundary address multiple. */
  48. #define SHMLBA (__getpagesize ())
  49. extern int __getpagesize (void) __THROW __attribute__ ((__const__));
  50. /* Type to count number of attaches. */
  51. typedef __syscall_ulong_t shmatt_t;
  52. /* Data structure describing a shared memory segment. */
  53. struct shmid_ds
  54. {
  55. struct ipc_perm shm_perm; /* operation permission struct */
  56. size_t shm_segsz; /* size of segment in bytes */
  57. __time_t shm_atime; /* time of last shmat() */
  58. #ifndef __x86_64__
  59. unsigned long int __glibc_reserved1;
  60. #endif
  61. __time_t shm_dtime; /* time of last shmdt() */
  62. #ifndef __x86_64__
  63. unsigned long int __glibc_reserved2;
  64. #endif
  65. __time_t shm_ctime; /* time of last change by shmctl() */
  66. #ifndef __x86_64__
  67. unsigned long int __glibc_reserved3;
  68. #endif
  69. __pid_t shm_cpid; /* pid of creator */
  70. __pid_t shm_lpid; /* pid of last shmop */
  71. shmatt_t shm_nattch; /* number of current attaches */
  72. __syscall_ulong_t __glibc_reserved4;
  73. __syscall_ulong_t __glibc_reserved5;
  74. };

(满屏的恶意, 直接从系统 include 拔下来, 木有强迫症) 基于上面大概. 写个 demo 带大家熟悉其用法

  1. #include <stdio.h>
  2. #include <errno.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include <stdint.h>
  6. #include <sys/types.h>
  7. #include <sys/ipc.h>
  8. #include <sys/shm.h>
  9. #include <unistd.h>
  10.  
  11. // str_hash_rs - Robert Sedgwicks Hash Function
  12. unsigned str_hash_rs(const char * s) {
  13. register unsigned hash = 0;
  14. for (unsigned c, a = 63689; (c = *s); ++s) {
  15. hash = hash * a + c;
  16. a *= 378551;
  17. }
  18. return hash & 0x7FFFFFFF;
  19. }
  20. #define STR_SHM "shm_1_to_2"
  21. #define INT_SHM 4096
  22.  
  23. // shm_init - // shm_init - init 操作, -1 error, 0 init create, 1 exists
  24. int shm_init(key_t key, size_t size);
  25. //
  26. // 文件 : shm.c
  27. // 目标 :
  28. // 1' 熟悉 Linux System V shm 机制
  29. // 2' 第一次创建共享内存, 写入数据
  30. // 3' 第二次读取共享内存, 随后销毁
  31. //
  32. int main(int argc, char * argv[]) {
  33. char cmd[BUFSIZ];
  34. key_t key = (key_t)str_hash_rs(STR_SHM);
  35. printf("name = %s -> key = %d\n", STR_SHM, key);
  36. // shm init
  37. int ret = shm_init(key, INT_SHM);
  38. if (ret < 0) {
  39. perror("shm_init "STR_SHM);
  40. return EXIT_FAILURE;
  41. }
  42. if (ret == 0) {
  43. // shm 刚创建
  44. return EXIT_SUCCESS;
  45. }
  46. // ret == 1 shm 已经存在我们开始获取共享内存
  47. int shmid = shmget(key, 0, 0);
  48. if (shmid < 0) {
  49. perror("shmget");
  50. return EXIT_FAILURE;
  51. }
  52. printf("key = %d -> shmid = %d\n", key, shmid);
  53. // 开始附加共享内存段
  54. void * shmaddr = shmat(shmid, NULL, 0);
  55. if ((intptr_t)shmaddr < 0) {
  56. perror("shmat shmid "STR_SHM);
  57. return EXIT_FAILURE;
  58. }
  59. printf("shmid = %d, shmaddr = %p\n", shmid, shmaddr);
  60. snprintf(cmd, sizeof cmd, "ipcs -m -i %d", shmid);
  61. // 开始操作内存
  62. long * ptr = shmaddr;
  63. printf("ptr = %p, *ptr = %ld\n", ptr, *ptr);
  64. long old = __sync_lock_test_and_set(ptr, 1);
  65. if (old) {
  66. // 共享内存分离
  67. printf("__sync_lock_test_and_set old = %ld, *ptr = %ld\n", old, *ptr);
  68. return shmdt(shmaddr);
  69. }
  70. printf("Second run now old = %ld, *ptr = %ld\n", old, *ptr);
  71. __sync_lock_release(ptr);
  72. printf("shmid = %d, shmdt after\n> %s\n", shmid, cmd);
  73. system(cmd);
  74. // 内存分离
  75. shmdt(shmaddr);
  76. // 删除共享内存
  77. shmctl(shmid, IPC_RMID, NULL);
  78. // 最后测试共享内存状态
  79. printf("shmid = %d, shmdt after\n> %s\n", shmid, cmd);
  80. system(cmd);
  81. return EXIT_SUCCESS;
  82. }
  83. // shm_init - init 操作, -1 error, 0 init create, 1 exists
  84. int
  85. shm_init(key_t key, size_t size) {
  86. // 通过 key 创建共享内存 or 打开共享内存
  87. int shmid = shmget(key, size, IPC_CREAT|IPC_EXCL|0666);
  88. if (shmid < 0) {
  89. if (errno == EEXIST) {
  90. // 当前的共享内存已经存在
  91. return 1;
  92. }
  93. return shmid;
  94. }
  95. // create shm and shm at
  96. void * shmaddr = shmat(shmid, NULL, 0);
  97. if ((intptr_t)shmaddr < 0) {
  98. return -1;
  99. }
  100. // memset zero
  101. memset(shmaddr, 0, size);
  102. // shm dt
  103. return shmdt(shmaddr);
  104. }

代码极其适合感受和练习. 写完后相应的 api 操作就熟悉了. 练习代码的设计思路是, 跑第一次初始化共享内存.

跑第二次输出共享内存中数据. 附带说一点 linux 可以通过下面命令查看和删除共享内存

  1. ipcs -m
  2. ipcrm -m [shmid]
  3. ipcs -m | awk -F' ' 'NR!=1{print $2}'

详细展示是这样的

  1. wang@zhi:~$ ipcs -m
  2. ------------ 共享内存段 --------------
  3. shmid 拥有者 权限 字节 连接数 状态
  4. 0x00000000 753664 wang 600 524288 2 目标
  5. 0x00000000 622593 wang 600 524288 2 目标
  6. ipcrm -m [shmid]

顺带看 demo.c 编译完执行后输出结果

  1. wang@zhi:~/code/shm$ gcc -g -Wall demo.c
  2. wang@zhi:~/code/shm$ ./a.out
  3. name = shm_1_to_2 -> key = 1294752001
  4. wang@zhi:~/code/shm$ ./a.out
  5. name = shm_1_to_2 -> key = 1294752001
  6. key = 1294752001 -> shmid = 242122773
  7. shmid = 242122773, shmaddr = 0x7f3ec7fde000
  8. ptr = 0x7f3ec7fde000, *ptr = 0
  9. Second run now old = 0, *ptr = 1
  10. shmid = 242122773, shmdt after
  11. > ipcs -m -i 242122773
  12. 共享内存段 shmid=242122773
  13. uid=1000 gid=1000 cuid=1000 cgid=1000
  14. 模式=0666 访问权限=0666
  15. bytes=4096 lpid=7282 cpid=7280 nattch=1
  16. 附加时间=Wed Dec 26 21:06:33 2018
  17. 脱离时间=Wed Dec 26 21:06:33 2018
  18. 更改时间=Wed Dec 26 21:06:32 2018
  19. shmid = 242122773, shmdt after
  20. > ipcs -m -i 242122773
  21. ipcs: id 242122773 not found

2. winds shm 相关 api 熟悉

  跨平台是个悲伤的话题. 这只是小人物单纯的站队. 

  1. //
  2. // http://www.office-cn.net/t/api/createfile.htm
  3. // https://docs.microsoft.com/zh-cn/windows/desktop/api/fileapi/nf-fileapi-createfilea
  4. // CreateFile - 创建或打开一个对象的句柄
  5. // lpFileName : 创建或打开的对象的名字
  6. // dwDesiredAccess : 指明对象的控制模式. 一个应用程序可以包含读控制, 写控制, 读/写控制, 设备查询控制
  7. // dwShareMode : 指定对象的共享模式
  8. // lpSecurityAttributes : 一个指向 SECURITY_ATTRIBUTES 结构对象的指针, 决定返回的句柄是否被子进程所继承
  9. // dwCreationDisposition : 指明当打开的对象存在或不存在的时候各需怎样处理
  10. // dwFlagsAndAttributes : 指定文件属性和标志
  11. // hTemplateFile : 把具有 GENERIC_READ 权限的句柄指定为一个模板文件
  12. // return : 对象句柄
  13. //
  14. HANDLE CreateFile(
  15. LPCTSTR lpFileName,
  16. DWORD dwDesiredAccess,
  17. DWORD dwShareMode,
  18. LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  19. DWORD dwCreationDisposition,
  20. DWORD dwFlagsAndAttributes,
  21. HANDLE hTemplateFile
  22. );
  23.  
  24. //
  25. // http://www.office-cn.net/t/api/deletefile.htm
  26. // https://docs.microsoft.com/zh-cn/windows/desktop/api/fileapi/nf-fileapi-deletefilea
  27. // DeleteFileA - 删除指定文件
  28. // lpFileName : 欲删除文件的名字
  29. // return : 非零表示成功,零表示失败
  30. //
  31. BOOL DeleteFile(
  32. LPCSTR lpFileName
  33. );
  34.  
  35. //
  36. // http://www.office-cn.net/t/api/createfilemapping.htm
  37. // https://docs.microsoft.com/zh-cn/windows/desktop/api/winbase/nf-winbase-createfilemappinga
  38. // CreateFileMapping - 创建一个新的文件映射对象
  39. // hFile : 指定欲在其中创建映射的一个文件句柄. INVALID_HANDLE_VALUE 表示在内存中创建一个文件映射
  40. // lpFileMappingAttributes : 指定一个安全对象, 在创建文件映射时使用. 如果为 NULL 表示使用默认安全对象
  41. // flProtect : 指定文件映射对象的页面保护
  42. // dwMaximumSizeHigh : 文件映射的最大长度高32位
  43. // dwMaximumSizeLow : 文件映射的最大长度低32位
  44. // lpName : 指定文件映射对象的名字. 如存在这个名字的一个映射, 函数就会打开它, NULL 表示没有名字
  45. // return : 新建文件映射对象的句柄, NULL 意味着出错
  46. //
  47. HANDLE CreateFileMapping(
  48. HANDLE hFile,
  49. LPSECURITY_ATTRIBUTES lpFileMappingAttributes,
  50. DWORD flProtect,
  51. DWORD dwMaximumSizeHigh,
  52. DWORD dwMaximumSizeLow,
  53. LPCSTR lpName
  54. );
  55.  
  56. //
  57. // http://www.office-cn.net/t/api/closehandle.htm
  58. // https://msdn.microsoft.com/en-us/library/windows/desktop/ms724211(v=vs.85).aspx
  59. // CloseHandle - 关闭一个内核对象。其中包括文件、文件映射、进程、线程、安全和同步对象等
  60. // hObject : 欲关闭的一个对象的句柄
  61. // return : 非零表示成功,零表示失败
  62. //
  63. BOOL WINAPI CloseHandle(
  64. _In_ HANDLE hObject
  65. );
  66.  
  67. //
  68. // http://www.office-cn.net/t/api/index.html?mapviewoffile.htm
  69. // https://msdn.microsoft.com/en-us/library/windows/desktop/aa366761(v=vs.85).aspx
  70. // MapViewOfFile - 将一个文件映射对象映射到当前应用程序的地址空间
  71. // hFileMappingObject : 文件映射对象的句柄
  72. // dwDesiredAccess : 映射对象的文件数据的访问方式
  73. // dwFileOffsetHigh : 表示文件映射起始偏移的高32位.
  74. // dwFileOffsetLow : 表示文件映射起始偏移的低32位
  75. // dwNumberOfBytesToMap : 指定映射文件的字节数
  76. // return : 文件映射在内存中的起始地址, NULL 表示出错
  77. //
  78. LPVOID WINAPI MapViewOfFile(
  79. _In_ HANDLE hFileMappingObject,
  80. _In_ DWORD dwDesiredAccess,
  81. _In_ DWORD dwFileOffsetHigh,
  82. _In_ DWORD dwFileOffsetLow,
  83. _In_ SIZE_T dwNumberOfBytesToMap
  84. );
  85.  
  86. //
  87. // http://www.office-cn.net/t/api/unmapviewoffile.htm
  88. // https://msdn.microsoft.com/en-us/library/windows/desktop/aa366882(v=vs.85).aspx
  89. // UnmapViewOfFile - 在当前应用程序的内存地址空间解除对一个文件映射对象的映射
  90. // lpBaseAddress : 指定要解除映射的一个文件映射的基准地址, 这个地址是早先用 MapViewOfFile 函数获得的
  91. // return : 非零表示成功,零表示失败
  92. //
  93. BOOL WINAPI UnmapViewOfFile(
  94. _In_ LPCVOID lpBaseAddress
  95. );

阅读完我整理注释和URL. 就没有其它了.

 

正文 - 共享内存封装

  前戏做完, 是时候进入接口设计正式封装环节.  ??

  1. #ifndef _H_SHM
  2. #define _H_SHM
  3. #include <errno.h>
  4. #include <string.h>
  5. #include <stdlib.h>
  6.  
  7. //
  8. // shm_init - 共享内存初始化
  9. // key : 共享内存 key
  10. // size : 共享内存 size
  11. // return : -1 error, 0 init create, 1 exists
  12. //
  13. extern int shm_init(const char * key, size_t size);
  14. //
  15. // shm_at - 映射共享内存并返回操作指针
  16. // key : 共享内存 key
  17. // size : 共享内存 size
  18. // return : 返回共享内存映射后操作指针
  19. //
  20. extern void * shm_at(const char * key, size_t size);
  21. //
  22. // shm_dt - 解除映射的共享内存
  23. // ptr : shm_at() 返回的操作指针
  24. // return : void
  25. //
  26. extern void shm_dt(void * ptr);
  27. //
  28. // shm_rmid - 删除共享内存
  29. // key : 共享内存 key
  30. // return : void
  31. //
  32. extern void shm_rmid(const char * key);
  33. #endif//_H_SHM

设计思路和 System V shm api 脉络很好的保持了一致. 先看 linux 实现部分

  1. #include "shm.h"
  2. #include <sys/ipc.h>
  3. #include <sys/shm.h>
  4. #include <sys/types.h>
  5.  
  6. // str_key - Robert Sedgwicks Hash Function
  7. key_t str_key(const char * s) {
  8. register unsigned hash = 0;
  9. for (unsigned c, a = 63689; (c = *s); ++s) {
  10. hash = hash * a + c;
  11. a *= 378551;
  12. }
  13. return (key_t)(hash & 0x7FFFFFFF);
  14. }
  15. //
  16. // shm_init - 共享内存初始化
  17. // key : 共享内存 key
  18. // size : 共享内存 size
  19. // return : -1 error, 0 init create, 1 exists
  20. //
  21. int
  22. shm_init(const char * key, size_t size) {
  23. // 通过 key 创建共享内存 or 打开共享内存
  24. key_t k = str_key(key);
  25. int shmid = shmget(k, size, IPC_CREAT|IPC_EXCL|0666);
  26. if (shmid < 0) {
  27. // EEXIST 当前的共享内存已经存在
  28. return errno == EEXIST ? 1 : -1;
  29. }
  30. // create shm and shm at
  31. void * shmaddr = shmat(shmid, NULL, 0);
  32. if (shmaddr == (void *)-1)
  33. return -1;
  34. // memset zero
  35. memset(shmaddr, 0, size);
  36. // shm dt
  37. return shmdt(shmaddr);
  38. }
  39. //
  40. // shm_at - 映射共享内存并返回操作指针
  41. // key : 共享内存 key
  42. // size : 共享内存 size
  43. // return : 返回共享内存映射后操作指针
  44. //
  45. void *
  46. shm_at(const char * key, size_t size) {
  47. // 通过 key 打开共享内存
  48. key_t k = str_key(key);
  49. int shmid = shmget(k, size, 0);
  50. if (shmid < 0)
  51. return NULL;
  52. // get shm and shm at
  53. void * shmaddr = shmat(shmid, NULL, 0);
  54. if (shmaddr == (void *)-1)
  55. return NULL;
  56. return shmaddr;
  57. }
  58. //
  59. // shm_dt - 解除映射的共享内存
  60. // ptr : shm_at() 返回的操作指针
  61. // return : void
  62. //
  63. inline void
  64. shm_dt(void * ptr) {
  65. if (ptr) shmdt(ptr);
  66. }
  67. //
  68. // shm_rmid - 删除共享内存
  69. // key : 共享内存 key
  70. // return : void
  71. //
  72. void
  73. shm_rmid(const char * key) {
  74. if (key) {
  75. int shmid = shmget(str_key(key), 0, 0);
  76. if (shmid < 0)
  77. return;
  78. // 删除共享内存
  79. shmctl(shmid, IPC_RMID, NULL);
  80. }
  81. }

有了这些我们写个业务测试 server.c 和 client.c 依次 初始化->写入->读取->删除

  1. #include "shm.h"
  2. #include <stdio.h>
  3.  
  4. #define STR_SHM "server_2_client.shm"
  5. #define INT_SIZE (1024*1024*10)
  6.  
  7. //
  8. // 共享内存 创建, 数据写入
  9. //
  10. int main(int argc, char * argv[]) {
  11. // 共享内存初始化
  12. int ret = shm_init(STR_SHM, INT_SIZE);
  13. if (ret < 0) {
  14. printf("shm_init is error %s, %d, %d\n", STR_SHM, INT_SIZE, errno);
  15. exit(EXIT_FAILURE);
  16. }
  17. printf("shm_int ret = %d\n", ret);
  18. // 映射共享内存
  19. void * ptr = shm_at(STR_SHM, INT_SIZE);
  20. if (ptr == NULL) {
  21. printf("shm_at is error %s, %d, %d\n", STR_SHM, INT_SIZE, errno);
  22. exit(EXIT_FAILURE);
  23. }
  24. strcpy(ptr, "My love 我的祖国, 我的家乡, 我的祖辈 ~");
  25. // 解绑共享内存
  26. shm_dt(ptr);
  27. return EXIT_SUCCESS;
  28. }

下方是 client.c  上方是 server.c

  1. #include "shm.h"
  2. #include <stdio.h>
  3.  
  4. #define STR_SHM "server_2_client.shm"
  5. #define INT_SIZE (10485760)
  6.  
  7. //
  8. // 共享内存 创建, 数据写入
  9. //
  10. int main(int argc, char * argv[]) {
  11. // 共享内存初始化
  12. int ret = shm_init(STR_SHM, INT_SIZE);
  13. if (ret < 0) {
  14. printf("shm_init is error %s, %d, %d\n", STR_SHM, INT_SIZE, errno);
  15. exit(EXIT_FAILURE);
  16. }
  17. printf("shm_int ret = %d\n", ret);
  18. // 映射共享内存
  19. void * ptr = shm_at(STR_SHM, INT_SIZE);
  20. if (ptr == NULL) {
  21. printf("shm_at is error %s, %d, %d\n", STR_SHM, INT_SIZE, errno);
  22. exit(EXIT_FAILURE);
  23. }
  24. puts(ptr);
  25. // 解绑共享内存
  26. shm_dt(ptr);
  27. // 删除共享内存
  28. shm_rmid(STR_SHM);
  29. return EXIT_SUCCESS;
  30. }

输出的结果发人深省呀. 斗胆爱一下祖国, 我所出生黄土地(种花生山芋)和黑土地(种水稻棉花).

最后看下 winds 封装实现

  1. #include "shm.h"
  2. #include <windows.h>
  3.  
  4. //
  5. // shm_init - 共享内存初始化
  6. // key : 共享内存 key
  7. // size : 共享内存 size
  8. // return : -1 error, 0 init create, 1 exists
  9. //
  10. int
  11. shm_init(const char * key, size_t size) {
  12. HANDLE file = CreateFile(key,
  13. GENERIC_READ | GENERIC_WRITE,
  14. FILE_SHARE_READ | FILE_SHARE_WRITE,
  15. NULL,
  16. OPEN_ALWAYS,
  17. FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_HIDDEN,
  18. NULL);
  19. DWORD err = GetLastError();
  20. if (file == INVALID_HANDLE_VALUE)
  21. return err == ERROR_ALREADY_EXISTS ? 1 : -1;
  22. if (err == ERROR_ALREADY_EXISTS) {
  23. CloseHandle(file);
  24. return 1;
  25. }
  26.  
  27. // 创建一个进程共享的对象
  28. DWORD how = (DWORD)(size >> 16 >> 16), low = (DWORD)size;
  29. HANDLE map = CreateFileMapping(file,
  30. NULL,
  31. PAGE_READWRITE,
  32. how, low, key);
  33.  
  34. if (map == INVALID_HANDLE_VALUE) {
  35. CloseHandle(file);
  36. return -1;
  37. }
  38. if (GetLastError() == ERROR_ALREADY_EXISTS) {
  39. CloseHandle(map);
  40. CloseHandle(file);
  41. return 1;
  42. }
  43.  
  44. // 开始映射文件内容
  45. void * ptr = MapViewOfFile(map, FILE_MAP_ALL_ACCESS, 0, 0, size);
  46. if (ptr == NULL) {
  47. CloseHandle(map);
  48. CloseHandle(file);
  49. return -1;
  50. }
  51.  
  52. // 开始初始化内存
  53. memset(ptr, 0, size);
  54.  
  55. // 回收资源
  56. UnmapViewOfFile(ptr);
  57. CloseHandle(map);
  58. CloseHandle(file);
  59.  
  60. return 0;
  61. }
  62.  
  63. //
  64. // shm_at - 映射共享内存并返回操作指针
  65. // key : 共享内存 key
  66. // size : 共享内存 size
  67. // return : 返回共享内存映射后操作指针
  68. //
  69. void *
  70. shm_at(const char * key, size_t size) {
  71. HANDLE file = CreateFile(key,
  72. GENERIC_READ | GENERIC_WRITE,
  73. FILE_SHARE_READ | FILE_SHARE_WRITE,
  74. NULL,
  75. OPEN_EXISTING,
  76. FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_HIDDEN,
  77. NULL);
  78. if (file == INVALID_HANDLE_VALUE)
  79. return NULL;
  80.  
  81. // 打开一个进程共享的对象
  82. DWORD how = (DWORD)(size >> 16 >> 16), low = (DWORD)size;
  83. HANDLE map = CreateFileMapping(file,
  84. NULL,
  85. PAGE_READWRITE,
  86. how, low, key);
  87.  
  88. if (map == INVALID_HANDLE_VALUE) {
  89. CloseHandle(file);
  90. return NULL;
  91. }
  92.  
  93. // 开始映射文件内容
  94. void * ptr = MapViewOfFile(map, FILE_MAP_ALL_ACCESS, 0, 0, size);
  95. if (ptr == NULL) {
  96. CloseHandle(map);
  97. CloseHandle(file);
  98. return NULL;
  99. }
  100.  
  101. // 内存构造并返回结果
  102. CloseHandle(map);
  103. CloseHandle(file);
  104. return ptr;
  105. }
  106.  
  107. //
  108. // shm_dt - 解除映射的共享内存
  109. // ptr : shm_at() 返回的操作指针
  110. // return : void
  111. //
  112. inline void
  113. shm_dt(void * ptr) {
  114. if (ptr) UnmapViewOfFile(ptr);
  115. }
  116.  
  117. //
  118. // shm_rmid - 删除共享内存
  119. // key : 共享内存 key
  120. // return : void
  121. //
  122. inline void
  123. shm_rmid(const char * key) {
  124. if (key) DeleteFile(key);
  125. }

有了这些下次遇见共享内存相关业务需求代码就不用太费力了(也不会有下次 :)

 

后记 - 展望

?? 错误是难免的, 欢迎交流感悟 ~

我的中国心 - http://music.163.com/m/song?id=194881&userid=16529894

 

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号