Windows进程通信 -- 共享内存(1)
共享内存的方式原理就是将一份物理内存映射到不同进程各自的虚拟地址空间上,这样每个进程都可以读取同一份数据,从而实现进程通信。因为是通过内存操作实现通信,因此是一种最高效的数据交换方法。
共享内存在 Windows 中是用 FileMapping 实现的,从具体的实现方法上看主要通过以下几步来实现:
1、调用 CreateFileMapping 创建一个内存文件映射对象;
HANDLE CreateFileMapping(
HANDLE hFile, // handle to file to map
LPSECURITY_ATTRIBUTES lpFileMappingAttributes,
// optional security attributes
DWORD flProtect, // protection for mapping object
DWORD dwMaximumSizeHigh, // high-order 32 bits of object size
DWORD dwMaximumSizeLow, // low-order 32 bits of object size
LPCTSTR lpName // name of file-mapping object
);
通过这个API函数 将创建一个内存映射文件的内核对象,用于映射文件到内存。与虚拟内存一样,内存映射文件可以用来保留一个地址空间的区域,并将物理存储器提交
给该区域。它们之间的差别是,物理存储器来自一个已经位于磁盘上的文件,而不是系统的页文件。
hFile:用于标识你想要映射到进程地址空间中的文件句柄。该句柄可以通过调用C r e a t e F i l e函数返回。这里,我们并不需要一个实际的文件,所以,就不需要调用 CreateFile 创建一个文件, hFile 这个参数可以填写 INVALID_HANDLE_VALUE; lpFileMappingAttributes:参数是指向文件映射内核对象的 SECURITY_ATTRIBUTES结构的指针,通常传递的值是 N U L L; flProtect:对内存映射文件的安全设置(PAGE_READONLY 以只读方式打开映射;PAGE_READWRITE 以可读、可写方式打开映射;PAGE_WRITECOPY 为写操作留下备份) dwMaximumSizeHigh:文件映射的最大长度的高32位。 dwMaximumSizeLow:文件映射的最大长度的低32位。如这个参数和dwMaximumSizeHigh都是零,就用磁盘文件的实际长度。 lpName:指定文件映射对象的名字,别的进程就可以用这个名字去调用 OpenFileMapping 来打开这个 FileMapping 对象。
如果创建成功,返回创建的内存映射文件的句柄,如果已经存在,则也返回其句柄,但是调用 GetLastError()返回的错误码是:183(ERROR_ALREADY_EXISTS),如果创建失败,则返回NULL;
2、调用 MapViewOfFile 映射到当前进程的虚拟地址上;
如果调用CreateFileMapping成功,则调用MapViewOfFile函数,将内存映射文件映射到进程的虚拟地址中;
LPVOID MapViewOfFile(
HANDLE hFileMappingObject, // file-mapping object to map into
// address space
DWORD dwDesiredAccess, // access mode
DWORD dwFileOffsetHigh, // high-order 32 bits of file offset
DWORD dwFileOffsetLow, // low-order 32 bits of file offset
DWORD dwNumberOfBytesToMap // number of bytes to map
);
hFileMappingObject:CreateFileMapping()返回的文件映像对象句柄。
dwDesiredAccess: 映射对象的文件数据的访问方式,而且同样要与CreateFileMapping()函数所设置的保护属性相匹配。
dwFileOffsetHigh: 表示文件映射起始偏移的高32位.
dwFileOffsetLow: 表示文件映射起始偏移的低32位.
dwNumberOfBytesToMap :文件中要映射的字节数。为0表示映射整个文件映射对象。
3、在接收进程中打开对应的内存映射对象
在数据接收进程中,首先调用OpenFileMapping()函数打开一个命名的文件映射内核对象,得到相应的文件映射内核对象句柄hFileMapping;如果打开成功,则调用MapViewOfFile()函数映射对象的一个视图,将文件映射内核对象hFileMapping映射到当前应用程序的进程地址,进行读取操作。(当然,这里如果用CreateFileMapping也是可以获取对应的句柄)
HANDLE OpenFileMapping(
DWORD dwDesiredAccess, // access mode
BOOL bInheritHandle, // inherit flag
LPCTSTR lpName // pointer to name of file-mapping object
);
dwDesiredAccess:同MapViewOfFile函数的dwDesiredAccess参数
bInheritHandle :如这个函数返回的句柄能由当前进程启动的新进程继承,则这个参数为TRUE。
lpName :指定要打开的文件映射对象名称。
4、进行内存映射文件的读写
一旦MapViewOfFile调用成功,就可以像读写本进程地址空间的内存区一样,进行内存的读写操作了。
//读操作:
if ( m_pViewOfFile )
{
// read text from memory-mapped file
TCHAR s[dwMemoryFileSize]; lstrcpy(s, (LPCTSTR) m_pViewOfFile);
}
//写操作:
if ( m_pViewOfFile )
{
TCHAR s[dwMemoryFileSize];
m_edit_box.GetWindowText(s, dwMemoryFileSize); lstrcpy( (LPTSTR) m_pViewOfFile, s); // Notify all running instances that text was changed
::PostMessage(HWND_BROADCAST,
wm_Message,
(WPARAM) m_hWnd,
);
}
5、清理内核对象
在用完后,要取消本进程地址空间的映射,并释放内存映射对象。
//取消本进程地址空间的映射;
UnmapViewOfFile(pLocalMem); pLocalMem=NULL;
//关闭文件映射内核文件
CloseHandle(hFileMapping);
6、简单例子:
下面写一个简单的例子来说明:
例子:在一个进程的文本对话框中输入文本,同时在另一个进程的文本对话框中显示之前输入的内容。
const DWORD dwMemoryFileSize = * ; //指定内存映射文件大小
const LPCTSTR sMemoryFileName = _T("D9287E19-6F9E-45fa-897C-D392F73A0F2F");//指定内存映射文件名称
const UINT wm_Message =
RegisterWindowMessage(_T("CC667211-7CE9-40c5-809A-1DA48E4014C4"));//注册消息
指定消息处理函数
BEGIN_MESSAGE_MAP(CIpcDlg, CDialog)
//{{AFX_MSG_MAP(CIpcDlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_WM_DESTROY()
ON_REGISTERED_MESSAGE(wm_Message, OnMessageTextChanged)
ON_EN_CHANGE(IDC_EDT_TEXT, OnChangeEdtText)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
LRESULT CIpcDlg::OnMessageTextChanged( WPARAM wParam, LPARAM lParam )
{
if ( wParam == (WPARAM) m_hWnd )
return 0;
// Get text from memory mapped file and set it to edit box
GetTextFromMemoryMappedFile();
return 0;
}
窗口初始化函数中进行内存映射文件的初始化:
void CIpcDlg::Initialize()
{
m_edit_box.SetLimitText(dwMemoryFileSize - );
m_hFileMapping = CreateFileMapping(
INVALID_HANDLE_VALUE, // system paging file
NULL, // security attributes
PAGE_READWRITE, // protection
, // high-order DWORD of size
dwMemoryFileSize*sizeof(TCHAR), // low-order DWORD of size
sMemoryFileName); // name
DWORD dwError = GetLastError(); // if ERROR_ALREADY_EXISTS
// this instance is not first (other instance created file mapping) if ( ! m_hFileMapping )
{
MessageBox(_T("Creating of file mapping failed"));
}
else
{
m_pViewOfFile = MapViewOfFile(
m_hFileMapping, // handle to file-mapping object
FILE_MAP_ALL_ACCESS, // desired access
,
,
); // map all file if ( ! m_pViewOfFile )
{
MessageBox(_T("MapViewOfFile failed"));
} // Now we have m_pViewOfFile memory block which is common for
// all instances of this program
} if ( dwError == ERROR_ALREADY_EXISTS )
{
// Some other instance is already running,
// get text from existing file mapping
GetTextFromMemoryMappedFile();
}
}
内存映射对象内容的读取及显示:
void CIpcDlg::GetTextFromMemoryMappedFile()
{
if ( m_pViewOfFile )
{
// read text from memory-mapped file
TCHAR s[dwMemoryFileSize]; lstrcpy(s, (LPCTSTR) m_pViewOfFile); // Write text to edit box.
// SetWindowText raises EN_CHANGE event and
// OnChangeEditBox is called. Ensure that OnChangeEditBox
// does nothing by setting m_bNotify to FALSE
m_bNotify = FALSE; m_edit_box.SetWindowText(s); m_bNotify = TRUE;
}
}
在文本框的OnChangeEdtText事件中写内存映射文件,并发wm_Message 进行通知:
void CIpcDlg::OnChangeEdtText()
{
if ( m_bNotify) // change is not done by SetWindowText
{
// write text to memory-mapped file
if ( m_pViewOfFile )
{
TCHAR s[dwMemoryFileSize];
m_edit_box.GetWindowText(s, dwMemoryFileSize); lstrcpy( (LPTSTR) m_pViewOfFile, s); // Notify all running instances that text was changed
::PostMessage(HWND_BROADCAST,
wm_Message,
(WPARAM) m_hWnd,
);
}
} }

