首页 > 代码库 > 进程间通信 - 动态链接库中共享内存(利用DLL的2~3G的地址段空间)

进程间通信 - 动态链接库中共享内存(利用DLL的2~3G的地址段空间)

前言

进程是装入内存并准备执行的程序,每个进程都有私有的虚拟地址空间,由代码、数据,以及其他的一些资源组成。32位系统的进程分配4G的虚拟地址空间。内存地址范围是0x00000000~0xFFFFFFFF。这个内存地址空间是每个进程独立的,也就是说,在一个进程中是不能访问其他进程的地址空间的。

举个例子,进程A的内存里保存了一个数据,假设这个数据的地址是0x33333333。这个是时候,即使把进程B的内存地址是0x33333333的数据读出来。这个数据也和进程A里同地址的数据是不一样的。甚至进程B的0x33333333地址根本就没有数据。

如果想要在多个进程间实现数据共享,这就涉及到进程间通信的问题了。当然,进程间的通信有很多种方法可以实现。比如,动态链接库,动态数据交换,文件映射,管道,剪切板等等。这篇文章主要讲讲利用动态链接库怎么实现进程间通信。

 

实现方法

动态链接库DLL是包含了一系列函数和数据的文件,它可以由应用程序调用其函数。

一般情况下,在应用程序调用一个dll里面的函数的时候,操作系统会将dll的文件映像映射到进程的地址空间中,这个时候dll对于进程中的线程来说只是一些被放在地址进程空间附加的代码和数据,当多个应用程序加载同一个dll的时候,dll在内存中只有一个。这个时候,如果其中一个进程调用DLL的函数,就会在当前进程的线程栈中取得传递给他的参数,并使用线程栈来存放他需要的变量。dll函数创建的任何对象都为调用线程或者调用进程拥有,dll不会拥有任何对象,也就是说如果dll中的一个函数调用了VirtualAlloc,系统会从调用进程的地址空间预定地址。在这种情况下是无法实现动态链接库的数据共享的。如果要想实现数据共享,首先我们来看看下面的进程虚拟空间构成图。

+---------------------------------+ 4 GB

|                  |

|        内核空间      |

|                  |
|                  |
+---------------------------------+ 3 GB
|                  |
|         用户共享空间    |
|                 |
|                 |
+---------------------------------+ 2 GB
|                 |
|        用户私有空间    |
|                 |
|                  |
+---------------------------------+ 0 GB

从这个图可以看出,上面所说的DLL的调用用的都是0GB-2GB的虚拟空间。想要实现共享的话,可以利用2GB-3GB的空间。也就是说,要把数据保存到这个空间里去。这里我们需要用#pragma data_seg()在DLL中定义一个共享的,有名字的数据段。具体怎么实现,我们可以通过下面这个示例来演示。

 

 

演示示例

首先创建一个共享DLL工程,这个DLL就是用来保存共享数据的。

实现步骤

1.新建一个Win32 Project,命名为SharedDll

技术分享

 

2.程序类型选择DLL

技术分享

 

3.工程结构

技术分享

 

4.SharedDll.h文件,主要申明两个函数用来保存共享数据和取得共享数据。

[cpp] view plain copy
 
  1. <span style="font-family:SimSun;font-size:14px;">// The following ifdef block is the standard way of creating macros which make exporting   
  2. // from a DLL simpler. All files within this DLL are compiled with the SHAREDDLL_EXPORTS  
  3. // symbol defined on the command line. this symbol should not be defined on any project  
  4. // that uses this DLL. This way any other project whose source files include this file see   
  5. // SHAREDDLL_API functions as being imported from a DLL, whereas this DLL sees symbols  
  6. // defined with this macro as being exported.  
  7. #ifdef SHAREDDLL_EXPORTS  
  8. #define SHAREDDLL_API __declspec(dllexport)  
  9. #else  
  10. #define SHAREDDLL_API __declspec(dllimport)  
  11. #endif  
  12.   
  13. // This class is exported from the SharedDll.dll  
  14. class SHAREDDLL_API CSharedDll {  
  15. public:  
  16.     CSharedDll(void);  
  17.     // TODO: add your methods here.  
  18. };  
  19.   
  20. // set string to shared dll  
  21. SHAREDDLL_API void SetValueString(LPCSTR str);  
  22.   
  23. // get string from shared dll  
  24. SHAREDDLL_API LPSTR GetValueString();  
  25. </span>  

 

