zoukankan      html  css  js  c++  java
  • Windows进程间通信--共享内存映射文件(FileMapping)--VS2012下发送和接收

    之前以为两个互不相关的程序a.exe b.exe通信就只能通过网络,人家说可以通过发消息,我还深以为不然,对此,我表示万分惭愧。

    之前课本上说的进程间通信,有共享内存、管道等之类的,但没有自己操刀写过程序的原理真心理解不了。

    进程间通信的方法有很多,使用的条件也不太一样,有些必须同时在本机使用,有些可以远程,希望接下来的时间可以一个一个尝试,并弄懂。

    言归正传,下面用共享映射文件的方式实现进程间通信,代码可以运行。但只是自己的学习过程,无过多实用价值,不足之处,请指正。

    //-----------------------------------------------------------------------------------------------------------------------------------

     1.浅理解

    每个进程有自己独立的空间,一个进程无法访问其他进程的数据。就好像两个是互不干涉的个体,想让它们进行通信(交换数据),就必须有一段它们都可以访问到的空间,作为中间介质。

    在计算机中,可以存放数据的地方分为内存和硬盘,进程是运行着的程序,肯定在内存当中。

                                        

    为让进程A和进程B进行通信,它们都可以访问的空间可以是内存中它们以外的区域,或者是硬盘中的区域。

    通过内存文件映射,则是将硬盘中的一个文件,映射到内存中,进程A,B都可以访问该内存(文件),达到交换数据的目的。

    如右图是给用户的直接感觉,两个进程操作同一个物理文件,通过文件的读写,交换数据。

    2.发送方(服务器)

     个人理解,虽然共享内存都可以读写,也没有服务器和客户端的概念,但是,有一方需要创建这个文件,而另一方只需要打开这个文件。

    所以,我将创建文件的一方,认为是服务器,而打开文件,进行读取的一方称为客户端。而事实上,服务器或者客户端都可以对文件进行读写,类似于网络编程中,都可以读写。

    先贴代码,再解释

    #include "stdafx.h"
    #include "windows.h"
    #include "stdio.h"
    #pragma warning(disable:4996)
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    	HANDLE hFile = CreateFile(TEXT("c:zj.dat"),GENERIC_READ|GENERIC_WRITE,
    		0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
    	if(hFile==NULL)
    	{
    		printf("create file error!");
    		return 0;
    	}
    
    //	HANDLE hFile = (HANDLE)0xffffffff; //创建一个进程间共享的对象
    	HANDLE hMap = CreateFileMapping(hFile,NULL,PAGE_READWRITE,	0,1024*1024,TEXT("ZJ"));
    	int rst = GetLastError();
    	if (hMap != NULL && rst == ERROR_ALREADY_EXISTS) 
    	{ 
    		printf("hMap error
    ");
    		CloseHandle(hMap); 
    		hMap = NULL; 
    		return 0;
    	} 
    
    	CHAR* pszText=NULL;
    	pszText = (CHAR*)MapViewOfFile(hMap,FILE_MAP_ALL_ACCESS,0,0,1024*1024);
    	if(pszText==NULL)
    	{
    		printf("view map error!");
    		return 0;
    	}
    	sprintf(pszText,"hello my first mapping file!
    "); //其实是向文件中(共享内存中)写入了
    	while(1)
    	{
    		printf(pszText);
    		Sleep(3000);
    	}
    
    	getchar();
    
    	UnmapViewOfFile((LPCVOID)pszText);
    	CloseHandle(hMap);
    CloseHandle(hFile); return 0; }

      1)创建一个文件  CreateFile

              HANDLE hFile = CreateFile(...);  参数可参见MSDN,就是创建一般的文件,此处不详说。个人认为这个文件的目的,就是共享内存的实体(物理存在的)。

             CreateFile(TEXT("c:zj.dat"),GENERIC_READ|GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);

             在C盘的确有文件zj.dat  该文件名可以随意取。

         2)创建内存映射文件 CreateFileMapping

             将上述真正存在的文件(物理文件) hFile映射成为一个虚拟的映射文件 hMap ,即将物理文件与虚拟文件绑定

       HANDLE hMap = CreateFileMapping( hFile, NULL, PAGE_READWRITE, 0,1024*1024,TEXT("ZJ"));

            参数解释: hFile: 是1)中对应的物理文件。如果hFile=NULL,即没有通过CreateFile创建一个实际存在的文件,有解释为创建一个进程间共享的对象。

                                  个人认为也可能是在内存中开辟了一段空间,或者在硬盘上有一个默认文件。

                         NULL: 安全属性

                         PAGE_READWRITE: 可读可写

                         0,  1024*1024: 从物理文件的高0位到低1024*1024位映射成虚拟文件。(个人是这样理解的)

                        ZJ :是虚拟文件的名字,客户端读时也用这个名字,所以,可能这个名字是会在进程外部注册的。

          3)加载内存映射文件  MapViewOfFile  :映射成内存地址

               将虚拟文件映射成内存地址,方便使用。即将文件与内存绑定,以后操作该内存其实就是操作该文件。

        CHAR* pszText=NULL; //一个指针,不需要分配空间

         pszText = (CHAR*)MapViewOfFile(hMap,FILE_MAP_ALL_ACCESS,0,0,1024*1024); //通过映射后,该指针就指向该文件。

              参数解释:hMap:虚拟文件名

                           FILE_MAP_ALL_ACCESS:访问模式

                          0,0:从虚拟文件的哪个位置开始映射成内存

                         1024*1024: 映射多大的内存

         4)使用内存,即使用文件

            可以向这个内存(文件)读写数据了。

            //写

       sprintf(pszText,"hello my first mapping file! "); //语句本身的意思,是将句子写入字符串pszText中,而这个字符串并没有在程序中分配空间,即没有new

            但这句话也不会报错。是因为该字符串地址指向了映射文件,即通过操作该指针,实际是操作了文件,句子写入了文件当中。

           //读

           printf(pszText); //语句本身是将字符串中的内容输出到屏幕上,该字符串中没有分配空间,也没有赋值。但通过映射为文件,可以将文件中的内容通过该指针输出到屏幕上。

          注:个人理解,可以认为该指针pszText直接指向了硬盘空间。就是文件的操作符。

       5)卸载映射       UnmapViewOfFile((LPCVOID)pszText);

       6)关闭文件

    CloseHandle(hMap); //关闭虚拟文件
    CloseHandle(hFile); //关闭物理文件

    3.接收方(客户端)

    HANDLE hMap = OpenFileMapping(FILE_MAP_ALL_ACCESS,TRUE,TEXT("ZJ"));
        if(hMap == NULL)
        {
            printf("open file map error!");
            return 0;
        }
    
        CHAR* pszText = NULL;
        pszText = (CHAR*)MapViewOfFile(hMap,FILE_MAP_ALL_ACCESS,0,0,1024*1024);
        if(pszText==NULL)
        {
            printf("map view error!
    ");
            return 0;
        }
        printf(pszText); //从文件中读(共享内存)
         
        sprintf(pszText,"second data!
    "); //写入
    
        getchar();
    
        UnmapViewOfFile(pszText);
        CloseHandle(hMap);
    
        hMap = NULL; 
        return 0;

     1)打开内存映射文件(虚拟文件)

        HANDLE hMap = OpenFileMapping(FILE_MAP_ALL_ACCESS,TRUE,TEXT("ZJ"));

         是通过内存映射文件名 ZJ  找到该文件的。

     2)映射成内存 MapViewOfFile

          和服务器中的一样。

    3)使用内存

        读写数据,此处写入数据后,从服务器中读出来的内容就改变了。可以证明,服务器客户端同时使用着同一个文件,文件是它们之间的一个通道,用来交换数据

    4)关闭映射,关闭文件。同服务器

    注:1)服务器和客户端必须同时为进程,即都在运行的时候,才可以交换数据。

             虽然是通过物理文件,交互数据的,但是ZJ是虚拟文件的名字,该名字必须在两个进程中都能认识,才可以通过它来交互数据。

             所以,如果,服务器先打开,写入文件后,关闭。 客户端,再打开文件,则CreateFileMap会失败,也无法进行文件映射了。

                   这是我犯的一个错误,不知道大家会不会理解,就是,像网络通信一样,必须双方都在,才可以通信。

    当然客户端也可以通过CreateFile打开一个存在的文件 zj.dat  ,再用CreateFileMap去映射,同样可以访问文件中的数据。

       2)内存映射文件的机制是单纯的让访问文件变的简单。

        就好像文件流fstream模拟了输入输出流iostream一样,操作文件,就像操作cout,cin一样方便。

        同理,通过文件映射,操作文件就可以向操作内存一样方便。

        只是将这个机制用于进程间通信时,才需要考虑一端发送,一端接收,同步问题等。

      

    路是一步一步走的
  • 相关阅读:
    EBS Form菜单栏增加选项
    Oracle EBS 基础概念:弹性域-上下文字段
    EBS apps, applsys 的关系及密码更改
    设计模式-建造者模式
    微服务入门
    常见SQL编写和优化
    java 8 stream toMap问题
    mysql8.0.22在centos7.6下的简单安装
    mysql8的collate问题和修改
    springboot+security自定义登录-1-基础-自定义用户和登录界面
  • 原文地址:https://www.cnblogs.com/Lalafengchui/p/4223584.html
Copyright © 2011-2022 走看看