Windows进程通信之PE文件共享节

来源:互联网 发布:数控滚齿机编程 编辑:程序博客网 时间:2024/06/15 04:19

本文由danny发表于 http://blog.csdn.net/danny_share

 

说明:建议先下载本文配套工程,其中

SectionDLLSectionMain工程、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.常见节

1Windows是通过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.exeB.exe需要通过第三方的dll实现,A.exeB.exe都打开该dll以后,dll的“SectionName”将被共享,而其他节则继续“CopyOnWrite

7VS2008中对于”/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中的触发函数

分别向SectionASubSectionBSub进程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. dllexe的共享节------多进程共享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号

于天津河西七天酒店

0 0
原创粉丝点击