事件和进程间的数据交换 .

简介: <p>//========================================================================<br> //TITLE:<br> //    事件和进程间的数据交换<br> //AUTHOR:<br> //    norains<br> //DATE:<br> //    Monday  13-July-2009<br

//========================================================================
//TITLE:
//    事件和进程间的数据交换
//AUTHOR:
//    norains
//DATE:
//    Monday  13-July-2009
//Environment:
//    WINCE5.0 + VS2005
//========================================================================
    多线程的数据交流不难,毕竟是同属于一个进程,更为重要的是,代码还很可能同属于一个工程,基本上想怎么干就怎么干,大不了我用一个全局变量来做缓存来进行数据的交换。
   
    但对于多进程来说,就没那么容易了。从代码的角度来说,多进程意味不同的工程,意味着不能简单通过全局变量来交流数据。试想一下,如果IE有一个全局变量g_iCurPage,你用什么方法才能得到该数据?因此,在多进程的情况下,多线程那一套就没辙了。
   
    不过,如果只是交流数据,情况倒不显得那么糟糕。一般的流程无非就是:假设有两个,分别是进程A和进程B,当线程A改变某些数值时,它会通过发送相应的事件给进程B;进程B在获得该通知事件后,会采取一定的方式,读取进程A所改变的数值。

    听起来是不是很简单?
   
    在讨论这个问题之前,我们先假设这两个进程存在如下架构的代码。

  1. 进程A:  
  2. DWORD NotifyProc(LPVOID pParam)  
  3. {  
  4.  while(TRUE)  
  5.  {  
  6.   if(IsDataChange() != FALSE)  
  7.   {  
  8.    //TODO:准备好传送的数据   
  9.      
  10.    PulseEvent(hEventNotify);         
  11.   }  
  12.  }  
  13. }  
  14.   
  15. 进程B:  
  16. DWORD WaitNotifyProc(LPVOID pParam)  
  17. {  
  18.  while(TRUE)  
  19.  {  
  20.   DWORD dwReturn = WaitForSingleObject(hEventNotify);  
  21.   if(dwReturn != WAIT_TIMEOUT)  
  22.   {  
  23.    //TODO:获取相应的数据   
  24.   }  
  25.  }  
  26. }  