至此,一个最简单的通过内存映射文件的进程通信的例子就完成了,但是这里存在一个问题:读和写之间的冲突没有很好的解决,内存映射文件是一个共享的资源,多个进程读写必然存在同步的问题,也许在这个例子中不会出现什么问题,但是实际项目中存在较高频率的并发读写的情况下,如何进行同步是一个必须解决的问题,未完待续…
Windows进程通信 -- 共享内存(1)的更多相关文章
- Windows进程通信 -- 共享内存
享内存的方式原理就是将一份物理内存映射到不同进程各自的虚拟地址空间上,这样每个进程都可以读取同一份数据,从而实现进程通信.因为是通过内存操作实现通信,因此是一种最高效的数据交换方法. 共享内存在 Wi ...
- Windows进程通信-共享内存空间
三个模块 1,game.exe,三个方法,控制台输入指令('A','B','R')分别控制三个方法的调用: 2,WGDll.dll,要注入到game进程中的dll文件: 3,myconsole.exe ...
- Windows进程间共享内存通信实例
Windows进程间共享内存通信实例 抄抄补补整出来 采用内存映射文件实现WIN32进程间的通讯:Windows中的内存映射文件的机制为我们高效地操作文件提供了一种途径,它允许我们在WIN32进程中保 ...
- Linux学习笔记(14)-进程通信|共享内存
在Linux中,共享内存是允许两个不相关的进程访问同一个逻辑内存的进程间通信方法,是在两个正在运行的进程之间共享和传递数据的一种非常有效的方式. 不同进程之间共享的内存通常安排为同一段物理内存.进程可 ...
- Windows中利用共享内存来实现不同进程间的通信
Windows中利用共享内存来实现不同进程间的通信 一.msdn详细介绍 https://docs.microsoft.com/zh-cn/windows/win32/memory/sharing-f ...
- C# .Net 多进程同步 通信 共享内存 内存映射文件 Memory Mapped 转 VC中进程与进程之间共享内存 .net环境下跨进程、高频率读写数据 使用C#开发Android应用之WebApp 分布式事务之消息补偿解决方案
C# .Net 多进程同步 通信 共享内存 内存映射文件 Memory Mapped 转 节点通信存在两种模型:共享内存(Shared memory)和消息传递(Messages passing). ...
- linux 两个进程通过 共享内存 通信例子
例子1:两个进程通过共享内存通信,一个进程向共享内存中写入数据,另一个进程从共享内存中读出数据 文件1 创建进程1,实现功能,打印共享内存中的数据 #include <stdio.h> # ...
- C# .Net 多进程同步 通信 共享内存 内存映射文件 Memory Mapped 转
原文:C# .Net 多进程同步 通信 共享内存 内存映射文件 Memory Mapped 转 节点通信存在两种模型:共享内存(Shared memory)和消息传递(Messages passing ...
- 【VS开发】内存映射文件进程间共享内存
内存映射文件进程间共享内存 内存映射文件的另一个功能是在进程间共享数据,它提供了不同进程共享内存的一个有效且简单的方法.后面的许多例子都要用到共享内存.共享内存主要是通过映射机制实现的.Windows ...
随机推荐
- React阶段开发总结
这次独立编写了React页面主要是数据切换.点击不同的按钮,Ajax请求不同的后台数据.数据驱动表格内容的显示.使用React组件开发. 开发中获得下面的心得: 1.后台给的地址早一点添加路由(写好数 ...
- phpStudy 的Apache虚拟主机配置
放弃了wamp,朋友介绍了phpstudy,不错的一款软件,关键是能自由切换php版本.相关的阿帕奇虚拟主机配置参考:http://www.th7.cn/system/win/201506/10846 ...
- Caused by: java.lang.UnsatisfiedLinkError: Couldn't load BaiduMapVOS_v2_1_3: findLibrary returned nu
我是在整合百度LBS服务与百度语音识别服务的时候遇到这个问题的........ 解决办法是:不要导armeabi-v7a这个文件夹即可. 貌似还有的人试一下以下这种方法(这种方法来自:http://w ...
- js-正则表达式的替换
正则表达式替换使用的是replace()方法.Replace()方法是用一些字符途欢另一些字符 语法:stringObject.replace(regexp,replacement) regexp 必 ...
- Linux最常用命令之cd和ls
为什么说是最常用的命令呢,因为从普及程度看,即使不怎么接触过Linux系统的人,大多数都会知道这两个命令:而从使用频率看,这两个命令也是当之无愧的首位.现在我们就来看看这两个命令. cd 篇:cd 即 ...
- html或者jsp页面引用jar包中的js文件
一,页面上引用jar包中的js文件的方法 使用java web框架AppFuse的时候发现,jquery.bootstrap等js框架都封装到jar包里面了.这些js文件通过一个wro4j的工具对其进 ...
- 天气预报API(三):免费接口测试(“旧编码”)
说明 我以参考文章为引子,自己测试并扩展,努力寻找更多的气象API... 本文所有测试均以青岛为例. 本文所列接口城市代码(cityid)参数都使用的 "旧编码": 全国城市代码列 ...
- Unity中使用多构造函数(转)
如果要实例化的类只有一个构造函数, 则使用方法很简单使用方法如下: 1 2 3 4 5 6 7 using (IUnityContainer container = new UnityContaine ...
- 分析Linux内核创建一个新进程的过程
一.原理分析 1.进程的描述 进程控制块PCB——task_struct,为了管理进程,内核必须对每个进程进行清晰的描述,进程描述符提供了内核所需了解的进程信息. struct task_struct ...
- 基于VC的ACM音频编程接口压缩Wave音频(三)
(三)音 频 数 据 的 压 缩 下 面 说 明 使 用 CODEC 实 现 音 频 压 缩 的 过 程:假 设 源 信 号 为8K 采 样.16bits PCM 编 码. 单 声 道. 长 度 为1 ...