5.SharedDll.cpp文件,用#pragma data_seg预处理指令用于设置共享数据段。

 

[cpp] view plain copy
 
  1. <span style="font-family:SimSun;font-size:14px;">……  
  2. //共享数据申明开始  
  3. #pragma data_seg("SharedData")   
  4. //一定要注意给下面的变量初始化,否则将无法实现数据在多个进程间共享  
  5. char m_strString[256]="";   
  6. //共享数据申明结束       
  7. #pragma data_seg()  
  8. //在连接器里定义共享数据段的权限为可读可写可共享  
  9. #pragma comment(linker,"/SECTION:SharedData,RWS")  
  10.   
  11. // 共享数据的取得接口  
  12. SHAREDDLL_API LPSTR GetValueString()  
  13. {  
  14.     return m_strString;  
  15. }  
  16. // 共享数据的保存接口  
  17. SHAREDDLL_API  void SetValueString(LPCSTR str)  
  18. {  
  19.     strcpy(m_strString,str);  
  20. }  
  21. ……  
  22. </span>  

 

 

到此为止,共享DLL的工程就做好了。接下来做两个MFC应用程序,用来保存数据到共享DLL,或者从共享DLL取得数据。

 

第一个程序:新建一个MCF的Dialog程序,画面如下。编辑框用来输入数据信息,[Submit]按钮用来提交数据到共享DLL。当然工程一定要导入SharedDLL的lib包。

技术分享

 

实现代码如下:

 

[cpp] view plain copy
 
  1. <span style="font-family:SimSun;font-size:14px;">……  
  2. //[Submit]按钮的点击事件  
  3. void CFormADlg::OnBnClickedOk()  
  4. {  
  5.     //从编辑框控件取得输入的数据信息  
  6.     CEdit* e = (CEdit*) this->GetDlgItem(IDC_EDIT1);  
  7.     CString str;  
  8.     e->GetWindowText(str);  
  9.        //调用SharedDLL.dll的SetValueString接口保存数据到共享DLL  
  10.     SetValueString((LPCTSTR)str);  
  11. }  
  12. ……  
  13. </span>  

 

 

第二个程序:和上面的一样,新建一个MCF的Dialog程序,画面如下。编辑框用来显示数据信息,[Display]按钮用来取得共享DLL里保存的数据。这个工程同样需要导入SharedDLL的lib包。

技术分享

 

代码如下:

 

[cpp] view plain copy
 
  1. <span style="font-family:SimSun;font-size:14px;">……  
  2. //[Display]按钮的点击事件  
  3. void CFormBDlg::OnBnClickedOk()  
  4. {  
  5.     //调用SharedDLL.dll的GetValueString接口取得共享数据  
  6.     CString str = (CString)GetValueString();  
  7.         //将取得的数据输出到FormB窗体的编辑框内  
  8.     CEdit* e = (CEdit*) this->GetDlgItem(IDC_EDIT1);  
  9.     e->SetWindowText(str);  
  10. }  
  11. </span>  


这样所有的工程都完成了,接下来看看运行效果。

 

首先,我们把将 SharedDll.dll和FormA.exe和FormB.exe拷贝到同一目录下,方便它们互相访问。

技术分享

 

而后运行FormA.exe和FormB.exe两个程序。

技术分享

 

将数据信息输入FormA窗口的编辑框内,并且按下Submit按钮。

技术分享

 

然后点击FormB窗口的Display按钮

技术分享

 

 

从上面的这个示例可以看出,通过动态链接库能够实现了进程FormA和进程FormB之间的通信。
但是如果使用动态链接库进行进程间通讯需要注意一下几点。
1.    动态链接库的进程间通信是能适用于本地进程间通讯,不可用于跨网络间的通信。
2.    动态链接库的进程间通信是无法实现实时通知的问题。如果想要同时实现实时通知的问题,可以配合一些同步方法包括:Event, Mutex, 信号量Semaphore, 临界区资源等一起使用。
3.    想要用动态链接库的进程间通信,必须需要双方进程导入这个动态链接库。

 

具体怎么用,是否能用还需要具体情况具体对待。

 

http://blog.csdn.net/mickeyty/article/details/51721860

进程间通信 - 动态链接库中共享内存(利用DLL的2~3G的地址段空间)