实验四:进程间共享内存

实验四:进程间共享内存

实验目的

通过实验了解windows如何通过内存映射文件机制来实现进程间共享内存;

实验内容

(1)创建一个写进程,创建一个命名的内存映射文件,将一个文件映射对象映射到当前应用程序的虚拟地址空间,在虚拟地址空间中写入数据;
(2)创建一个读进程,打开命名的内存映射文件,将文件映射对象映射到当前应用程序的虚拟地址空间,在虚拟地址空间中读出数据。

实验步骤

1、写进程

实验描述

(1) 利用CreateFileMapping()创建一个命名的内存映射文件对象;
(2) 利用MapViewOfFile()将文件映射到当前应用程序的虚拟地址空间;
(3)在虚拟地址空间中写入数据。

IProcessSharedMW.cpp

// IProcessSharedMW.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <windows.h>
#include <iostream>

int main(int argc, char* argv[])
{
	HANDLE lhShareMemory;  
	char* lpBuffer = NULL;  
	  
	//创建一个和物理文件无关的内存映射文件对象(换出时使用页文件)。
    /*
      HANDLE   CreateFileMapping(HANDLE   hFile,   
                                 LPSECURITY_ATTRIBUTES   lpFileMappingAttributes,   
                                 DWORD   flProtect,   
                                 DWORD   dwMaximumSizeHigh,   
                                 DWORD   dwMaximumSizeLow,   
                                 LPCTSTR   lpName);  
           hFile:指定欲在其中创建映射的一个文件句柄。
                 0xFFFFFFFF(-1,即INVALID_HANDLE_VALUE)表示换出时使用页文件。  
           dwMaximumSizeHigh:文件映射的最大长度的高32位,
           dwMaximumSizeLow:文件映射的最大长度的低32位,
		   如果这两个参数都是零,就用磁盘文件的实际长度。
    */ 

	 lhShareMemory = CreateFileMapping(HANDLE(0xFFFFFFFF), NULL, PAGE_READWRITE,  
	                                   0, 100, "mySharedMemory");  
	   
	 if (NULL == lhShareMemory)  
	 {  
	    if (ERROR_ALREADY_EXISTS == GetLastError())  
		{  
	       std:: cout << "Already exists!"<<std:: endl;
		}  
	    else  
		{  
	       std::cout << "Create Sheared Memory unsuccessfully!"<< std::endl; 
		}  
	    return 0;  
	 }  
	 
	 //把文件或文件的一部分映射到进程的虚拟地址空间。
     /* 
       LPVOID   MapViewOfFile(HANDLE   hFileMappingObject,   
                              DWORD   dwDesiredAccess,   
                              DWORD   dwFileOffsetHigh,   
                              DWORD   dwFileOffsetLow,   
                              DWORD   dwNumberOfBytesToMap); 
     */
	 lpBuffer = (char*)MapViewOfFile(lhShareMemory, FILE_MAP_WRITE, 0, 0, 100);  
	 if (NULL == lpBuffer)  
	 {  
	    std::cout << "Get Share memory unsuccessfully!"<< std::endl;  
	    return 0;  
	 }  
   
     strcpy(lpBuffer, "Hello,every students,please study hard! ");  
	 std::cout << "进程通信:采用共享内存" << std::endl; 
	 std::cout << "写进程" << std::endl;
	 std::cout << "写入数据:"<< std::endl<<lpBuffer << std::endl;  

	 getchar();  
	 UnmapViewOfFile(lpBuffer); 
	 
	 return 0;


}


在这里插入图片描述

2、读进程

实验描述

(1) 利用OpenFileMapping()打开已创建的命名内存映射文件对象;
(2) 利用MapViewOfFile()将文件映射到当前应用程序的虚拟地址空间;
(3)从虚拟地址空间中读出数据。

IProcessSharedMR.cpp

// IProcessSharedMR.cpp : Defines the entry point for the console application.
#include "stdafx.h"
#include <windows.h>
#include <iostream>

