不同进程间的共享问题

不同进程间的共享问题一直是编程人员常常需要面对,但又不容易解决的问题。我在写程序的工程中也深受其害,从而想到将自己遇到的情况做一下总结,也就促成了这篇文章。文章中重点论述有关数据和内核句柄的共享问题。
声明:文章中很多技术不是我发明的,而是在很多图书及网站上可以看到,我所做的工作只是在使用工程中做了下总结而已。
     --风小云
-----------------------------------------
多线程是开发过程中经常需要面对的一种技术,然而某些时候我们所遇到的工作不单单局限在一个进程里来完成,相反不同进程间的共享问题同样需要我们来面对。
这篇文章中重点讲述有关数据和内核对象的共享问题。

数据共享
不同进程间共享数据往往是通过在DLL文件中设立共享内存来实现的,结构如下图:
exe1    exe2
-------------
  dateseg    
    DLL
具体的实现方法也就是通过在dll中设立一个共享数据段,这个数据段中的数据可以提供给不同的进程使用,从而达到目的。

共享数据DLL允许进程以类似于Windows 3.1 DLL共享数据的方式访问读写数据,多个进程都可以对该共享数据DLL进行数据操作,达到共享数据的目的。在WIN32中为建立共享内存,必须执行以下步骤:
首先创建一个有名的数据区。这在Visual C++中是使用data_seg pragma宏。使用data_seg pragma宏必须注意数据的初始化:
#pragma data_seg("MYSEC")
char MySharedData[4096]={0};
#pragma data_seg()
然后在用户的DEF文件中为有名的数据区设定共享属性。
LIBRARY TEST
DATA READ WRITE
SECTIONS
.MYSEC READ WRITE SHARED

这样每个附属于DLL的进程都将接受到属于自己的数据拷贝,一个进程的数据变化并不会反映到其他进程的数据中。

在DEF文件中适当地输出数据。以下的DEF文件项说明了如何以常数变量的形式输出MySharedData。
EXPORTS
MySharedData CONSTANT
最后在应用程序(进程)按外部变量引用共享数据。
extern _export"C"{char * MySharedData[];}
进程中使用该变量应注意间接引用。
m_pStatic=(CEdit*)GetDlgItem(IDC_SHARED);
m_pStatic->GetLine(0,*MySharedData,80);

内核对象的共享
许多情况下,在不同进程中运行的线程需要共享内核对象。下面是为何需要共享的原因:
• 文件映射对象使你能够在同一台机器上运行的两个进程之间共享数据块。
• 邮箱和指定的管道使得应用程序能够在连网的不同机器上运行的进程之间发送数据块。
• 互斥对象、信标和事件使得不同进程中的线程能够同步它们的连续运行,这与一个应用
程序在完成某项任务时需要将情况通知另一个应用程序的情况相同。

一般来说对于操作系统的内核对象句柄,比如线程句柄等等它们是不能在进程中进行共享的,也就是说这些句柄只能属于进程私有,句柄本身只是一个32位值,不同的句柄有不同的含义,HBRUSH 、HINSTANCE、HRESULT 都是句柄,但其含义大相径庭,但一般情况下句柄都是标识某对象的(HRESULT就不是),所以一般的句柄是内存空间相关的,而不同的exe其内存空间是无关的,所以共享句柄的值是无意义的,实际上返回的这些句柄的值是进程句柄表中的索引值。

那么如何在不同进程间共享内核对象句柄呢?一般来说,系统为我们提供了三种方法:
1 利用对象句柄的继承性
只有当进程具有父子关系时,才能使用对象句柄的继承性。在这种情况下,父进程可以使
用一个或多个内核对象句柄,并且该父进程可以决定生成一个子进程,为子进程赋予对父进程的内核对象的访问权。。若要使这种类型的继承性能够实现,父进程必须执行若干个操作步骤。
首先,当父进程创建内核对象时,必须向系统指明,它希望对象的句柄是个可继承的句柄。这要求进程必须指定一个S E C U R I T Y _ AT T R I B U T E S结构并对它进行初始化,然后将该结构的地址传递给特定的C r e a t e函数。
然后,是让父进程生成子进程。这要使用C r e a t eP r o c e s s函数来完成。

