设为首页 收藏本站
查看: 899|回复: 0

[经验分享] Windows环境下共享内存通信

[复制链接]
累计签到:1 天
连续签到:1 天
发表于 2015-5-25 08:42:58 | 显示全部楼层 |阅读模式
                       

一、引言

在Windows程序中,各个进程之间常常需要交换数据,进行数据通讯。WIN32API提供了许多函数使我们能够方便高效的进行进程间的通讯,通过这些函数我们可以控制不同进程间的数据交换。
进程间通讯(即:同机通讯)和数据交换有多种方式:消息、共享内存、匿名(命名)管道、邮槽、Windows套接字等多种技术。“共享内存”(shared memory)可以定义为对一个以上的进程是可见的内存或存在于多个进程的虚拟地址空间。例如:如果两个进程使用相同的DLL,只把DLL的代码页装入内存一次,其他所有映射这个DLL的进程只要共享这些代码页就可以了;利用消息机制实现IPC虽然有交换的数据量小、携带的信息少等缺点,但由于其实现方便、应用灵活而广泛应用于无须大量、频繁数据交换的内部进程通讯系统之中。
二、共享内存的介绍
1、共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝。
2、为了在多个进程间交换信息,内核专门留出了一块内存区,可以由需要访问的进程将其映射到自己的私有地址空间。进程就可以直接读写这一块内存而不需要进行数据的拷贝,从而大大提高效率。
3、由于多个进程共享一段内存,因此也需要依靠某种同步机制。
共享内存的特点:
001NbB2vgy6SwIfHsymda&690.jpg

三、同机共享存的实现
采用内存映射文件实现WIN32进程间的通讯:Windows中的内存映射文件的机制为我们高效地操作文件提供了一种途径,它允许我们在WIN32进程中保留一段内存区域,把硬盘或页文件上的目标文件映射到这段虚拟内存中。注意:在程序实现中必须考虑各进程之间的同步问题。
具体实现如下:
1、服务器创建一个共享内存。
在服务器端进程中调用内存映射API函数CreateFileMapping创建一个有名字标识的共享内存。
与虚拟内存类似,保护方式参数可以是PAGE_READONLY或是PAGE_READWRITE。如果多进程都对同一共享内存进行写访问,则必须保持相互间同步。映射文件还可以指定PAGE_WRITECOPY标志,可以保证其原始数据不会遭到破坏,同时允许其他进程在必要时自由的操作数据的拷贝。
例:创建一个名为"liuker"的长度为4096字节的有名映射文件:
HANDLE hmap =CreateFileMappingA(INVALID_HANDLE_VALUE, NULL,
      PAGE_READWRITE | SEC_COMMIT, 0, 0x1000, "liuker");
