不同進程間的共用問題一直是編程人員常常需要面對,但又不容易解決的問題。我在寫程式的工程中也深受其害,從而想到將自己遇到的情況做一下總結,也就促成了這篇文章。文章中重點論述有關資料和核心控制代碼的共用問題。
聲明:文章中很多技術不是我發明的,而是在很多圖書及網站上可以看到,我所做的工作只是在使用工程中做了下總結而已。
--風小雲
-----------------------------------------
多線程是開發過程中經常需要面對的一種技術,然而某些時候我們所遇到的工作不單單局限在一個進程裡來完成,相反不同進程間的共用問題同樣需要我們來面對。
這篇文章中重點講述有關資料和核心對象的共用問題。
資料共用
不同進程間共用資料往往是通過在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核心編程》這本書的第三章中講述的內容,這裡我只是簡單提到,希望能對你有所啟發!
--風小雲原創,轉貼請註明出處!