int main(int argc, char* argv[])
{
	 HANDLE lhShareMemory;  
	 char* lpcBuffer;  
	 //这个函式返回的句柄由当前进程启动的新进程继承,这个参数为true
	 //指定要打开的档案映射对象名称
	 lhShareMemory = OpenFileMapping(FILE_MAP_READ, false, "mySharedMemory");  //打开一个现成的文件映射对象的函数。
	 if (NULL == lhShareMemory)  
	 {  	  
	    std::cout << "Open share memory unsuccessfully!" << std::endl;  
	    DWORD ldwError = GetLastError();  
	    std::cout << ldwError<<"\n";  
	    return 0;  
	 }  
	   
	 //把文件或文件的一部分映射到进程的虚拟地址空间。
	 /*
       LPVOID   MapViewOfFile(
							  HANDLE   hFileMappingObject,   //返回的文件映像对象句柄。
                              DWORD   dwDesiredAccess,		//映射对象的文件数据的访问方式
                              DWORD   dwFileOffsetHigh,   //表示文件映射起始偏移的高32位.
                              DWORD   dwFileOffsetLow,   //表示文件映射起始偏移的低32位.
                              DWORD   dwNumberOfBytesToMap); //指定映射文件的字节数.
     */
	 lpcBuffer = (char*)MapViewOfFile(lhShareMemory, FILE_MAP_READ, 0, 0, 100);  
	 if (NULL == lpcBuffer)  
	 {  
	    std::cout << "Open share memory unsuccessfully!\n";  
	    return 0;  
	 }  
	 std::cout << "进程通信:采用共享内存" << std::endl;  
	 std::cout << "读进程" << std::endl;  
	 std::cout << "读出数据:" << std::endl;  
	 for (int i = 0; i < 100; ++i)  
	 {  
	    std::cout << *(lpcBuffer + i);  

	 } 

	 getchar();  
	 UnmapViewOfFile(lpcBuffer);  
	 
	 return 0;  

}

需要手动执行,有先后顺序,需要先运行写进程在运行读进程才能成功,否则则会如下图所示:
在这里插入图片描述
正确操作:
在这里插入图片描述
在这里插入图片描述

思考题

1.Windows下的进程间共享内存是如何实现的?
基于共享存储区的共享存储器系统机制
内存映射文件,把对文件的操作转变成对内存的操作

2.对于读写进程,物理内存是什么时候分配的?
读写进程物理内存是写时分配的, 读时不重新分配。
解释:首次使用的时候:
对于写数据时
strcpy(lpBuffer, "Hello,ever students,please study hard! ");时候时发生缺页中断分配了物理内存。
对于读进程不会分配:
在std::cout <<*(lpcBuffer+i);时由于使用同一块共享存储区,写进程已经分配物理内存,因此不需要再分配物理内存。

3.读写进程之间的同步和互斥在共享内存机制中已经存在了,还是需要用户自己来实现?
需要用户自己来手动实现同步和互斥操作。
共享内存并未提供进程同步和互斥机制,使用共享内存完成进程间通信时,需要借助互斥量或者信号量来完成进程的同步。

4.利用“虚拟内存的检测”程序检测读进程所映射的虚拟地址空间块的信息。

//引入虚拟内存检测的文件
#include "CheckVM.cpp"
//当前所使用的的虚拟内存块(以16进制形式输出)
printf("%x'\n'", lpcBuffer);
//调用虚拟内存检测函数
::WalkVM(::GetCurrentProcess());
//001d0000-001d1000 (4.00 KB) Committed, READONLY, Mapped
// IProcessSharedMR.cpp : Defines the entry point for the console application.
#include "stdafx.h"
#include <windows.h>
#include <iostream>
#include "C:\CheckVM\CheckVM.cpp"


using namespace std;


int main(int argc, char* argv[])
{
	 HANDLE lhShareMemory;
	 char* lpcBuffer;

	 lhShareMemory = OpenFileMapping(FILE_MAP_READ, false, "mySharedMemory");


	// void WalkVM(HANDLE hProcess,char* pBlock)




	 if (NULL == lhShareMemory)
	 {
	    std::cout << "Open share memory unsuccessfully!" << std::endl;
	    DWORD ldwError = GetLastError();
	    std::cout << ldwError;
	    return 0;
	 }

	 //把文件或文件的一部分映射到进程的虚拟地址空间。
	 /*
       LPVOID   MapViewOfFile(HANDLE   hFileMappingObject,
                              DWORD   dwDesiredAccess,
                              DWORD   dwFileOffsetHigh,
                              DWORD   dwFileOffsetLow,
                              DWORD   dwNumberOfBytesToMap);
     */


	 lpcBuffer = (char*)MapViewOfFile(lhShareMemory, FILE_MAP_READ, 0, 0, 6*1024);
	 if (NULL == lpcBuffer)
	 {
	    std::cout << "Open share memory unsuccessfully!";
	    return 0;
	 }
	 //std::cout << "::GetCurrentProcess()===>" <<hex<< lpcBuffer << std::endl;

	 

	 std::cout << "进程通信:采用共享内存" << std::endl;
	 std::cout << "读进程" << std::endl;
	 std::cout << "读入数据:" << std::endl;
	 for (int i = 0; i < 100; ++i)
	 {
	    std::cout << *(lpcBuffer + i);
	 }
	cout<<endl;
	printf("%x \n", lpcBuffer);
	::WalkVM(::GetCurrentProcess());
	 getchar();
	 UnmapViewOfFile(lpcBuffer);

	 return 0;

}

