小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

《windows核心編程系列》三談談內(nèi)核對象及句柄的本質(zhì)

 求真我 2014-04-20

2012、8、29

http://blog.csdn.net/ithzhang/article/details/7920204轉載請注明出處!

                             內(nèi)核對象

    本章討論的是相對抽象的概念,不涉及任何具體的內(nèi)核對象的細節(jié)而是討論所有內(nèi)核對象的共有特性。

首先讓我們來了解一下什么是內(nèi)核對象。內(nèi)核對象通過API來創(chuàng)建,每個內(nèi)核對象是一個數(shù)據(jù)結構,它對應一塊內(nèi)存,由操作系統(tǒng)內(nèi)核分配,并且只能由操作系統(tǒng)內(nèi)核訪問。在此數(shù)據(jù)結構中少數(shù)成員如安全描述符和使用計數(shù)是所有對象都有的,但其他大多數(shù)成員都是不同類型的對象特有的。內(nèi)核對象的數(shù)據(jù)結構只能由操作系統(tǒng)提供的API訪問,應用程序在內(nèi)存中不能訪問。調(diào)用創(chuàng)建內(nèi)核對象的函數(shù)后,該函數(shù)會返回一個句柄,它標識了所創(chuàng)建的對象。它可以由進程的任何線程使用。在32位系統(tǒng)中,句柄是一個32位值。64位系統(tǒng)中則是64位值。

 

很多人對句柄到底是什么東西很疑惑。有人說是指針有人說是索引。其實句柄僅僅是獨立于每個進程的句柄表的一個索引。在每個進程中都存在一個句柄表,列出了所有本進程內(nèi)可以使用的句柄

 