2、服务器映射到当前进程的地址空间。
创建文件映射对象后,服务器端进程调用MapViewOfFile函数映射到本进程的地址空间内。
例:映射缓存区视图:
LPVOID lpdata = MapViewOfFile(hmap, FILE_MAP_READ |FILE_MAP_WRITE, 0, 0, 0);
3、客户端打指定的文件映射
例如:
HANDLE hmapfile = OpenFileMappingA(FILE_MAP_READ,FALSE, "liuker");
4、客户端将一个文件映射对象映射到当前应用程序的地址空间。
例如:映射缓存区视图:
LPVOID lpbase = MapViewOfFile(hmapfile,FILE_MAP_READ, 0, 0, 0);
5、UnmapViewOfFile撤消地址空间内的视图。
当用户进程结束使用共享内存后,调用UnmapViewOfFile函数以取消其地址空间内的视图。
6、关闭映射。
四、使用文件映射实现共享内存。
FileMapping用于将存在于磁盘的文件放进一个进程的虚拟地址空间,并在该进程的虚拟地址空间中产生一个区域用于“存放”该文件,这个空间就叫做File View(存放在进程的虚拟内存中),系统并同时产生一个File MappingObject(存放于物理内存中)用于维持这种映射关系,这样当多个进程需要读写那个文件的数据时,它们的File View其实对应的都是同一个File MappingObject,这样做可节省内存和保持数据的同步性,并达到数据共享的目的。
当然在一个应用向文件中写入数据时,其它进程不应该去读取这个正在写入的数据。这就需要进行一些同步的操作。下边来看一下具体的API。
CreateFileMaping的用法:
HANDLECreateFileMapping( //返回FileMapping Object的句柄
HANDLEhFile, //想要产生映射的文件的句柄
LPSECURITY_ATTRIBUTES lpAttributes, //安全属性(只对NT和2000生效)
DWORDflProtect, //保护标致
DWORDdwMaximumSizeHigh, //在DWORD的高位中存放
File MappingObject //的大小
DWORDdwMaximumSizeLow, //在DWORD的低位中存放
File MappingObject,//的大小(通常这两个参数有一个为0)
LPCTSTRlpName //File Mapping Object的名称。
);
1)物理文件句柄
任何可以获得的物理文件句柄,如果你需要创建一个物理文件无关的内存映射也无妨,将它设置成为0xFFFFFFFF(INVALID_HANDLE_VALUE)就可以了。
如果需要和物理文件关联,要确保你的物理文件创建的时候的访问模式和"保护设置"匹配,比如:物理文件只读,内存映射需要读写就会发生错误。推荐你的物理文件使用独占方式创建。
如果使用INVALID_HANDLE_VALUE,也需要设置需要申请的内存空间的大小,无论物理文件句柄参数是否有效,这样CreateFileMapping就可以创建一个和物理文件大小无关的内存空间给你,甚至超过实际文件大小,如果你的物理文件有效,而大小参数为0,则返回给你的是一个和物理文件大小一样的内存空间地址范围。返回给你的文件映射地址空间是可以通过复制,集成或者命名得到,初始内容为0。
2)保护设置
就是安全设置,不过一般设置NULL就可以了,使用默认的安全配置。 在win2k下如果需要进行限制,这是针对那些将内存文件映射共享给整个网络上面的应用进程使用是,可以考虑进行限制。
3)高位文件大小
32位地址空间,设置为0。
4)共享内存名称
命名可以包含"Global"或者"Local"前缀在全局或者会话名空间初级文件映射。其他部分可以包含任何除了()以外的字符,可以参考KernelObject Name Spaces。
5)调用CreateFileMapping的时候GetLastError的对应错误
ERROR_FILE_INVALID如果企图创建一个零长度的文件映射,应有此报
ERROR_INVALID_HANDLE如果发现你的命名内存空间和现有的内存映射,互斥量,信号量,临界区同名就麻烦了
ERROR_ALREADY_EXISTS表示内存空间命名已经存在
使用函数CreateFileMapping创建一个想共享的文件数据句柄,然后使用MapViewOfFile来获取共享的内存地址,然后使用OpenFileMapping函数在另一个进程里打开共享文件的名称,这样就可以实现不同的进程共享数据。

附录:示例
  这个程序包括一个客户端和一个服务端,服务端创建共享内存,客户端打开共享内存,两者通过两个事件互斥访问共享内存,实现一个小功能,就是服务端进程从控制台读入数据发送给客户端进程。