// CheckVM.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <windows.h>
#include <iostream>
#include <shlwapi.h>
#include <iomanip>
#pragma comment(lib, "Shlwapi.lib")

// 显示内存保护的方法。
// 保护标记指示:允许应用程序对内存进行访问的类型以及操作系统强制访问的类型
inline bool TestSet(DWORD dwTarget, DWORD dwMask)
{
    return ((dwTarget &dwMask) == dwMask) ;
}

//宏定义一个函数
//TestSet为内联函数
# define SHOWMASK(dwTarget, type) \
if (TestSet(dwTarget, PAGE_##type) ) \
    {std :: cout << ", " << #type; }

void ShowProtection(DWORD dwTarget)
{
    SHOWMASK(dwTarget, READONLY) ;
    SHOWMASK(dwTarget, GUARD) ;
    SHOWMASK(dwTarget, NOCACHE) ;
    SHOWMASK(dwTarget, READWRITE) ;
    SHOWMASK(dwTarget, WRITECOPY) ;
    SHOWMASK(dwTarget, EXECUTE) ;
    SHOWMASK(dwTarget, EXECUTE_READ) ;
    SHOWMASK(dwTarget, EXECUTE_READWRITE) ;
    SHOWMASK(dwTarget, EXECUTE_WRITECOPY) ;
    SHOWMASK(dwTarget, NOACCESS) ;
}

// 遍历整个进程虚拟地址空间,并显示虚拟内存块的属性
void WalkVM(HANDLE hProcess)
{
    // 首先获得系统信息
    SYSTEM_INFO si;
    :: ZeroMemory(&si, sizeof(si) ) ;
    :: GetSystemInfo(&si) ;

    // 分配要存放信息的缓冲区
    MEMORY_BASIC_INFORMATION mbi;
    :: ZeroMemory(&mbi, sizeof(mbi) ) ;

    // 循环检查整个进程虚拟地址空间
    LPCVOID pBlock = (LPVOID) si.lpMinimumApplicationAddress;
    while (pBlock < si.lpMaximumApplicationAddress)
    {
        // 获得当前虚拟内存块的信息
        if (:: VirtualQueryEx(
            hProcess,			          // 相关的进程
            pBlock,                       // 虚拟内存块的开始位置
			&mbi,                         // 存放虚拟内存块信息的缓冲区
            sizeof(mbi))==sizeof(mbi) )	  // 大小的确认
        {
            // 计算下一虚拟内存块的起始地址
            LPCVOID pEnd = (PBYTE) pBlock + mbi.RegionSize;
			//保存当前虚拟内存块的长度
			TCHAR szSize[MAX_PATH];
            :: StrFormatByteSize(mbi.RegionSize, szSize, MAX_PATH) ;

            // 显示当前虚拟内存块的地址和大小
            std :: cout.fill ('0') ;
            std :: cout
                << std :: hex << std :: setw(8) << (DWORD) pBlock
                << "-"
                << std :: hex << std :: setw(8) << (DWORD) pEnd
                << (:: strlen(szSize)==7? " (" : " (") << szSize
                << ") " ;

            // 显示当前虚拟内存块的状态
            switch(mbi.State)
            {
                case MEM_COMMIT :
					std :: cout << "Committed" ;//虚拟页面映射到外存。
					break;
                case MEM_FREE :
					std :: cout << "Free" ;     
					break;
                case MEM_RESERVE :
					std :: cout << "Reserved" ; // 以这个地址开始的虚拟内存块被预留,
					                            // 该进程再分配虚拟内存时不得使用这段内存。
					                            // 此时还没有映射到外存。
					break;
            }

            //重新调整当前虚拟内存块的保护方式
			if(mbi.Protect==0 && mbi.State!=MEM_FREE)
            {
                mbi.Protect=PAGE_READONLY;
            }
			// 显示当前虚拟内存块的保护方式
			ShowProtection(mbi.Protect);

            // 显示当前虚拟内存块的类型
            switch(mbi.Type){
                case MEM_IMAGE :    //该虚拟内存块映射的是可执行文件,如*.dll,*.exe。
					std :: cout << ", Image" ;
					break;
				case MEM_MAPPED:    //该虚拟内存块映射的是数据文件,用CreateFileMapping()创建。
					std :: cout << ", Mapped";
					break;
                case MEM_PRIVATE :  //该虚拟内存块不被共享,如堆栈。
					std :: cout << ", Private" ;
					break;
			}

            // 获得可执行的文件名。
            TCHAR szFilename [MAX_PATH] ;
            if (:: GetModuleFileName (
                (HMODULE) pBlock,   // 一个模块的句柄。模块句柄跟一般的句柄不一样,
				                    // 模块句柄指向的就是EXE和DLL等在虚拟地址空间的位置。
		                            // 如果该参数为NULL,该函数返回该应用程序全路径。
				szFilename,         // 文件名称
				MAX_PATH)>0)        // 实际使用的缓冲区大小
            {
                // 除去文件名的路径并将文件名显示出来
                :: PathStripPath(szFilename) ;
                std :: cout << ", Module: " << szFilename;
			}

            std :: cout << std :: endl;
			
			// 移动虚拟内存块指针以获得下一个虚拟内存块
            pBlock = pEnd;
        }
    }
}

/*
int main(int argc, char* argv[])
{
	// 遍历当前进程的虚拟地址空间
	::WalkVM(::GetCurrentProcess());
	return 0;
}
*/
// IProcessSharedMW.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <windows.h>
#include <iostream>

int main(int argc, char* argv[])
{
	HANDLE lhShareMemory;  
	char* lpBuffer = NULL; 
	  
	//创建一个和物理文件无关的内存映射文件对象(换出时使用页文件)。
    /*
      HANDLE   CreateFileMapping(HANDLE   hFile,   
                                 LPSECURITY_ATTRIBUTES   lpFileMappingAttributes,   
                                 DWORD   flProtect,   
                                 DWORD   dwMaximumSizeHigh,   
                                 DWORD   dwMaximumSizeLow,   
                                 LPCTSTR   lpName);  
           hFile:指定欲在其中创建映射的一个文件句柄。
                 0xFFFFFFFF(-1,即INVALID_HANDLE_VALUE)表示换出时使用页文件。  
           dwMaximumSizeHigh:文件映射的最大长度的高32位,
           dwMaximumSizeLow:文件映射的最大长度的低32位,
		   如果这两个参数都是零,就用磁盘文件的实际长度。
    */ 

	 lhShareMemory = CreateFileMapping(HANDLE(0xFFFFFFFF), NULL, PAGE_READWRITE,  
	                                   0, 6*1024, "mySharedMemory");  
	   
	 if (NULL == lhShareMemory)  
	 {  
	    if (ERROR_ALREADY_EXISTS == GetLastError())  
		{  
	       std:: cout << "Already exists!"<<std:: endl;
		}  
	    else  
		{  
	       std::cout << "Create Sheared Memory unsuccessfully!"<< std::endl; 
		}  
	    return 0;  
	 }  
	 
	 //把文件或文件的一部分映射到进程的虚拟地址空间。
     /* 
       LPVOID   MapViewOfFile(HANDLE   hFileMappingObject,   
                              DWORD   dwDesiredAccess,   
                              DWORD   dwFileOffsetHigh,   
                              DWORD   dwFileOffsetLow,   
                              DWORD   dwNumberOfBytesToMap); 
     */


	 lpBuffer = (char*)MapViewOfFile(lhShareMemory, FILE_MAP_WRITE, 0, 0, 100);  
	 if (NULL == lpBuffer)  
	 {  
	    std::cout << "Get Share memory unsuccessfully!"<< std::endl;  
	    return 0;  
	 }  
	  
     strcpy(lpBuffer, "can you hear me?");  
	 std::cout << "进程通信:采用共享内存" << std::endl; 
	 std::cout << "写进程" << std::endl;
	 std::cout << "写入数据:"<<lpBuffer << std::endl<< std::endl;  
	 getchar(); 
	 UnmapViewOfFile(lpBuffer); //停止当前程序的一个内存映射。
	 return 0;

}

首先先打开写进程的工程,运行。
在这里插入图片描述

然后在打开读进程的工程,运行。
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

更换数据监测:

// IProcessSharedMW.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <windows.h>
#include <iostream>

int main(int argc, char* argv[])
{
	HANDLE lhShareMemory;  
	char* lpBuffer = NULL;  
	  
	//创建一个和物理文件无关的内存映射文件对象(换出时使用页文件)。
    /*
      HANDLE   CreateFileMapping(HANDLE   hFile,   
                                 LPSECURITY_ATTRIBUTES   lpFileMappingAttributes,   
                                 DWORD   flProtect,   
                                 DWORD   dwMaximumSizeHigh,   
                                 DWORD   dwMaximumSizeLow,   
                                 LPCTSTR   lpName);  
           hFile:指定欲在其中创建映射的一个文件句柄。
                 0xFFFFFFFF(-1,即INVALID_HANDLE_VALUE)表示换出时使用页文件。  
           dwMaximumSizeHigh:文件映射的最大长度的高32位,
           dwMaximumSizeLow:文件映射的最大长度的低32位,
		   如果这两个参数都是零,就用磁盘文件的实际长度。
    */ 

	 lhShareMemory = CreateFileMapping(HANDLE(0xFFFFFFFF), NULL, PAGE_READWRITE,  
	                                   0, 6*1024, "mySharedMemory");  
	   
	 if (NULL == lhShareMemory)  
	 {  
	    if (ERROR_ALREADY_EXISTS == GetLastError())  
		{  
	       std:: cout << "Already exists!"<<std:: endl;
		}  
	    else  
		{  
	       std::cout << "Create Sheared Memory unsuccessfully!"<< std::endl; 
		}  
	    return 0;  
	 }  
	 
	 //把文件或文件的一部分映射到进程的虚拟地址空间。
     /* 
       LPVOID   MapViewOfFile(HANDLE   hFileMappingObject,   
                              DWORD   dwDesiredAccess,   
                              DWORD   dwFileOffsetHigh,   
                              DWORD   dwFileOffsetLow,   
                              DWORD   dwNumberOfBytesToMap); 
     */
	 lpBuffer = (char*)MapViewOfFile(lhShareMemory, FILE_MAP_WRITE, 0, 0, 6*1024);  
	 if (NULL == lpBuffer)  
	 {  
	    std::cout << "Get Share memory unsuccessfully!"<< std::endl;  
	    return 0;  
	 }  
   
     strcpy(lpBuffer, "Hello,every students,please study hard! ");  
	 std::cout << "进程通信:采用共享内存" << std::endl; 
	 std::cout << "写进程" << std::endl;
	 std::cout << "写入数据:"<< std::endl<<lpBuffer << std::endl;  

	 getchar();  
	 UnmapViewOfFile(lpBuffer); 
	 
	 return 0;


}




// IProcessSharedMR.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <windows.h>
#include <iostream>
#include "C:\CheckVM\CheckVM.cpp"

int main(int argc, char* argv[])
{
	 HANDLE lhShareMemory;  
	 char* lpcBuffer;  
	 //这个函式返回的句柄由当前进程启动的新进程继承,这个参数为true
	 //指定要打开的档案映射对象名称
	 lhShareMemory = OpenFileMapping(FILE_MAP_READ, false, "mySharedMemory");  //打开一个现成的文件映射对象的函数。
	 if (NULL == lhShareMemory)  
	 {  	  
	    std::cout << "Open share memory unsuccessfully!" << std::endl;  
	    DWORD ldwError = GetLastError();  
	    std::cout << ldwError<<"\n";  
	    return 0;  
	 }  
	   
	 //把文件或文件的一部分映射到进程的虚拟地址空间。
	 /*
       LPVOID   MapViewOfFile(
							  HANDLE   hFileMappingObject,   //返回的文件映像对象句柄。
                              DWORD   dwDesiredAccess,		//映射对象的文件数据的访问方式
                              DWORD   dwFileOffsetHigh,   //表示文件映射起始偏移的高32位.
                              DWORD   dwFileOffsetLow,   //表示文件映射起始偏移的低32位.
                              DWORD   dwNumberOfBytesToMap); //指定映射文件的字节数.
     */
	 lpcBuffer = (char*)MapViewOfFile(lhShareMemory, FILE_MAP_READ, 0, 0, 6*1024);  
	 if (NULL == lpcBuffer)  
	 {  
	    std::cout << "Open share memory unsuccessfully!\n";  
	    return 0;  
	 }  
	 std::cout << "进程通信:采用共享内存" << std::endl;  
	 std::cout << "读进程" << std::endl;  
	 std::cout << "读出数据:" << std::endl;  
	 for (int i = 0; i < 100; ++i)  
	 {  
	    std::cout << *(lpcBuffer + i);  

	 } 
	 std::cout<<std::endl;
	 printf("%x \n", lpcBuffer);
	 ::WalkVM(::GetCurrentProcess());
	 getchar();  
	 UnmapViewOfFile(lpcBuffer);  
	 
	 return 0;  

}

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


  • 8
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值