推动Windows的限制:句柄
這是我推出Windows??系列的第五篇文章,在這篇文章中??,我探索了Windows管理的資源的數量和大小上限,例如物理內存,虛擬內存,進程和線程。這是整個推動限制系列的索引。雖然他們可以獨立存在,但他們認為你是按順序閱讀的。
推動Windows的限制:物理內存
推動Windows的限制:虛擬內存
推動Windows的限制:分頁和非分頁池
推動Windows的限制:進程和線程
推動Windows的限制:句柄
推動Windows的限制:USER和GDI對象 - 第1部分
推動Windows的限制:USER和GDI對象 - 第2部分
這一次我要去執行句柄來查找和解釋它們的極限。句柄是表示與應用程序交互的基本操作系統對象的開放實例的數據結構,例如文件,注冊表鍵,同步原語和共享內存。有兩個與進程可以創建的句柄數量相關的限制:系統為進程設置的句柄的最大數量,以及可用于存儲句柄的內存量以及應用程序正在使用其句柄引用的對象的數量。
在大多數情況下,句柄的限制遠遠超出了典型的應用程序或系統所使用的范圍。但是,沒有考慮到限制的應用程序可能會以開發人員無法預料的方式推動應用程序。出現更常見的一類問題是因為這些資源的生存期必須由應用程序來管理,就像虛擬內存一樣,即使對于最好的開發人員來說,資源生命周期管理也是具有挑戰性的。無法釋放不需要的資源的應用程序會導致資源泄漏,最終導致限制被打擊,從而導致對應用程序,其他應用程序或一般系統的行為進行奇怪和困難的診斷。?
與往常一樣,我建議您閱讀以前的文章,因為他們解釋了這篇文章引用的一些概念,如分頁池。
把手和對象
在%SystemRoot%\ System32 \ Ntoskrnl.exe映像中實現的Windows的內核模式核心由各種子系統組成,如內存管理器,進程管理器,I / O管理器,配置管理器(注冊表)執行的所有部分。這些子系統中的每一個都使用對象管理器定義一個或多個類型來表示它們暴露給應用程序的資源。例如,Configuration Manager將密鑰?對象定義??為表示打開的注冊表項;?內存管理器將Section?對象定義??為共享內存;?執行官定義了S?信號,M?utant??(互斥體的內部名稱)和???Event?同步對象(這些對象包裝由操作系統的內核子系統定義的基本數據結構);?I / O管理器將File??對象定義??為表示設備驅動程序資源的打開實例,其中包括文件系統文件;?和進程管理器創建??線程?和??過程?對象,我在我最后一個推動極限后討論。每個Windows版本都引入了Windows 7的新對象類型,總共定義了42個。通過運行?具有管理權限的Sysinternals?Winobj實用程序并導航到對象管理器名稱空間中的ObjectTypes目錄,您可以看到定義??的對象:
當應用程序想要管理其中一個資源時,它首先必須調用相應的API來創建或打開資源。例如,??CreateFile??函數打開或創建一個文件,??RegOpenKeyEx??函數打開一個注冊表項,并且??CreateSemaphoreEx函數打開或創建一個信號量。如果函數成功,Windows會?在應用程序進程的句柄表中分配一個??句柄?,??并返回句柄值,哪些應用程序將被視為不透明,但實際上是句柄表中返回句柄的索引。
通過手柄,應用程序可以通過將句柄值傳遞給后續API函數(如ReadFile,??SetEvent,??SetThreadPriority和??MapViewOfFile)來查詢或操作對象??。系統可以通過索引到句柄表來查找句柄引用的對象,以找到相應的句柄入口,其中包含一個指向對象的指針。句柄入口還存儲進程在打開對象的時候被授予的訪問權限,這使系統能夠確保它不允許進程對沒有請求權限的對象執行操作。例如,如果進程成功打開一個文件進行讀取訪問,則句柄條目如下所示:
如果進程試圖寫入文件,則該功能將失敗,因為訪問權限未被授予,并且緩存的讀取訪問意味著系統不必再次執行更昂貴的訪問檢查。
最大手柄數量
您可以使用我在本系列中使用的Testlimit工具來探索第一個限制,以實踐探索限制。它可以在這里的Windows Internals書頁上下載??。為了測試進程可以創建的句柄數量,Testlimit實現了-h開關,該開關指示它創建盡可能多的句柄。通過CreateEvent創建事件對象,???然后使用DuplicateHandle反復復制系統返回的句柄??。通過復制句柄,Testlimit避免了創建新的事件,它所消耗的唯一資源就是句柄表項的資源。以下是在64位系統上使用-h選項的Testlimit的結果:
結果并不代表進程可以創建的句柄總數,但是,因為系統DLL在進程初始化期間打開了各種對象。通過向任務管理器或進程資源管理器添加句柄計數列,您可以看到進程的總句柄計數。在這種情況下,Testlimit顯示的總數是16,711,680:
在32位系統上運行Testlimit時,它可以創建的句柄數稍有不同:
其總手數也不同,16,744,448:
差異從哪里來?答案在于負責管理句柄表的執行者設置每個進程的句柄限制以及句柄表條目的大小。在Windows為資源設置硬編碼上限的罕見情況之一中,Executive定義了16,777,216(16 * 1024 * 1024)作為進程可以分配的最大句柄數。任何在任何時候都有超過一萬個句柄的過程可能要么設計得不好,要么會產生句柄泄漏,所以1600萬的限制本質上是無限的,并且可以簡單地幫助防止泄漏的過程影響系統的其余部分。了解為什么數字任務管理器顯示的數字不等于硬編碼最大值需要查看執行組織處理表的方式。
句柄表項必須足夠大以存儲授予訪問掩碼和對象指針。訪問掩碼是32位,但指針大小顯然取決于它是32位還是64位系統。因此,句柄條目在32位Windows上是8字節,而在64位Windows上是12字節。64位Windows在64位邊界上對齊句柄條目數據結構,所以64位句柄條目實際上消耗了16個字節。下面是在64位Windows上處理條目的定義,如使用dt(dump type)命令的內核調試器所示:
輸出顯示結構實際上是一個聯合,有時可以存儲除對象指針和訪問掩碼以外的信息,但這兩個字段是高亮顯示的。
Executive根據需要在頁面大小的塊中分配句柄表,將其分為句柄表條目。這意味著x86和x64上的一個4096字節的頁面可以在32位Windows上存儲512個條目,在64位Windows上存儲256個條目。執行者通過將硬編碼的最大值16,777,216除以32位Windows上的處理條目數(32,768和64位Windows上的處理條目數)除以65,536來為處理條目分配最大頁數。因為執行者使用每個頁面的第一個條目作為自己的跟蹤信息,所以一個進程可用的句柄數量實際上是16,777,216減去這些數字,這就解釋了Testlimit得到的結果:16,777,216-65,536是16,711,680和16,777,216-65,536-32,768是16,744,448。
手柄和分頁池
影響句柄的第二個限制是存儲句柄表所需的內存量,執行者從頁面緩沖池中分配。執行者使用三級方案,類似于處理器內存管理單元(MMU)管理虛擬到物理地址轉換的方式,以跟蹤它分配的句柄表頁面。我們已經看到了存儲實際句柄表條目的最低和中等級別的組織。頂層用作指向中級表的指針,在32位Windows上每頁包含1024個條目。因此,存儲最大句柄數所需的頁面總數可以在32位Windows上計算為16,777,216 / 512 * 4096,即128MB。這與任務管理器中顯示的Testlimit的分頁池使用情況一致:
在64位Windows上,頂級指針頁面中有256個指針。這意味著完整句柄表的總頁面緩沖池使用量為16,777,216 / 256 * 4096,即256MB。在64位Windows上查看Testlimit的分頁池使用情況來確認計算:
分頁池通常足夠大以容納這些大小,但正如我前面所述,創建多個句柄的進程幾乎肯定會耗盡其他資源,并且如果達到每進程句柄限制,它可能會自行失敗因為它不能打開任何其他對象。
處理泄漏
把手的數量會隨著時間的推移而增加。處理程序泄漏如此隱蔽的原因是,與Testlimit創建的句柄不同,它們都指向相同的對象,而泄漏句柄的進程也可能泄漏對象。例如,如果進程創建事件但未能關閉它們,則會泄漏句柄條目和事件對象。事件對象消耗非分頁池,除了分頁池之外,泄漏還會影響非分頁池。
您可以使用Process Explorer的句柄視圖以圖形方式查看進程正在泄漏的對象,因為它突出顯示了綠色的新手柄和紅色的關閉手柄;?如果你看到大量的綠色與罕見的紅色,那么你可能會看到泄漏。您可以通過打開命令提示符流程,選擇Process Explorer中的流程,打開句柄視圖下方窗格,然后在命令提示符中更改目錄,來觀察Process Explorer的句柄突出顯示。舊的工作目錄的句柄將以紅色突出顯示,而新的以綠色顯示:
?
默認情況下,Process Explorer僅顯示引用具有名稱的對象的句柄,這意味著除非?從“視圖”菜單中選擇“?顯示未命名的句柄和映射”,否則不會看到進程正在使用的所有句柄??。以下是命令提示符句柄表中的一些未命名的句柄:
就像大多數錯誤一樣,只有泄漏代碼的開發者才能修復它。如果您在可以托管多個組件或擴展的進程中發現泄漏,例如資源管理器,服務主機或Internet Explorer,那么問題是負責泄露的組件是什么。弄清楚這一點可能會使您通過禁用或卸載有問題的擴展來避免該問題,通過檢查更新來修復問題,或向供應商報告錯誤。
幸運的是,Windows包含一個句柄跟蹤工具,可以用來幫助識別泄漏和負責任的軟件。它在每個進程的基礎上啟用,并且在主動時導致執行者在創建或關閉每個句柄時記錄堆棧跟蹤。您可以通過使用Application Verifier(可從Microsoft免費下載)或使用??Windows??D?ebugger??(Windbg)來啟用它??。如果您希望系統從啟動時跟蹤進程的句柄活動,則應該使用Application Verifier。無論哪種情況,您都需要使用調試器和??!htrace??debugger命令來查看跟蹤信息。
為了展示實際中的追蹤,我啟動了Windbg,并將其附加到之前打開的命令提示符處。然后使用 -?enable?開關執行!htrace命令來打開句柄跟蹤:
我讓進程的執行繼續,并再次改變目錄。然后,我切換回Windbg,停止進程的執行,并且執行htrace而沒有任何選項,它列出了從上一個!htrace快照(使用-snapshot?選項創建)以來的進程執行的所有打開和關閉操作,??跟蹤已啟用。以下是相同會話的命令輸出:
事件從最近的操作打印到最少,所以從底部讀取,命令提示符打開句柄0xb8,然后關閉它,接下來打開句柄0x22c,最后關閉句柄0xec。如果在目錄更改后刷新,Process Explorer將顯示綠色的句柄0x22c,紅色的0xec,但是除非在該句柄的打開和關閉之間發生刷新,否則可能不會看到0xb8。0x22c打開的堆棧顯示它是命令提示符(cmd.exe)執行其ChangeDirectory函數的結果。將處理值列添加到Process Explorer確認新的句柄是0x22c:
如果你只是在尋找漏洞,你應該使用!htrace和??-diff?開關,它只顯示自上次快照或開始跟蹤以來只有新的句柄。執行該命令只顯示處理0x22c,如預期的那樣:
最后,一個偉大的視頻提供了更多關于調試處理泄漏的技巧,第9頻道采訪了Jeff Dailey,他是微軟升級工程師,他為了生活進行調試:https:??//channel9.msdn.com/posts/jeff_dailey/Understanding-handle -leaks-和如何使用的,HTRACE找到的,他們/
下一次,我將研究其他一些基于句柄的資源(GDI對象和USER對象)的限制。對這些資源的處理由Windows子系統管理,而不是執行者,因此使用不同的資源并具有不同的限制。
總結
以上是生活随笔為你收集整理的推动Windows的限制:句柄的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: libevent evhttp学习——h
- 下一篇: 【DSP开发】【VS开发】YUV与RGB