服务器端:
#include
#include
#include
#define FileMapping_NAME "liuker"
#define  FILESIZE 4096
LPVOID lpdata = NULL;//指针标识首地址
void main()
{
    if(lpdata != NULL)
   {
      puts("共享内存存在!\n");
   }
   //创建一个有名字标识的共享内存。
   HANDLE hmap = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL,PAGE_READWRITE | SEC_COMMIT, 0, FILESIZE,FileMapping_NAME);
    if(hmap == NULL)
   {
      puts("创建内存共享失败!\n");
   }
   else
   {
      //映射文件到指针
      lpdata = MapViewOfFile(hmap, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0,0);
      if (lpdata == NULL)
      {
          printf("服务器映射失败!\n");
      }
      else
      {
          int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
          memcpy(lpdata, a, 40);
      }
   }
   system("pause");
   UnmapViewOfFile(lpdata);//解除
   CloseHandle(hmap);
   system("pause");
}
客户端:
#include
#include
#include
#define FileMapping_NAME "liuker"
void main()
{
   //打开一个指定的文件映射对象,获得共享内存对象的句柄
   HANDLE hmapfile = OpenFileMappingA(FILE_MAP_READ, FALSE,FileMapping_NAME);
    if(hmapfile == NULL)
   {
      printf("打开文件映射对象失败!\n");
   }
   else
   {
      //将一个文件映射对象映射到当前应用程序的地址空间。
      LPVOID lpbase = MapViewOfFile(hmapfile, FILE_MAP_READ, 0, 0,0);
      if (lpbase == NULL)
      {
          printf("映射失败!\n");
      }
      else
      {
          int *p = lpbase;
          for (int i = 0; i < 10; i++)
          {
             printf("%d\n", p);
          }
      }
      UnmapViewOfFile(lpbase);//解除映射
      CloseHandle(hmapfile);//一个指定的文件映射对象
      system("pause");
   }
}
附录:函数说明
1、CreateFileMapping函数:
HANDLE CreateFileMapping(
  HANDLE hFile,
  LPSECURITY_ATTRIBUTESlpFileMappingAttributes,
  DWORD flProtect,
  DWORDdwMaximumSizeHigh,
  DWORDdwMaximumSizeLow,
  LPCTSTR lpName
);
参数
(1)hFile
映射文件的句柄,若设为0xFFFFFFFF(即:INVALID_HANDLE_VALUE)则创建一个进程间共享的对象
(2)lpFileMappingAttributes
SECURITY_ATTRIBUTES,它指明返回的句柄是否可以被子进程所继承,指定一个安全对象,在创建文件映射时使用。如果为NULL(用ByVal As Long传递零),表示使用默认安全对象。
(3)flProtect
保护文件视图。
a.PAGE_READONLY 以只读方式打开映射
b.PAGE_READWRITE 以可读、可写方式打开映射
c.PAGE_WRITECOPY 为写操作留下备份
可组合使用下述一个或多个常数:
a.SEC_COMMIT 为文件映射一个小节中的所有页分配内存
b.SEC_IMAGE 文件是个可执行文件
c.SEC_RESERVE 为没有分配实际内存的一个小节保留虚拟内存空间
(4)dwMaximumSizeHigh
文件映射的最大长度的高32位。
(5)dwMaximumSizeLow
文件映射的最大长度的低32位。如这个参数和dwMaximumSizeHigh都是零,就用磁盘文件的实际长度。
(6)lpName
映射文件名,即共享内存的名称。
返回值
返回的句柄,如果函数是成功的文件映射对象;否则,返回NULL。为了获取更多的错误信息,调用GetLastError。
如果对象存在的函数调用之前,该函数返回的句柄,其目前的大小,而不是指定的大小现有的对象,GetLastError返回ERROR_ALREADY_EXISTS。
如果dwMaximumSizeLow设置为零,GetLastError返回ERROR_INVALID_PARAMETER。
2MapViewOfFile函数:
LPVOID MapViewOfFile(
  HANDLEhFileMappingObject,
  DWORDdwDesiredAccess,
  DWORDdwFileOffsetHigh,
  DWORDdwFileOffsetLow,
  DWORDdwNumberOfBytesToMap
);
? 参数
(1)hFileMappingObject
CreateFileMapping函数返回一个文件映射对象的打开的句柄。
(2)dwDesiredAccess
映射对象的文件数据的访问方式,与CreateFileMapping()函数所设置的保护属性相匹配。
a.FILE_MAP_WRITE 映射可读可写。文件映射对象必须通过PAGE_READWRITE访问创建。
B.FILE_MAP_READ 映射只读。文件映射对象必须通过PAGE_READ 或 PAGE_READWRITE访问创建。
C.FILE_MAP_ALL_ACCESS 与FILE_MAP_WRITE相同。
d.FILE_MAP_COPY 映射时保留写操作的副本。文件映射对象必须用PAGE_WRITECOPY访问在win95下创建。
(3)dwFileOffsetHigh
文件中映射起点的高32位地址
(4)dwFileOffsetLow
文件中映射起点的低32位地址
(5)dwNumberOfBytesToMap
文件中要映射的字节数。用零映射整个文件映射对象。
返回值
映射视图的起始地址表示成功;否则,返回NULL。
3OpenFileMapping函数:
HANDLE WINAPI OpenFileMapping(
  _In_  DWORDdwDesiredAccess,
  _In_  BOOLbInheritHandle,
  _In_  LPCTSTRlpName
);
参数
(1)dwDesiredAccess
访问文件映射对象的安全描述。FILE_MAP_ALL_ACCESS、FILE_MAP_EXECUTE、FILE_MAP_READ、FILE_MAP_WRITE
(2)bInheritHandle
如果这个参数为TRUE,通过CreateProcess函数创建一个进程可以继承句柄;否则,手柄不能被继承。
(3)lpName
文件映射对象的名称。可以有一个"Global\" or"Local\"前缀全局或会话名称空间中明确打开一个对象
返回值
如果函数成功,返回值是打开的句柄到指定的文件映射对象。
如果函数失败,返回值为NULL。
                                                                       

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-70266-1-1.html 上篇帖子: Active Directory 活动目录之操作主机转移 下篇帖子: Active Directory 活动目录之域控制器的卸载(降域) Windows 通信
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表