实验四:进程间共享内存
实验目的
通过实验了解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;
}