Windows进程通信之PE文件共享节
来源:互联网 发布:数控滚齿机编程 编辑:程序博客网 时间:2024/06/15 04:19
本文由danny发表于 http://blog.csdn.net/danny_share
说明:建议先下载本文配套工程,其中
SectionDLL、SectionMain工程、SectionASub工程,SectionBSub工程分别用于演示进程间通信的DLL工程、主进程和两个子进程
下载地址:http://download.csdn.net/detail/danny_share/7749203
注意:
1.不要F5直接运行
2.编译生成debug目录或者release目录以后,手动双击SectionMain.exe点击Test按钮即可
一. PE文件
本文主要是从使用的角度来看PE文件的节(也称段),来实现进程间通信,实际只是共享数据,和进程内存读写一样,没有监听或者通知的功能。所以对PE文件只作概览陈述。
1.Copy On Write
在Windows保护模式下,由于每个进程拥有独立的虚拟地址空间,操作系统负责把虚拟地址映射成物理地址,对于同一个可执行文件生成的不同实例,Windows采用“Copy On Write”技术实现创建时共享虚拟地址空间(此时只读),当需写入时则copy一份(此时可写),因而两者的运行互不干扰,又可以加快运行。
2.PE结构
PE文件大致结构如下:
详细结构如下(此图转载自程序人生的博客http://www.programlife.net/pe-coff-specification.html):
3.常见节
(1)Windows是通过PE-Optional-Header中的DataDirectory而不是通过名字来定位的
(2)但为了避免混淆,我们在自定义节的时候,应尽量避免使用下面的节名
(3)通过PE文件的结构,我们也可以明白为什么C/C++中老是区分全局初始化变量,未初始化变量,只读数据,
ID
节名
含义
1
.arch
代码段
2
.bss
未经初始化的数据
3
.CRT
C运行期只读数据
4
.data
初始化的数据
5
.debug
调试信息
6
.didata
延迟输入文件表名
7
.edata
导出文件表名
8
.idata
导入文件表名
9
.pdata
异常信息
10
.rdata
只读的初始化数据
11
.reloc
重定位表信息
12
.rsrc
资源
13
.text
可执行代码
14
.tls
线程本地存储
15
.xdata
异常处理表
二.共享节
1.节属性
节属性标志位由IMAGE_SECTION_HEADER字段决定,其中对于我们自定义用来共享的节,主要关心以下三个属性
属性
含义
R
只读
W
可写
S
共享,
该节就关闭了CopyOnWrite功能
2.VS创建共享节
在VS中通过以下预处理指令实现新建共享节
#pragma data_seg(“SectionName”)int testShareData=0;#pragma data_seg#pragma comment(linker,”/section:SectionName,rws”)
注意事项:
(1)“SectionName”如上所述,为避免混淆,不应和3.常见节中名字重复
(2)共享数据testShareData必须初始化,否则就会放入.bss段
(3)”/section:SectionName,rws”中的rws表示该节可read,可write,可share
(4)所谓的共享节,就是当这个生成的PE文件实例化以后,下次再实例化时,该SectionName节不再使用CopyOnWrite技术,而是该PE文件的多个实例共享这个节
(5)共享节的这个特性可以让我们实现应用程序的单例模式
(6)我们要区分同一个程序多个实例化共享节,和不同程序各自实例化共享节的区别,同一个程序的多个实例共享节比如A.exe双击两次,生成两个实例,只需要在该A.exe程序中创建共享节即可,但是对于不同程序各自实例化而通信的情况,比如A.exe和B.exe需要通过第三方的dll实现,A.exe和B.exe都打开该dll以后,dll的“SectionName”将被共享,而其他节则继续“CopyOnWrite”
(7)VS2008中对于”/section:SectionName,rws”不允许出现空格,其他IDE没有试验过
三.使用共享节实现进程通信
1.设计
和进程内存读写,由于共享节不具备监听方去主动监听内容变化或者监听方被通知的功能,所以我们这里采用消息+共享节的方式实现通知+数据共享
通信模型如下:
2.实现
(1)首先建立DLL工程SectionDLL,在VS2008中创建Win32工程后,再选择DLL
在SectionDLL.cpp中假如如下代码,实现创建两个共享段“SectionA”和“SectionB”
#pragma data_seg("SectionA")int shareDataA=0;#pragma data_seg() #pragma data_seg("SectionB")int shareDataB=0;#pragma data_seg() #pragma comment(linker,"/section:SectionA,rws") #pragma comment(linker,"/section:SectionB,rws") extern "C" SECTIONDLL_API void setDataA(int Data_In) { shareDataA=Data_In; } extern "C" SECTIONDLL_API int getDataA() { return shareDataA; } extern "C" SECTIONDLL_API void setDataB(int Data_In) { shareDataB=Data_In; } extern "C" SECTIONDLL_API int getDataB() { return shareDataB; }
(2)主进程SectionMain中定义相关消息和函数原型
#define WM_PMCommA (WM_USER+100)#define WM_PMCommB (WM_USER+101)extern "C" { typedef int (*DllGetFun)(); }
(3)主进程SectionMain中映射消息响应函数
ON_MESSAGE(WM_PMCommA, OnMyMessageA) ON_MESSAGE(WM_PMCommB, OnMyMessageB)
其中消息响应函数实现如下(以响应WM_PMCommA为例),其中实现读取共享节SectionA的数据
LRESULT CSectionMainDlg::OnMyMessageA(WPARAM wParam, LPARAM lParam){ if(NULL!=getFunDataA) { int temp=getFunDataA(); CString info; info.Format("A response ,the data is :%d",temp); MessageBox(info,"Info",MB_OK); } else { MessageBox("A response Failed","Info",MB_OK); } return 0;}
(4)主进程SectionMain在OnInitDialog中打开子进程、打开dll、获取函数地址
openSubProcessFlag=false; openSubProcess(); // TODO: Add extra initialization here m_hinstance=LoadLibrary("SectionDLL.dll"); if(m_hinstance!=NULL) { getFunDataA=(DllGetFun)::GetProcAddress( m_hinstance,"getDataA"); getFunDataB=(DllGetFun)::GetProcAddress( m_hinstance,"getDataB"); }
(5)子进程定义相关消息和函数原型,以SectionASub为例
#define WM_PMCommA (WM_USER+100)extern "C" { typedef void (*DllSetFun)(int); }
(6)子进程的OnInitDialog中打开dll、获取函数地址
m_hinstance=LoadLibrary("SectionDLL.dll"); if(m_hinstance!=NULL) { setFunDataA=(DllSetFun)::GetProcAddress( m_hinstance,"setDataA"); }
(7)子进程映射消息响应函数
ON_MESSAGE(WM_PMCommA, OnMyMessageA)
消息响应函数如下
LRESULT CSectionASubDlg::OnMyMessageA(WPARAM wParam, LPARAM lParam){ if(NULL!=setFunDataA) { setFunDataA(10); ::PostMessage(::FindWindow(NULL,"SectionMain"),WM_PMCommA,0,0); } return 0;}
(8)万事俱备,只欠东风,这个东风就是SectionMain中的触发函数
分别向SectionASub和SectionBSub进程Post消息
void CSectionMainDlg::OnBnClickedButton1(){ // TODO: Add your control notification handler code here ::PostMessage(::FindWindow(NULL,"SectionASub"),WM_PMCommA,0,0); ::PostMessage(::FindWindow(NULL,"SectionBSub"),WM_PMCommB,0,0);}
好了一切都连起来了
四.总结
1.通过实验发现,共享节技术更适合同一个程序的多个实例化共享的情况
2.对于不同程序而言,要通过第三方共享,要考虑数据同步问题,维护也比较麻烦
3.当程序要扩展的时候,比如共享的数据格式有所变化的时候,还是比较繁琐的
4.共享节比较适合小数据量共享的情况
5.另一种应用程序层面单例模式实现的可选方案
五.参考
感谢以下博主
1. 再写手工打造可执行程序
http://bbs.pediy.com/showthread.php?t=122191
2. dll和exe的共享节------多进程共享dll/exe全局变量
http://blog.csdn.net/mydreamremindme/article/details/10311075
3. PE文件格式详解
上半部分 http://www.vckbase.com/index.php/wv/1057
下半部分 http://www.vckbase.com/index.php/wv/1058
4. 微软官方PE文件格式说明
http://msdn.microsoft.com/en-us/gg463119.aspx
5. 程序人生博客PE专题
http://www.programlife.net/category/windev/pe-coff-format
danny
2014年8月11号
于天津河西七天酒店
- Windows进程通信之PE文件共享节
- Windows进程间通信之共享内存
- windows编程之进程通信:内存共享
- Windows环境进程间通信(一):共享文件方式
- windows进程间通信之文件映射
- windows进程通信之共享内存那点事
- Windows IPC 进程间通信之共享内存
- 进程通信之共享内存
- 进程通信之共享内存
- 进程通信之共享内存
- 进程通信之共享内存
- 进程通信之共享内存
- 进程通信之共享内存
- 【进程通信】之共享内存
- 进程通信之共享内存
- 进程通信之共享内存
- 进程通信之共享内存
- windows 进程间通信(共享内存)
- Forward: 软件测试与质量保证
- 修改配置真正解决php文件上传大小限制问题(apache+php)
- FFT
- 数据结构—队列
- Phalcon框架在读取和写入数据库时产生中文乱码
- Windows进程通信之PE文件共享节
- MsgQ的对比
- 暑假训练4-数据结构专题
- HDU-1846-Brave Game
- java专题——javaweb开发和j2ee区别
- 【LeetCode】Surrounded Regions
- 法规及虽然放假日特价日是一款
- 发的货收入和热爱加热一体机啊
- 非健康的人员发空间是日语课