BOOL CreateProcess(
  LPCTSTR lpApplicationName,                 // name of executable module
  LPTSTR lpCommandLine,                      // command line string
  LPSECURITY_ATTRIBUTES lpProcessAttributes, // SD
  LPSECURITY_ATTRIBUTES lpThreadAttributes,  // SD
  BOOL bInheritHandles,                      // handle inheritance option
  DWORD dwCreationFlags,                     // creation flags
  LPVOID lpEnvironment,                      // new environment block
  LPCTSTR lpCurrentDirectory,                // current directory name
  LPSTARTUPINFO lpStartupInfo,               // startup information
  LPPROCESS_INFORMATION lpProcessInformation // process information
);

可通过将bInheritHandles设置为True来实现。接下来通过这种方法生成的子进程就可以拥有父进程内核句柄的继承权了。
2 命名对象
共享跨越进程边界的内核对象的第二种方法是给对象命名。许多(虽然不是全部)内核对
象都是可以命名的。但当创建一个未命名的对象时,可以通过使用继承性(如上一方法介绍的那样)或D u p l i c a t e H a n d l e (下一方法将要介绍)共享跨越进程的对象。若要按名字共享对象,必须为对象赋予一个名字。
3 复制对象句柄
共享跨越进程边界的内核对象的最后一个方法是使用D u p l i c a t e H a n d l e函数

BOOL DuplicateHandle(
  HANDLE hSourceProcessHandle,  // handle to source process
  HANDLE hSourceHandle,         // handle to duplicate
  HANDLE hTargetProcessHandle,  // handle to target process
  LPHANDLE lpTargetHandle,      // duplicate handle
  DWORD dwDesiredAccess,        // requested access
  BOOL bInheritHandle,          // handle inheritance option
  DWORD dwOptions               // optional actions
);


简单说来,该函数取出一个进程的句柄表中的项目,并将该项目拷贝到另一个进程的句柄
表中。D u p l i c a t e H a n d l e函数配有若干个参数,但是实际上它是非常简单的。D u p l i c a t e H a n d l e函数最普通的用法要涉及系统中运行的3个不同进程。当调用D u p l i c a t e H a n d l e函数时,第一和第三个参数h S o u r c e P r o c e s s H a n d l e和h Ta rg e tP r o c e s s H a n d l e是内核对象句柄。这些句柄本身必须与调用D u p l i c a t e H a n d l e函数的进程相关。此外,这两个参数必须标识进程的内核对象。如果将句柄传递给任何其他类型的内核对象,那么该函数运行就会失败。第4章将详细介绍进程的内核对象,而现在只需要知道,每当系统中启动一个新进程时都会创建一个进程内核对象。
第二个参数h S o u r c e H a n d l e是任何类型的内核对象的句柄。但是该句柄值与调用D u p l i c a t eH a n d l e的进程并无关系。相反,该句柄必须与h S o u r c e P r o c e s s H a n d l e句柄标识的进程相关。
第四个参数p h Ta rg e t H a n d l e是H A N D L E变量的地址,它将接收获取源进程句柄信息拷贝的项目索引。返回的句柄值与h Ta rg e t P r o c e s s H a n d l e标识的进程相关。
D u p l i c a t e H a n d l e的最后3个参数用于指明该目标进程的内核对象句柄表项目中使用的访问屏蔽值和继承性标志。d w O p t i o n s参数可以是0(零),也可以是下面两个标志的任何组合:D U P L I C AT E _ S A M E _ A C C E S S和D U P L I C AT E _ C L O S E _ S O U R C E。如果设定了D U P L I C AT E _ S A M E _ A C C E S S标志,则告诉D u p l i c a t e H a n d l e函数,你希望目标进程的句柄拥有与源进程句柄相同的访问屏蔽。使用该标志将使D u p l i c a t e H a n d l e忽略它的d w D e s i r e d A c c e s s参数。如果设定了D U P L I C AT E _ C L O S E _ S O U R C E标志,则可以关闭源进程中的句柄。该标志使得一个进程能够很容易地将内核对象传递给另一个进程。当使用该标志时,内核对象的使用计数不会受到影响。

上述三种方法的使用都有自己需要的条件,使用之前要先确定自己所具备的条件以及自己所要求达到的目的,以免使用时遇到不必要的错误。
更详细的使用说明你可以参照《Windows核心编程》这本书的第三章中讲述的内容,这里我只是简单提到,希望能对你有所启发!
                                                                                  --风小云原创,转贴请注明出处!

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值