。它只是一個有數(shù)據(jù)結構組成的數(shù)組,每個結構都包含一個指向內(nèi)核對象的指針、訪問掩碼、繼承標識等,而句柄僅僅是句柄表數(shù)組的下標。由于每個進程都存在句柄表,因此句柄是獨立于進程的,雖然將一個進程的句柄傳給另一個進程不一定會失敗,但是它引用的是另一個進程完全不同的內(nèi)核對象。后面的跨進程邊界共享內(nèi)核對象將介紹如何跨界成共享內(nèi)核對象。

 

    內(nèi)核對象的所有者是操作系統(tǒng)內(nèi)核,而非進程。也就是說多個進程可以共享一個內(nèi)核對象。內(nèi)核對象數(shù)據(jù)結構內(nèi)有一個使用計數(shù)成員,它是所有對象都有的一個成員,標識該內(nèi)核對象被引用的次數(shù)。剛創(chuàng)建時使用計數(shù)被初始化為1,如果有另一個進程獲得對此內(nèi)核對象的訪問后,使用計數(shù)就會遞增。一個使用此內(nèi)核對象的進程終止后或是對此內(nèi)核對象調(diào)用CloseHandle,操作系統(tǒng)內(nèi)核會自動遞減內(nèi)核對象的使用計數(shù)。一旦計數(shù)變?yōu)?span style="font-family:Times New Roman">0,操作系統(tǒng)內(nèi)核就會銷毀對象。

 

    安全描述符用以描述內(nèi)核對象的安全性。它描述了內(nèi)核對象的擁有者,那組用戶可以訪問此對象,那組用戶無訪問權限。安全描述符對應一個數(shù)據(jù)結構:SECURITY_ATTRIBUTES結構,幾乎內(nèi)核對象在創(chuàng)建時都需要傳入此結構,但是大部分情況下都是傳入NULL,表示使用默認的安全性。

 

    除了使用內(nèi)核對象,應用程序還需要使用其他類型的對象,如菜單、窗口、鼠標光標等,這些屬于用戶對象或GDI對象,而非內(nèi)核對象。要判斷一個對象是不是內(nèi)核對象,最簡單的方法就是查看創(chuàng)建這個對象的函數(shù),幾乎所有的創(chuàng)建內(nèi)核對象的函數(shù)都需要指定安全屬性信息的參數(shù),而用于創(chuàng)建用戶對象的函數(shù)都不需要使用安全描述符。

 

    一個進程在剛被創(chuàng)建時,它的句柄表是空的。當進程內(nèi)的一個線程調(diào)用創(chuàng)建內(nèi)核對象的函數(shù)時,內(nèi)核將為這個對象分配并初始化一個內(nèi)存塊,然后掃描進程的句柄表,查找一個空白的記錄項,并對其進行初始化。指針成員將會被初始化為內(nèi)核對象的地址,繼承標志也會被設置。

 

    用于創(chuàng)建內(nèi)核對象的函數(shù)都會返回一個與進程相關的句柄,此句柄可由屬于該進程的所有線程引用。調(diào)用一個函數(shù),如果它需要一個內(nèi)核對象句柄的參數(shù),就必須為它傳遞一個句柄。在內(nèi)部,這個函數(shù)會查找進程的句柄表,獲得目標內(nèi)核對象的地址然后對此數(shù)據(jù)結構進行操作。如果我們直接使用其他進程的的句柄,那么實際引用的只是那個進程句柄表中位于同一索引的內(nèi)核對象,它們僅僅是索引值相同而已。創(chuàng)建內(nèi)核對象的函數(shù)在失敗時會返回NULL。但有時也有的函數(shù)會返回-1,如CreateFile,它返回的是INVALID_HANDLE_VALUE而不是NULL。失敗的原因可能是內(nèi)存不足或是沒有權限。這在檢查內(nèi)核對象是否創(chuàng)建成功時要特別注意。

 

    當進程不再使用某內(nèi)核對象時應該調(diào)用CloseHandle來向系統(tǒng)表明我們已經(jīng)結束使用此對象。在內(nèi)部該函數(shù)會掃描進程的句柄表,如果句柄是有效的,系統(tǒng)就獲得此內(nèi)核對象的數(shù)據(jù)結構的地址,并將此結構的使用計數(shù)成員遞減1。如果使用計數(shù)變?yōu)?span style="font-family:Times New Roman">0,句柄表對應的記錄項將會被清除,內(nèi)核對象將被銷毀,所占內(nèi)存將會被釋放。此后再在此進程內(nèi)使用此句柄將會發(fā)生未知錯誤。因為調(diào)用CloseHandle后此內(nèi)核對象不知是否已經(jīng)被銷毀,如果沒有銷毀那么此次對此句柄的使用將沒有問題。如果此內(nèi)核對象已被銷毀,且句柄表對應項已經(jīng)被其他項占據(jù),此時操作的將是另一個內(nèi)核對象,可能發(fā)生無法預知的錯誤。因此在調(diào)用CloseHandle后要最好將原來的變量賦值為NULL

 

    即使在進程結束了對內(nèi)核對象的訪問后,沒有調(diào)用CloseHandle,進程終止時,操作系統(tǒng)也會確保進程所使用的所有資源都被釋放。系統(tǒng)自動掃描進程句柄表,將所有內(nèi)核對象的使用計數(shù)都減1。同樣如存在使用計數(shù)為0的內(nèi)核對象,它就會被釋放。但是這畢竟不是個好習慣,如果我們開發(fā)的程序是長時間運行的程序,由于沒有主動調(diào)用CloseHandle,進程已經(jīng)不再使用的內(nèi)核對象仍然得不到釋放,越往后運行系統(tǒng)所占內(nèi)存就越大。這跟內(nèi)存泄露很類似,自己開辟的堆空間不再使用時要自己主動釋放。對于內(nèi)核對象這也同樣適用。在任務管理器的Handle列可以查看每個進程占用的內(nèi)核對象數(shù)。

 

    前面說到另一個進程不能直接使用一個進程的內(nèi)核對象的句柄。注意這里使用直接二字,這并不意味著其他進程不能使用此內(nèi)核對象,雖然句柄是獨立于進程的,但是內(nèi)核對象是歸系統(tǒng)內(nèi)核所有,各個進程都可以使用,只是要使用還需要費一番周折。接下來將介紹跨進程邊界共享內(nèi)核對象。

 

    跨進程共享內(nèi)核對象是必要的,1:是利用文件映射對象可以在兩個進程間共享數(shù)據(jù)塊。2:互斥量、信號量和事件允許不同進程的線程同步執(zhí)行。

 

 

    跨進程共享內(nèi)核對象方法之一:使用對象句柄繼承

 

 

    只有進程之間屬于父子關系時才可以使用對象句柄繼承。當父進程創(chuàng)建一個內(nèi)核對象時,父進程必須向系統(tǒng)指出它希望這個對象的句柄是可繼承的。為了創(chuàng)建可繼承句柄父進程必須分配并初始化一個SECURITY_ATTRIBUTES結構,并將這個結構的地址傳遞給Create*函數(shù)。如:

SECURITY_ATTRIBUTES sa;

sa.nLength=sizeof(sa);

sa.lpSecurityDescriptor=NULL;//使用默認安全性。

sa.bInheritHandle=TRUE;//是此句柄可以繼承。

HANDLE mutex=CeattMutex(&sa,FALSE,NULL);

 

    以上代碼初始化了一個SECURITY_ATTRIBUTES結構,表明使用默認安全性來創(chuàng)建此對象,且返回的對象時可繼承的。

 

 

    句柄表的每個記錄中還有一個指明該句柄是否可繼承的標志位,如果在創(chuàng)建內(nèi)核對象的時候將NULL作為PSECURITY_ATTRIBUTES的參數(shù)傳入,則返回的句柄是不可繼承的,標志位為0。

 

下一步是由父進程創(chuàng)建子進程,這是通過CreateProcess實現(xiàn)的,此函數(shù)第四章會詳細介紹,此處僅僅注意bInheritHandles參數(shù)。如果在創(chuàng)建進程時,此參數(shù)被設為false,則表明不希望子進程繼承父進程句柄表中的可繼承句柄。如為true,則表明希望子進程繼承父進程句柄表中的可繼承句柄。注意只有可繼承句柄才可以被繼承。

 

新創(chuàng)建的進程句柄表為空,由于我們希望它繼承父進程句柄表,此時系統(tǒng)會遍歷父進程句柄表,對它的每一個項進行檢查,將所有的可繼承的句柄的項全部復制到子進程的句柄表中。在子進程的句柄表中,復制項的位置與它在父進程句柄表中的位置是完全一樣的,這是非常重要的。它意味著在父進程和子進程中,對一個內(nèi)核對象進行標識的句柄是完全一樣的。除了復制句柄表,系統(tǒng)還會遞增每個可繼承句柄的使用計數(shù)。為了銷毀內(nèi)核對象,父進程和子進程必須都不再使用才可以。這可以通過CloseHandle和進程終止來實現(xiàn)。注意:句柄進程僅僅發(fā)生在進程剛被創(chuàng)建時,如果此后父進程又創(chuàng)建了新的內(nèi)核對象,那么此時子進程不會繼承這些新創(chuàng)建的內(nèi)核對象句柄。

 

    如果父進程創(chuàng)建了一個內(nèi)核對象,得到一個不可繼承的句柄,但是后來父進程又希望后來創(chuàng)建的子進程繼承它,這怎么辦呢?這可以通過使用SetHandleInformation修改內(nèi)核對象句柄的繼承標志 。它需三個參數(shù),第一個標識了一個有效句柄,第二個標識想更改哪些標識。第三個標識指出想把它設成什么。這個標識可以是

HANDLE_FLAG_INHERI,//打開句柄繼承標識。

