引言 - 背景
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
- #include <sys/types.h>
- #include <sys/ipc.h>
- #include <sys/shm.h>
-
- /*
- * System V 风格的 IPC 函数实现共享内存接口
- */
-
- //
- // shmctl - 共享内存控制操作
- // __shmid : 共享内存标识符, shmget () 返回返回值
- // __cmd : IPC_STAT -> 得到共享内存的状态, 把共享内存的 shmid_ds 结构复制到 buf 中
- // : IPC_SET -> 改变共享内存的状态, 把 buf 复制到共享内存的 shmid_ds 结构内
- // : IPC_RMID -> 删除这片共享内存
- // __buf : 共享内存管理结构体
- // return : 0 正确, 出错 -1, 错误原因查 errno 表
- // : EINVAL -> 参数 size 小于 SHMMIN 或大于 SHMMAX
- // : EEXIST -> 预建立 key 所致的共享内存, 但已经存在
- // : EIDRM -> 参数 key 所致的共享内存已经删除
- // : ENOSPC -> 超过了系统允许建立的共享内存的最大值 SHMALL
- // : ENOENT -> key 所指的共享内存不存在, 参数 shmflg 也未设 IPC_CREAT 位
- // : EACCES -> 没有权限
- // : ENOMEM -> 核心内存不足
- //
- //
- extern int shmctl (int __shmid, int __cmd, struct shmid_ds *__buf) __THROW;
- //
- // shmget - 得到共享内存段
- // __key : 新建或者获取已经存在的共享内存的 key
- // __size : 创建共享内存的大小, 获取设置默认 0
- // __shmflg : 标志集合
- // : IPC_CREAT -> 若不存在则创建, 需要在 shmflg 中 '|权限信息' |0664 若存在则打开
- // : IPC_EXCL -> 与 IPC_CREAT 搭配使用, 若存在则创建失败 errno == EEXIST
- // : 0 -> 获取已经存在的共享内存
- // return : __shmid, 出错 -1
- //
- extern int shmget (key_t __key, size_t __size, int __shmflg) __THROW;
- //
- // shmat - 附加共享内存段
- // __shmid : 共享内存标识符, shmget () 返回返回值
- // __shmaddr: NULL 表示由系统选择
- // : 非 NULL 且 shmflg 是 SHM_RND, 会按照页对齐的原则从 shmaddr 开始
- // 找最近的地址开始分配, 否则 shmaddr 指定的地址必须是页对齐的
- // __shmflg : 0 -> 默认操作标志
- // : SHM_RDONLY -> 表示挂接到该共享内存的进程必须有读权限
- // : SHM_REMAP -> 表示如果要映射的共享内存已经有现存的内存, 那么就将旧的替换
- // return : 成功返回映射内存的地址, 失败返回 (void *)-1 设 errno
- //
- extern void *shmat (int __shmid, const void *__shmaddr, int __shmflg) __THROW;
- //
- // shmdt - 分离共享内存段
- // __shmaddr: 共享内存标识符, shmat() 返回值
- // return : 成功返回 0, 失败返回 -1 设 errno
- //
- extern int shmdt (const void *__shmaddr) __THROW;
对于 shm_ctl 用到的相关结构和宏截取部分如下
- /* Mode bits for `msgget', `semget', and `shmget'. */
- #define IPC_CREAT 01000 /* Create key if key does not exist. */
- #define IPC_EXCL 02000 /* Fail if key exists. */
- #define IPC_NOWAIT 04000 /* Return error on wait. */
-
- /* Control commands for `msgctl', `semctl', and `shmctl'. */
- #define IPC_RMID 0 /* Remove identifier. */
- #define IPC_SET 1 /* Set `ipc_perm' options. */
- #define IPC_STAT 2 /* Get `ipc_perm' options. */
- #ifdef __USE_GNU
- # define IPC_INFO 3 /* See ipcs. */
- #endif
-
- /* Special key values. */
- #define IPC_PRIVATE ((__key_t) 0) /* Private key. */
-
-
- /* Data structure used to pass permission information to IPC operations. */
- struct ipc_perm
- {
- __key_t __key; /* Key. */
- __uid_t uid; /* Owner's user ID. */
- __gid_t gid; /* Owner's group ID. */
- __uid_t cuid; /* Creator's user ID. */
- __gid_t cgid; /* Creator's group ID. */
- unsigned short int mode; /* Read/write permission. */
- unsigned short int __pad1;
- unsigned short int __seq; /* Sequence number. */
- unsigned short int __pad2;
- __syscall_ulong_t __glibc_reserved1;
- __syscall_ulong_t __glibc_reserved2;
- };
- /* Permission flag for shmget. */
- #define SHM_R 0400 /* or S_IRUGO from <linux/stat.h> */
- #define SHM_W 0200 /* or S_IWUGO from <linux/stat.h> */
-
- /* Flags for `shmat'. */
- #define SHM_RDONLY 010000 /* attach read-only else read-write */
- #define SHM_RND 020000 /* round attach address to SHMLBA */
- #define SHM_REMAP 040000 /* take-over region on attach */
- #define SHM_EXEC 0100000 /* execution access */
-
- /* Commands for `shmctl'. */
- #define SHM_LOCK 11 /* lock segment (root only) */
- #define SHM_UNLOCK 12 /* unlock segment (root only) */
- __BEGIN_DECLS
- /* Segment low boundary address multiple. */
- #define SHMLBA (__getpagesize ())
- extern int __getpagesize (void) __THROW __attribute__ ((__const__));
- /* Type to count number of attaches. */
- typedef __syscall_ulong_t shmatt_t;
- /* Data structure describing a shared memory segment. */
- struct shmid_ds
- {
- struct ipc_perm shm_perm; /* operation permission struct */
- size_t shm_segsz; /* size of segment in bytes */
- __time_t shm_atime; /* time of last shmat() */
- #ifndef __x86_64__
- unsigned long int __glibc_reserved1;
- #endif
- __time_t shm_dtime; /* time of last shmdt() */
- #ifndef __x86_64__
- unsigned long int __glibc_reserved2;
- #endif
- __time_t shm_ctime; /* time of last change by shmctl() */
- #ifndef __x86_64__
- unsigned long int __glibc_reserved3;
- #endif
- __pid_t shm_cpid; /* pid of creator */
- __pid_t shm_lpid; /* pid of last shmop */
- shmatt_t shm_nattch; /* number of current attaches */
- __syscall_ulong_t __glibc_reserved4;
- __syscall_ulong_t __glibc_reserved5;
- };
(满屏的恶意, 直接从系统 include 拔下来, 木有强迫症) 基于上面大概. 写个 demo 带大家熟悉其用法
- #include <stdio.h>
- #include <errno.h>
- #include <stdlib.h>
- #include <string.h>
- #include <stdint.h>
- #include <sys/types.h>
- #include <sys/ipc.h>
- #include <sys/shm.h>
- #include <unistd.h>
-
- // str_hash_rs - Robert Sedgwicks Hash Function
- unsigned str_hash_rs(const char * s) {
- register unsigned hash = 0;
- for (unsigned c, a = 63689; (c = *s); ++s) {
- hash = hash * a + c;
- a *= 378551;
- }
- return hash & 0x7FFFFFFF;
- }
- #define STR_SHM "shm_1_to_2"
- #define INT_SHM 4096
-
- // shm_init - // shm_init - init 操作, -1 error, 0 init create, 1 exists
- int shm_init(key_t key, size_t size);
- //
- // 文件 : shm.c
- // 目标 :
- // 1' 熟悉 Linux System V shm 机制
- // 2' 第一次创建共享内存, 写入数据
- // 3' 第二次读取共享内存, 随后销毁
- //
- int main(int argc, char * argv[]) {
- char cmd[BUFSIZ];
- key_t key = (key_t)str_hash_rs(STR_SHM);
- printf("name = %s -> key = %d\n", STR_SHM, key);
- // shm init
- int ret = shm_init(key, INT_SHM);
- if (ret < 0) {
- perror("shm_init "STR_SHM);
- return EXIT_FAILURE;
- }
- if (ret == 0) {
- // shm 刚创建
- return EXIT_SUCCESS;
- }
- // ret == 1 shm 已经存在我们开始获取共享内存
- int shmid = shmget(key, 0, 0);
- if (shmid < 0) {
- perror("shmget");
- return EXIT_FAILURE;
- }
- printf("key = %d -> shmid = %d\n", key, shmid);
- // 开始附加共享内存段
- void * shmaddr = shmat(shmid, NULL, 0);
- if ((intptr_t)shmaddr < 0) {
- perror("shmat shmid "STR_SHM);
- return EXIT_FAILURE;
- }
- printf("shmid = %d, shmaddr = %p\n", shmid, shmaddr);
- snprintf(cmd, sizeof cmd, "ipcs -m -i %d", shmid);
- // 开始操作内存
- long * ptr = shmaddr;
- printf("ptr = %p, *ptr = %ld\n", ptr, *ptr);
- long old = __sync_lock_test_and_set(ptr, 1);
- if (old) {
- // 共享内存分离
- printf("__sync_lock_test_and_set old = %ld, *ptr = %ld\n", old, *ptr);
- return shmdt(shmaddr);
- }
- printf("Second run now old = %ld, *ptr = %ld\n", old, *ptr);
- __sync_lock_release(ptr);
- printf("shmid = %d, shmdt after\n> %s\n", shmid, cmd);
- system(cmd);
- // 内存分离
- shmdt(shmaddr);
- // 删除共享内存
- shmctl(shmid, IPC_RMID, NULL);
- // 最后测试共享内存状态
- printf("shmid = %d, shmdt after\n> %s\n", shmid, cmd);
- system(cmd);
- return EXIT_SUCCESS;
- }
- // shm_init - init 操作, -1 error, 0 init create, 1 exists
- int
- shm_init(key_t key, size_t size) {
- // 通过 key 创建共享内存 or 打开共享内存
- int shmid = shmget(key, size, IPC_CREAT|IPC_EXCL|0666);
- if (shmid < 0) {
- if (errno == EEXIST) {
- // 当前的共享内存已经存在
- return 1;
- }
- return shmid;
- }
- // create shm and shm at
- void * shmaddr = shmat(shmid, NULL, 0);
- if ((intptr_t)shmaddr < 0) {
- return -1;
- }
- // memset zero
- memset(shmaddr, 0, size);
- // shm dt
- return shmdt(shmaddr);
- }
代码极其适合感受和练习. 写完后相应的 api 操作就熟悉了. 练习代码的设计思路是, 跑第一次初始化共享内存.
跑第二次输出共享内存中数据. 附带说一点 linux 可以通过下面命令查看和删除共享内存
- ipcs -m
- ipcrm -m [shmid]
- ipcs -m | awk -F' ' 'NR!=1{print $2}'
详细展示是这样的
- wang@zhi:~$ ipcs -m
- ------------ 共享内存段 --------------
- 键 shmid 拥有者 权限 字节 连接数 状态
- 0x00000000 753664 wang 600 524288 2 目标
- 0x00000000 622593 wang 600 524288 2 目标
- ipcrm -m [shmid]
顺带看 demo.c 编译完执行后输出结果
- wang@zhi:~/code/shm$ gcc -g -Wall demo.c
- wang@zhi:~/code/shm$ ./a.out
- name = shm_1_to_2 -> key = 1294752001
- wang@zhi:~/code/shm$ ./a.out
- name = shm_1_to_2 -> key = 1294752001
- key = 1294752001 -> shmid = 242122773
- shmid = 242122773, shmaddr = 0x7f3ec7fde000
- ptr = 0x7f3ec7fde000, *ptr = 0
- Second run now old = 0, *ptr = 1
- shmid = 242122773, shmdt after
- > ipcs -m -i 242122773
- 共享内存段 shmid=242122773
- uid=1000 gid=1000 cuid=1000 cgid=1000
- 模式=0666 访问权限=0666
- bytes=4096 lpid=7282 cpid=7280 nattch=1
- 附加时间=Wed Dec 26 21:06:33 2018
- 脱离时间=Wed Dec 26 21:06:33 2018
- 更改时间=Wed Dec 26 21:06:32 2018
- shmid = 242122773, shmdt after
- > ipcs -m -i 242122773
- ipcs: id 242122773 not found
2. winds shm 相关 api 熟悉
跨平台是个悲伤的话题. 这只是小人物单纯的站队.
- //
- // http://www.office-cn.net/t/api/createfile.htm
- // https://docs.microsoft.com/zh-cn/windows/desktop/api/fileapi/nf-fileapi-createfilea
- // CreateFile - 创建或打开一个对象的句柄
- // lpFileName : 创建或打开的对象的名字
- // dwDesiredAccess : 指明对象的控制模式. 一个应用程序可以包含读控制, 写控制, 读/写控制, 设备查询控制
- // dwShareMode : 指定对象的共享模式
- // lpSecurityAttributes : 一个指向 SECURITY_ATTRIBUTES 结构对象的指针, 决定返回的句柄是否被子进程所继承
- // dwCreationDisposition : 指明当打开的对象存在或不存在的时候各需怎样处理
- // dwFlagsAndAttributes : 指定文件属性和标志
- // hTemplateFile : 把具有 GENERIC_READ 权限的句柄指定为一个模板文件
- // return : 对象句柄
- //
- HANDLE CreateFile(
- LPCTSTR lpFileName,
- DWORD dwDesiredAccess,
- DWORD dwShareMode,
- LPSECURITY_ATTRIBUTES lpSecurityAttributes,
- DWORD dwCreationDisposition,
- DWORD dwFlagsAndAttributes,
- HANDLE hTemplateFile
- );
-
- //
- // http://www.office-cn.net/t/api/deletefile.htm
- // https://docs.microsoft.com/zh-cn/windows/desktop/api/fileapi/nf-fileapi-deletefilea
- // DeleteFileA - 删除指定文件
- // lpFileName : 欲删除文件的名字
- // return : 非零表示成功,零表示失败
- //
- BOOL DeleteFile(
- LPCSTR lpFileName
- );
-
- //
- // http://www.office-cn.net/t/api/createfilemapping.htm
- // https://docs.microsoft.com/zh-cn/windows/desktop/api/winbase/nf-winbase-createfilemappinga
- // CreateFileMapping - 创建一个新的文件映射对象
- // hFile : 指定欲在其中创建映射的一个文件句柄. INVALID_HANDLE_VALUE 表示在内存中创建一个文件映射
- // lpFileMappingAttributes : 指定一个安全对象, 在创建文件映射时使用. 如果为 NULL 表示使用默认安全对象
- // flProtect : 指定文件映射对象的页面保护
- // dwMaximumSizeHigh : 文件映射的最大长度高32位
- // dwMaximumSizeLow : 文件映射的最大长度低32位
- // lpName : 指定文件映射对象的名字. 如存在这个名字的一个映射, 函数就会打开它, NULL 表示没有名字
- // return : 新建文件映射对象的句柄, NULL 意味着出错
- //
- HANDLE CreateFileMapping(
- HANDLE hFile,
- LPSECURITY_ATTRIBUTES lpFileMappingAttributes,
- DWORD flProtect,
- DWORD dwMaximumSizeHigh,
- DWORD dwMaximumSizeLow,
- LPCSTR lpName
- );
-
- //
- // http://www.office-cn.net/t/api/closehandle.htm
- // https://msdn.microsoft.com/en-us/library/windows/desktop/ms724211(v=vs.85).aspx
- // CloseHandle - 关闭一个内核对象。其中包括文件、文件映射、进程、线程、安全和同步对象等
- // hObject : 欲关闭的一个对象的句柄
- // return : 非零表示成功,零表示失败
- //
- BOOL WINAPI CloseHandle(
- _In_ HANDLE hObject
- );
-
- //
- // http://www.office-cn.net/t/api/index.html?mapviewoffile.htm
- // https://msdn.microsoft.com/en-us/library/windows/desktop/aa366761(v=vs.85).aspx
- // MapViewOfFile - 将一个文件映射对象映射到当前应用程序的地址空间
- // hFileMappingObject : 文件映射对象的句柄
- // dwDesiredAccess : 映射对象的文件数据的访问方式
- // dwFileOffsetHigh : 表示文件映射起始偏移的高32位.
- // dwFileOffsetLow : 表示文件映射起始偏移的低32位
- // dwNumberOfBytesToMap : 指定映射文件的字节数
- // return : 文件映射在内存中的起始地址, NULL 表示出错
- //
- LPVOID WINAPI MapViewOfFile(
- _In_ HANDLE hFileMappingObject,
- _In_ DWORD dwDesiredAccess,
- _In_ DWORD dwFileOffsetHigh,
- _In_ DWORD dwFileOffsetLow,
- _In_ SIZE_T dwNumberOfBytesToMap
- );
-
- //
- // http://www.office-cn.net/t/api/unmapviewoffile.htm
- // https://msdn.microsoft.com/en-us/library/windows/desktop/aa366882(v=vs.85).aspx
- // UnmapViewOfFile - 在当前应用程序的内存地址空间解除对一个文件映射对象的映射
- // lpBaseAddress : 指定要解除映射的一个文件映射的基准地址, 这个地址是早先用 MapViewOfFile 函数获得的
- // return : 非零表示成功,零表示失败
- //
- BOOL WINAPI UnmapViewOfFile(
- _In_ LPCVOID lpBaseAddress
- );
阅读完我整理注释和URL. 就没有其它了.
正文 - 共享内存封装
前戏做完, 是时候进入接口设计正式封装环节. ??
- #ifndef _H_SHM
- #define _H_SHM
- #include <errno.h>
- #include <string.h>
- #include <stdlib.h>
-
- //
- // shm_init - 共享内存初始化
- // key : 共享内存 key
- // size : 共享内存 size
- // return : -1 error, 0 init create, 1 exists
- //
- extern int shm_init(const char * key, size_t size);
- //
- // shm_at - 映射共享内存并返回操作指针
- // key : 共享内存 key
- // size : 共享内存 size
- // return : 返回共享内存映射后操作指针
- //
- extern void * shm_at(const char * key, size_t size);
- //
- // shm_dt - 解除映射的共享内存
- // ptr : shm_at() 返回的操作指针
- // return : void
- //
- extern void shm_dt(void * ptr);
- //
- // shm_rmid - 删除共享内存
- // key : 共享内存 key
- // return : void
- //
- extern void shm_rmid(const char * key);
- #endif//_H_SHM
设计思路和 System V shm api 脉络很好的保持了一致. 先看 linux 实现部分
- #include "shm.h"
- #include <sys/ipc.h>
- #include <sys/shm.h>
- #include <sys/types.h>
-
- // str_key - Robert Sedgwicks Hash Function
- key_t str_key(const char * s) {
- register unsigned hash = 0;
- for (unsigned c, a = 63689; (c = *s); ++s) {
- hash = hash * a + c;
- a *= 378551;
- }
- return (key_t)(hash & 0x7FFFFFFF);
- }
- //
- // shm_init - 共享内存初始化
- // key : 共享内存 key
- // size : 共享内存 size
- // return : -1 error, 0 init create, 1 exists
- //
- int
- shm_init(const char * key, size_t size) {
- // 通过 key 创建共享内存 or 打开共享内存
- key_t k = str_key(key);
- int shmid = shmget(k, size, IPC_CREAT|IPC_EXCL|0666);
- if (shmid < 0) {
- // EEXIST 当前的共享内存已经存在
- return errno == EEXIST ? 1 : -1;
- }
- // create shm and shm at
- void * shmaddr = shmat(shmid, NULL, 0);
- if (shmaddr == (void *)-1)
- return -1;
- // memset zero
- memset(shmaddr, 0, size);
- // shm dt
- return shmdt(shmaddr);
- }
- //
- // shm_at - 映射共享内存并返回操作指针
- // key : 共享内存 key
- // size : 共享内存 size
- // return : 返回共享内存映射后操作指针
- //
- void *
- shm_at(const char * key, size_t size) {
- // 通过 key 打开共享内存
- key_t k = str_key(key);
- int shmid = shmget(k, size, 0);
- if (shmid < 0)
- return NULL;
- // get shm and shm at
- void * shmaddr = shmat(shmid, NULL, 0);
- if (shmaddr == (void *)-1)
- return NULL;
- return shmaddr;
- }
- //
- // shm_dt - 解除映射的共享内存
- // ptr : shm_at() 返回的操作指针
- // return : void
- //
- inline void
- shm_dt(void * ptr) {
- if (ptr) shmdt(ptr);
- }
- //
- // shm_rmid - 删除共享内存
- // key : 共享内存 key
- // return : void
- //
- void
- shm_rmid(const char * key) {
- if (key) {
- int shmid = shmget(str_key(key), 0, 0);
- if (shmid < 0)
- return;
- // 删除共享内存
- shmctl(shmid, IPC_RMID, NULL);
- }
- }
有了这些我们写个业务测试 server.c 和 client.c 依次 初始化->写入->读取->删除
- #include "shm.h"
- #include <stdio.h>
-
- #define STR_SHM "server_2_client.shm"
- #define INT_SIZE (1024*1024*10)
-
- //
- // 共享内存 创建, 数据写入
- //
- int main(int argc, char * argv[]) {
- // 共享内存初始化
- int ret = shm_init(STR_SHM, INT_SIZE);
- if (ret < 0) {
- printf("shm_init is error %s, %d, %d\n", STR_SHM, INT_SIZE, errno);
- exit(EXIT_FAILURE);
- }
- printf("shm_int ret = %d\n", ret);
- // 映射共享内存
- void * ptr = shm_at(STR_SHM, INT_SIZE);
- if (ptr == NULL) {
- printf("shm_at is error %s, %d, %d\n", STR_SHM, INT_SIZE, errno);
- exit(EXIT_FAILURE);
- }
- strcpy(ptr, "My love 我的祖国, 我的家乡, 我的祖辈 ~");
- // 解绑共享内存
- shm_dt(ptr);
- return EXIT_SUCCESS;
- }
下方是 client.c 上方是 server.c
- #include "shm.h"
- #include <stdio.h>
-
- #define STR_SHM "server_2_client.shm"
- #define INT_SIZE (10485760)
-
- //
- // 共享内存 创建, 数据写入
- //
- int main(int argc, char * argv[]) {
- // 共享内存初始化
- int ret = shm_init(STR_SHM, INT_SIZE);
- if (ret < 0) {
- printf("shm_init is error %s, %d, %d\n", STR_SHM, INT_SIZE, errno);
- exit(EXIT_FAILURE);
- }
- printf("shm_int ret = %d\n", ret);
- // 映射共享内存
- void * ptr = shm_at(STR_SHM, INT_SIZE);
- if (ptr == NULL) {
- printf("shm_at is error %s, %d, %d\n", STR_SHM, INT_SIZE, errno);
- exit(EXIT_FAILURE);
- }
- puts(ptr);
- // 解绑共享内存
- shm_dt(ptr);
- // 删除共享内存
- shm_rmid(STR_SHM);
- return EXIT_SUCCESS;
- }
输出的结果发人深省呀. 斗胆爱一下祖国, 我所出生黄土地(种花生山芋)和黑土地(种水稻棉花).
最后看下 winds 封装实现
- #include "shm.h"
- #include <windows.h>
-
- //
- // shm_init - 共享内存初始化
- // key : 共享内存 key
- // size : 共享内存 size
- // return : -1 error, 0 init create, 1 exists
- //
- int
- shm_init(const char * key, size_t size) {
- HANDLE file = CreateFile(key,
- GENERIC_READ | GENERIC_WRITE,
- FILE_SHARE_READ | FILE_SHARE_WRITE,
- NULL,
- OPEN_ALWAYS,
- FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_HIDDEN,
- NULL);
- DWORD err = GetLastError();
- if (file == INVALID_HANDLE_VALUE)
- return err == ERROR_ALREADY_EXISTS ? 1 : -1;
- if (err == ERROR_ALREADY_EXISTS) {
- CloseHandle(file);
- return 1;
- }
-
- // 创建一个进程共享的对象
- DWORD how = (DWORD)(size >> 16 >> 16), low = (DWORD)size;
- HANDLE map = CreateFileMapping(file,
- NULL,
- PAGE_READWRITE,
- how, low, key);
-
- if (map == INVALID_HANDLE_VALUE) {
- CloseHandle(file);
- return -1;
- }
- if (GetLastError() == ERROR_ALREADY_EXISTS) {
- CloseHandle(map);
- CloseHandle(file);
- return 1;
- }
-
- // 开始映射文件内容
- void * ptr = MapViewOfFile(map, FILE_MAP_ALL_ACCESS, 0, 0, size);
- if (ptr == NULL) {
- CloseHandle(map);
- CloseHandle(file);
- return -1;
- }
-
- // 开始初始化内存
- memset(ptr, 0, size);
-
- // 回收资源
- UnmapViewOfFile(ptr);
- CloseHandle(map);
- CloseHandle(file);
-
- return 0;
- }
-
- //
- // shm_at - 映射共享内存并返回操作指针
- // key : 共享内存 key
- // size : 共享内存 size
- // return : 返回共享内存映射后操作指针
- //
- void *
- shm_at(const char * key, size_t size) {
- HANDLE file = CreateFile(key,
- GENERIC_READ | GENERIC_WRITE,
- FILE_SHARE_READ | FILE_SHARE_WRITE,
- NULL,
- OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_HIDDEN,
- NULL);
- if (file == INVALID_HANDLE_VALUE)
- return NULL;
-
- // 打开一个进程共享的对象
- DWORD how = (DWORD)(size >> 16 >> 16), low = (DWORD)size;
- HANDLE map = CreateFileMapping(file,
- NULL,
- PAGE_READWRITE,
- how, low, key);
-
- if (map == INVALID_HANDLE_VALUE) {
- CloseHandle(file);
- return NULL;
- }
-
- // 开始映射文件内容
- void * ptr = MapViewOfFile(map, FILE_MAP_ALL_ACCESS, 0, 0, size);
- if (ptr == NULL) {
- CloseHandle(map);
- CloseHandle(file);
- return NULL;
- }
-
- // 内存构造并返回结果
- CloseHandle(map);
- CloseHandle(file);
- return ptr;
- }
-
- //
- // shm_dt - 解除映射的共享内存
- // ptr : shm_at() 返回的操作指针
- // return : void
- //
- inline void
- shm_dt(void * ptr) {
- if (ptr) UnmapViewOfFile(ptr);
- }
-
- //
- // shm_rmid - 删除共享内存
- // key : 共享内存 key
- // return : void
- //
- inline void
- shm_rmid(const char * key) {
- if (key) DeleteFile(key);
- }
有了这些下次遇见共享内存相关业务需求代码就不用太费力了(也不会有下次 :)
后记 - 展望
?? 错误是难免的, 欢迎交流感悟 ~
我的中国心 - http://music.163.com/m/song?id=194881&userid=16529894