进程A: DWORD NotifyProc(LPVOID pParam) { while(TRUE) { if(IsDataChange() != FALSE) { //TODO:准备好传送的数据 PulseEvent(hEventNotify); } } } 进程B: DWORD WaitNotifyProc(LPVOID pParam) { while(TRUE) { DWORD dwReturn = WaitForSingleObject(hEventNotify); if(dwReturn != WAIT_TIMEOUT) { //TODO:获取相应的数据 } } }

 

    从这段简单的代码之中,我们可以知道有这么两个难点,首先是进程A如何准备数据,其次便是进程B如何获取进程A准备的数据。
   
    接下来要论述的方式,都是基于这个框架的两个难点来讨论。   
   
    一般的做法,无非有三种。   
   
1).注册表
   
    采用该方式后完善的代码如下:

  1. 进程A:  
  2. DWORD NotifyProc(LPVOID pParam)  
  3. {  
  4.  while(TRUE)  
  5.  {  
  6.   if(IsDataChange() != FALSE)  
  7.   {  
  8.    //更改相应的注册表数值   
  9.    CReg reg;  
  10.    reg.Create(HKEY_CURRENT_USER,DEVICE_INFO);  
  11.    reg.SetDW(MAIN_VOLUME,dwVal);  
  12.    reg.Close();  
  13.      
  14.    //发送通知事件   
  15.    PulseEvent(hEventNotify);         
  16.   }  
  17.  }  
  18. }  
  19.   
  20. 进程B:      
  21. DWORD WaitNotifyProc(LPVOID pParam)  
  22. {  
  23.  while(TRUE)  
  24.  {  
  25.   //等待通知事件   
  26.   DWORD dwReturn = WaitForSingleObject(hEventNotify);  
  27.     
  28.   if(dwReturn != WAIT_TIMEOUT)  
  29.   {  
  30.    //读取注册表   
  31.    CReg reg;  
  32.    reg.Create(HKEY_CURRENT_USER,DEVICE_INFO);  
  33.    DWORD dwVal = 0;  
  34.    dwVal = reg.GetDW(MAIN_VOLUME);  
  35.   }  
  36.  }  
  37. }  
进程A: DWORD NotifyProc(LPVOID pParam) { while(TRUE) { if(IsDataChange() != FALSE) { //更改相应的注册表数值 CReg reg; reg.Create(HKEY_CURRENT_USER,DEVICE_INFO); reg.SetDW(MAIN_VOLUME,dwVal); reg.Close(); //发送通知事件 PulseEvent(hEventNotify); } } } 进程B: DWORD WaitNotifyProc(LPVOID pParam) { while(TRUE) { //等待通知事件 DWORD dwReturn = WaitForSingleObject(hEventNotify); if(dwReturn != WAIT_TIMEOUT) { //读取注册表 CReg reg; reg.Create(HKEY_CURRENT_USER,DEVICE_INFO); DWORD dwVal = 0; dwVal = reg.GetDW(MAIN_VOLUME); } } }

 

    该方法灵活性非常高,进程A如果想增加更多的通知数据,只需要简单地多设注册表项。而进程B可以不用管进程A设置了多少注册表项,只需要获取自己所需要的项目即可。

    另外一个更为明显的优势在于,由于该方法是将数据保存于注册表,所以在进程B的运行是在进程A退出之后,进程B还能获取数据。甚至于,机器重启后,进程B依然能获取相应数据——前提条件是系统的注册表为Hive Registry。

    如果说缺陷,确切地说是相对于另外两种方法而言,便是速度。因为期间会对注册表进行读写,所以速度会略有损失。如果对速度非常在意,那么该方法并不是最理想的。


2).内存印射

    其实这种方式在我blog的另一篇文章《进程间的数据共享》(http://blog.csdn.net/norains/archive/2008/07/16/2663390.aspx)有提过,但为了本篇的完整性,在这里根据我们的论述框架重新来讨论一次。
   
    原理很简单,进程A先调用CreateFileMapping开辟一个印射的内存区,然后往里面拷贝数据,最后通知进程B读取数据;进程B接受通知时,就直接调用memcpy从内存中获取数据。

  1. 进程A:  
  2. DWORD NotifyProc(LPVOID pParam)  
  3. {  
  4.  //创建内存文件印射   
  5.  HANDLE hFile = CreateFileMapping((HANDLE)-1,NULL,PAGE_READWRITE,0,MEM_SIZE,MEM_SHARE_NAME);  
  6.    
  7.  VOID * pMem = NULL;  
  8.  if(hFile != NULL)  
  9.  {  
  10.   pMem = MapViewOfFile(hFile,FILE_MAP_ALL_ACCESS,0,0,0);  
  11.  }  
  12.   
  13.  while(TRUE)  
  14.  {  
  15.   if(IsDataChange() != FALSE)  
  16.   {  
  17.    //拷贝传输的数据   
  18.    if(pMem != NULL)  
  19.    {  
  20.     memcpy(pMem,&dwValue,sizeof(dwValue));  
  21.    }  
  22.      
  23.    PulseEvent(hEventNotify);         
  24.   }  
  25.  }  
  26.    
  27.  //如果不再使用,应该关闭句柄   
  28.  CloseHandle(hFile);  
  29.   
  30. }  
  31.   
  32. 进程B:  
  33. DWORD WaitNotifyProc(LPVOID pParam)  
  34. {  
  35.  //创建内存文件印射   
  36.  HANDLE hFile = CreateFileMapping((HANDLE)-1,NULL,PAGE_READWRITE,0,MEM_SIZE,MEM_SHARE_NAME);  
  37.    
  38.  VOID * pMem = NULL;  
  39.  if(hFile != NULL)  
  40.  {  
  41.   pMem = MapViewOfFile(hFile,FILE_MAP_ALL_ACCESS,0,0,0);  
  42.  }  
  43.    
  44.  while(TRUE)  
  45.  {  
  46.   DWORD dwReturn = WaitForSingleObject(hEventNotify);  
  47.   if(dwReturn != WAIT_TIMEOUT)  
  48.   {  
  49.    //拷贝传输过来的数据   
  50.    if(pMem != NULL)  
  51.    {  
  52.     memcpy(&dwValue,pMem,sizeof(dwValue));  
  53.    }  
  54.   }  
  55.  }  
  56.    
  57.  //如果不再使用,应该关闭句柄   
  58.  CloseHandle(hFile);  
  59. }  
进程A: DWORD NotifyProc(LPVOID pParam) { //创建内存文件印射 HANDLE hFile = CreateFileMapping((HANDLE)-1,NULL,PAGE_READWRITE,0,MEM_SIZE,MEM_SHARE_NAME); VOID * pMem = NULL; if(hFile != NULL) { pMem = MapViewOfFile(hFile,FILE_MAP_ALL_ACCESS,0,0,0); } while(TRUE) { if(IsDataChange() != FALSE) { //拷贝传输的数据 if(pMem != NULL) { memcpy(pMem,&dwValue,sizeof(dwValue)); } PulseEvent(hEventNotify); } } //如果不再使用,应该关闭句柄 CloseHandle(hFile); } 进程B: DWORD WaitNotifyProc(LPVOID pParam) { //创建内存文件印射 HANDLE hFile = CreateFileMapping((HANDLE)-1,NULL,PAGE_READWRITE,0,MEM_SIZE,MEM_SHARE_NAME); VOID * pMem = NULL; if(hFile != NULL) { pMem = MapViewOfFile(hFile,FILE_MAP_ALL_ACCESS,0,0,0); } while(TRUE) { DWORD dwReturn = WaitForSingleObject(hEventNotify); if(dwReturn != WAIT_TIMEOUT) { //拷贝传输过来的数据 if(pMem != NULL) { memcpy(&dwValue,pMem,sizeof(dwValue)); } } } //如果不再使用,应该关闭句柄 CloseHandle(hFile); }

 

    该方法是最复杂的,两个进程不仅协同设置内存的大小(MEM_SIZE),还要设置同样的名称(MEM_SHARE_NAME),更要判断该内存是否能分配成功。相对的,灵活性也是最高的,只要以上问题协商解决,则什么数据类型都能传递,无论是DWORD,或是struct。当然,我们还是不能传递对象指针,因为简单地传递对象指针,则基本上都会引发内存访问违例的致命错误。

3).设置事件数据

    相对于以上两种方式,该方式彻头彻尾只能属于轻量级。因为它方式最为简单,同样,所传递的数据也最少。
   
    其原理很简单,进程A通过SetEventData设置和事件关联的数据,然后发送事件通知进程B;进程B接收到进程A的事件以后,则通过GetEventData来获取数据。根据该原理,则代码的样式可以如下:

  1. 进程A:  
  2. DWORD NotifyProc(LPVOID pParam)  
  3. {  
  4.  while(TRUE)  
  5.  {  
  6.   if(IsDataChange() != FALSE)  
  7.   {  
  8.    //设置关联数据   
  9.    SetEventData(hEventNotify,dwData);  
  10.      
  11.    PulseEvent(hEventNotify);         
  12.   }  
  13.  }  
  14. }  
  15.   
  16. 进程B:  
  17. DWORD WaitNotifyProc(LPVOID pParam)  
  18. {  
  19.  while(TRUE)  
  20.  {  
  21.   DWORD dwReturn = WaitForSingleObject(hEventNotify);  
  22.   if(dwReturn != WAIT_TIMEOUT)  
  23.   {  
  24.    //获取关联数据   
  25.    dwData = GetEventData(hEventNotify);  
  26.   }  
  27.  }  
  28. }  
进程A: DWORD NotifyProc(LPVOID pParam) { while(TRUE) { if(IsDataChange() != FALSE) { //设置关联数据 SetEventData(hEventNotify,dwData); PulseEvent(hEventNotify); } } } 进程B: DWORD WaitNotifyProc(LPVOID pParam) { while(TRUE) { DWORD dwReturn = WaitForSingleObject(hEventNotify); if(dwReturn != WAIT_TIMEOUT) { //获取关联数据 dwData = GetEventData(hEventNotify); } } }

 

    该方式是最简单的,在传递DWORD长度类型的数据有得天独厚的优势,无论是速度还是简便性。但,也仅限于此,如果想采用该方式传递大小大于DWORD的数值,基本上会丢失精度,更不用说struct等结构体数值了。不过,这却是这三种方法之中,和事件联系最为紧密的。

相关文章
|
1天前
|
SQL 关系型数据库 MySQL
定时任务频繁插入数据导致锁表问题 -> 查询mysql进程
定时任务频繁插入数据导致锁表问题 -> 查询mysql进程
9 1
|
3月前
|
数据采集 Java Python
python 递归锁、信号量、事件、线程队列、进程池和线程池、回调函数、定时器
python 递归锁、信号量、事件、线程队列、进程池和线程池、回调函数、定时器
|
4月前
|
Python
在Python中,`multiprocessing`模块提供了一种在多个进程之间共享数据和同步的机制。
在Python中,`multiprocessing`模块提供了一种在多个进程之间共享数据和同步的机制。
后端登录接口使用postman,无法接收返回数据,怎样解决,认真比较与原项目的代码,看看有没有写的不一样的,问题就能解决,不要多少写,根据postman的提示先找到错误的进程,看错误进程出现在那个进程
后端登录接口使用postman,无法接收返回数据,怎样解决,认真比较与原项目的代码,看看有没有写的不一样的,问题就能解决,不要多少写,根据postman的提示先找到错误的进程,看错误进程出现在那个进程
|
6月前
|
存储 缓存 Linux
xenomai内核解析--xenomai与普通linux进程之间通讯XDDP(三)--实时与非实时数据交互
本文介绍了Xenomai中的XDDP(Xenomai Distributed Data Protocol)通信机制,XDDP用于实时和非实时进程之间的数据交换。XDDP在Xenomai内核中涉及的数据结构和管理方式,以及创建XDDP通道后的实时端和非实时端连接过程。
266 0
xenomai内核解析--xenomai与普通linux进程之间通讯XDDP(三)--实时与非实时数据交互
|
6月前
|
负载均衡 网络协议 中间件
掌握 SOME/IP :访问进程数据 构建高效通信系统的关键技术
掌握 SOME/IP :访问进程数据 构建高效通信系统的关键技术
320 2
|
6月前
|
数据处理 数据安全/隐私保护
智能推荐映射关系,加速数据标准落地进程
在V4.0版本中,Dataphin推出了智能推荐映射关系功能,用户可以基于内置特征或创建自定义特征,对数据内容进行表示,并将其与数据标准关联,进而智能映射映射关系,尤其在字段分布广泛和命名多变的情况下,可以提高映射的准确性和效率,加速了数据标准实施。
298 0
|
6月前
|
存储 JSON 运维
【运维】Powershell 服务器系统管理信息总结(进程、线程、磁盘、内存、网络、CPU、持续运行时间、系统账户、日志事件)
【运维】Powershell 服务器系统管理信息总结(进程、线程、磁盘、内存、网络、CPU、持续运行时间、系统账户、日志事件)
141 0
|
11月前
|
存储 数据安全/隐私保护 Windows
4.5 Windows驱动开发:内核中实现进程数据转储
多数ARK反内核工具中都存在驱动级别的内存转存功能,该功能可以将应用层中运行进程的内存镜像转存到特定目录下,内存转存功能在应对加壳程序的分析尤为重要,当进程在内存中解码后,我们可以很容易的将内存镜像导出,从而更好的对样本进行分析,当然某些加密壳可能无效但绝大多数情况下是可以被转存的。
66 0
4.5 Windows驱动开发:内核中实现进程数据转储
|
缓存 API 数据安全/隐私保护
10.3 调试事件转存进程内存
我们继续延申调试事件的话题,实现进程转存功能,进程转储功能是指通过调试API使获得了目标进程控制权的进程,将目标进程的内存中的数据完整地转存到本地磁盘上,对于加壳软件,通常会通过加密、压缩等手段来保护其代码和数据,使其不易被分析。在这种情况下,通过进程转储功能,可以将加壳程序的内存镜像完整地保存到本地,以便进行后续的分析。在实现进程转储功能时,主要使用调试API和内存读写函数。具体实现方法包括:以调试方式启动目标进程,将其暂停在运行前的位置;让目标进程进入运行状态;使用ReadProcessMemory函数读取目标进程内存,并将结果保存到缓冲区;将缓冲区中的数据写入文件;关闭目标进程的调试状态
87 0
10.3 调试事件转存进程内存

相关实验场景

更多