HANDLE_FLAG_PROTECT_FROM_CLOSE//不允許關閉句柄。

    GetHandleInformation可以用來返回指定句柄的當前標識。

 

 

    跨進程共享內(nèi)核對象方法之二:命名對象

 

 

    許多對象都可以進行命名,但并不是全部。因此該方法有一定局限性。有些創(chuàng)建內(nèi)核對象的函數(shù)都有一個指定內(nèi)核對象名稱的參數(shù),如果傳入NULL,則會創(chuàng)建一個匿名的內(nèi)核對象。如果不為NULL,則應該傳入一個一'\0'結尾的字符串。所有這些命名對象共享一個名字空間。即使它們類型不同,如果已存在同名對象,創(chuàng)建就會失敗。

 

    一旦一個命名的內(nèi)核對象被創(chuàng)建,其他進程(不僅僅是子進程)可以通過調(diào)用Open*或是Create*函數(shù)來訪問它。當使用Create*函數(shù)時,系統(tǒng)會檢查是否存在一個傳給此函數(shù)的名字,如果確實存在一個這樣的對象,內(nèi)核執(zhí)行安全檢查,驗證調(diào)用者是否有足夠的安全權限。如果是,系統(tǒng)就會在此進程的句柄表中查找空白記錄項,并將其初始化為指向已存在的命名的內(nèi)核對象。兩個進程的句柄不一定相同,這沒有任何影響。由于內(nèi)核對象被再一次引用,所以其引用計數(shù)會被遞增。

 

    為了防止在創(chuàng)建一個命名對象時,僅僅打開了一個現(xiàn)有的而不是新建的,可以在創(chuàng)建后調(diào)用GetLastError獲得詳細信息。

 

    使用Open*函數(shù)可以打開已存在的命名內(nèi)核對象,如果沒有找到這個名稱的內(nèi)核對象將返回NULL。如果找到這個名稱的內(nèi)核對象,但類型不同,函數(shù)仍返回NULL。只有當名稱相同且類型相同的情況下系統(tǒng)才會進一步檢查訪問權限。如果有權訪問,系統(tǒng)就會更新此進程的句柄表,并遞增內(nèi)核對象的引用計數(shù)。在Open*函數(shù)中也可以指定此句柄的繼承性。

 

    Open*和Create*的區(qū)別:如果對象不存在,Create*會創(chuàng)建它,Open*將會調(diào)用失敗。

 

    我們經(jīng)常使用命名的內(nèi)核對象來防止運行一個程序的多個實例。可以在main函數(shù)中建立一個命名對象,返回后調(diào)用GetLastError如果GetLastError返回ERROR_ALREADY_EXISTS表明此程序的另一個實例在運行。

 

     關于終端服務命名空間不再介紹,只需知道它是為了防止命名內(nèi)核對象命名沖突而設計的。以后有需要的可以仔細研究下。

 

 

    跨進程共享內(nèi)核對象方法之三:復制對象句柄

 

 

    實現(xiàn)該方法使用的是Duplicatehandle函數(shù)。

bool DuplicateHandle(

   HANDLE hSourceProcessHandle,

HANDLE hSourceHandle,

HANDLE hTargetProcessHandle,

PHANDLE phTargethandle

DWORD ddwDesiredAccess,

BOOL bInheritHandle,

DWORD dwOptions

);

 

這個函數(shù)的功能就是獲得進程句柄表的一個記錄項,然后在另一個進程中創(chuàng)建這個記錄項的副本。第一個和第三個參數(shù)分別標識源進程和目標進程內(nèi)核對象句柄。第二個參數(shù)標識要復制的內(nèi)核對象句柄,它可以指向任何類型的內(nèi)核對象。第四個參數(shù)是一個目標句柄的地址,用來接收復制到的HANDLE值。

 

函數(shù)將源進程中的句柄信息復制到目標進程所標識的句柄表中。第五第六個參數(shù)用以指定此內(nèi)核對象句柄在目標進程句柄表中應該使用何種訪問掩碼和繼承標志。

 

dwOption參數(shù)可以是DUPLICATE_SAME_ACCESSDUPLICATE_CLOSE_SOURCE任一個。如果是DUPLICATE_SAME_ACCESS標志,將向DuplicateHandle函數(shù)表明我們希望目標句柄擁有與源進程句柄一樣的訪問掩碼,此時會忽略dwDesiredAccess。如果是DUPLICATE_CLOSE_SOURCE標志,會關閉源進程的句柄,此時將一個內(nèi)核對象從一個進程復制到另一個進程,但是內(nèi)核對象的使用計數(shù)不受影響。

 

     GetCurrentProcess可以返回當前進程的句柄,但是它是一個偽句柄。其值為-1,GetCurrentThread返回的也是偽句柄其值為-2,它們并不在句柄表中而僅僅代表當前進程和當前線程。

     這一章很抽象,原來學習的時候讀了很多遍也不是很明白,后來干脆跳過去了,一段時間的學習之后再回來看看,發(fā)現(xiàn)竟然非常簡單。所以有時候學習不能鉆牛角尖該跳過就跳過。隨著學習的深入,你所站的高度、看問題的角度都會不一樣。理解起來也會更容易?。?!

 

    本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權內(nèi)容,請點擊一鍵舉報。
    轉藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多