微軟提供了一些基本組件讓內核模式的組件使用:前端
1.陷阱分發,包括終端,延遲的過程調用(DPC),異步過程調用(APC),異常分發以及系統服務分發算法
2.執行體對象管理器windows
3.同步,包括自旋鎖,內核分發器對象,以及等待是如何實現的。api
4.系統輔助線程數組
5.其餘的機制,好比Windows全局標記緩存
6.本地過程調用安全
7.內核事件跟蹤服務器
8.Wow64網絡
3.系統機制session
中斷和異常是致使處理器轉向正常控制流以外代碼的兩種系統條件。陷阱(trap)是指當異常或者中斷髮生時,處理器捕捉到一個執行線程,並將控制權轉移到操做系統中某處固定地址處的機制。
在Windows中處理器將控制權轉給一個陷阱處理器 (trap handle)。所謂陷阱處理器是指與某個特殊的中斷或者一場相關的一個函數。
內核對待中斷和異常是有區別的,中斷是異步事件,而且與當前正在運行的任務毫無關係。中斷主要由I/O設備,處理器時鐘,定時器產生。中斷能夠容許和禁止。異常是一個同步過程,它是一個特殊指令執行的結果。
異常能夠在一樣數據在一個程序裏重現。異常的例子:內存訪問違例,特定的調試器指令,以及除0錯誤。內核把系統服務調用異常(從技術上講,他們是系統陷阱(trap))。
當一個硬件異常或者中斷產生的時候,處理器在被中斷的線程的內核棧中記錄機器狀態信息,當它能夠回到控制流中該點處繼續執行。若是該線程在用戶模式下執行,那麼windows就切換到該線程的內核模式棧。而後windows在被中斷的線程的內核棧上建立一個陷阱幀(trap frame),並把線程的執行狀態保存在陷阱幀裏。在內核調試器中輸入dtnt!_ktrap_frame就能夠看到陷阱定義。
多數狀況下內核安裝了前端陷阱處理函數,在內核將控制權交給與改陷阱香瓜的其餘函數以後或者以前,由這些前段陷阱來執行一些常規的陷阱任務。
如陷阱條件是一個設備中斷,則內核硬件中斷陷阱處理器將控制權轉交給一個由設備驅動程序提供給改中斷設備的中斷服務例程(ISR)。
若陷阱條件是由於調用了一個系統服務引起,那麼通用的系統服務陷阱處理器將控制前交給執行體中指定的系統服務。內核不會爲不處理的陷阱安裝陷阱處理器。陷阱處理器通常使用KeBugCheckEx,當內核檢測到可能致使數據被破壞的行爲時,改函數會中止計算機。
硬件產生的中斷每每是有I/O設置激發的。當設備須要服務就會以中斷的方式通知處理器。中斷驅動的設置能夠一步的進行I/O處理。
系統能夠產生軟中斷,如內核可能觸發一個軟中斷,觸發線程分發過程,同時也以異步的方式打斷一個線程的執行。
內核安裝了中斷陷阱處理器來響應設備中斷,中斷陷阱處理器將控制權遞給一個負責該中斷的外部例程(ISR)或者傳遞給一個響應中斷的內部內核例程。
下面介紹硬件如何向處理器通知中斷,內核支持中斷類型,設備驅動如何與內核交互,以及內核如何識別軟中斷。
在windows鎖支持的平臺上,外部I/O中斷進入中斷控制器的一個引腳,該控制器在cpu的引腳上中斷cpu。中斷控制器將IRQ(中斷請求)翻譯成中斷號,利用該中斷號做爲中斷分發表的索引。並將控制權傳遞給恰當的中斷分發例程。
在引導時,windows填充IDT(中斷分發表),其中包含了指向內核中負責處理每一箇中斷和異常的指針。
windows將硬件IRQ映射到IDT上,同時它利用IDT來爲異常配置陷阱處理器。雖然windows支持最多256個IDT項,可是支持的IRQ數據量由中斷控制機設計決定。
雖然中斷控制器已經實現一層中斷優先級,可是windows仍然強迫使用它本身的中斷優先級方案,稱爲中斷請求級別(IRQL)。
X86,X64,IA64中斷請求級別:
中斷是按優先級別來處理的高優先會搶佔低優先級中斷的執行權,當一個高優先級中斷髮送,處理器會把中斷線程上下文保存起來,並調用與中斷相關的陷阱分發器,陷阱分發器提高IRQL,並調用中斷服務例程,調用完成後下降IRQL,回到中斷髮送前,被中斷線程運行。可是當有其餘,低優先級中斷時,當IRQL下降,低優先級中斷出現。這樣,內核會恢復到上述過程來處理中斷。
線程優先是線程的屬性,IRQL是中斷源的屬性。每一個處理器的IRQL設置能夠隨系統代碼的執行變化。
每一個處理器的IRQL設置決定了該處理器能夠接收哪些中斷。當一個內核模式線程運行時,能夠經過KeRaiseIrql和KeLowerIrql來提高和下降處理器IRQL或經過調用內核同步對象的函數間接提升或者下降IRQL。當處理器的IRQL高於中斷源則被屏蔽,不然被中斷打斷。
訪問。
由於訪問PIC(中斷控制器)比較慢因此引入了優化技術延遲IRQL以免訪問PIC。當IRQL被提高,HAL記下新的IRQL而不是去修改中斷屏蔽值。當一個較低中斷髮生則HAL將中斷屏蔽值設置爲對於第一個中斷正常的值。這樣當IRQL被提高的時候沒有更低優先級中斷,則HAL須要修改PIC。
一個內核模式線程根據請求,來下降和升高處理器IRQL。當中斷髮生時,陷阱處理器(或處理器自己)將改處理器的IRQL提高到中斷源的IRQL.這樣會把等於或者低於它的全部中斷都屏蔽。保證了不被低級中斷截掉。被屏蔽的中斷由其餘處理器處理或者被保存下來知道IRQL降低。
所以系統組件包括內核和設備驅動,都試圖讓IRQL保持在被動級別。這樣能夠提升設備啓動能夠更加及時的響應硬件中斷。
每一箇中斷級別都有特定的目的,如內核發出一個處理器間的中斷,以請求另一個處理器執行一個動做。
將中斷映射到IRQL:IRQL級別和中斷控制器定義的中斷請求並不相同,在hal中決定一箇中斷分配給那個IRQL。而後調用HAL函數HalGetSystemInterruptVector把中斷映射到對應的IRQL。
預約義的IRQL:如下介紹一下預約義的IRQL
1.只有當內核在KeBugCheckEx中中止了系統並屏蔽全部中斷的時候,內核纔會使用高級別的IRQL。
2.電源失敗,出如今NT文檔中,可是歷來沒有使用過。
3.處理器間的中斷,用於向另一個處理器請求執行一個動做。
4.時鐘,主要用於系統時鐘,內核利用該黃總段級別來跟蹤具體時刻,以及現場測量或者分配cpu時間。
5.性能剖析(Profile),當內核的性能剖析功能被打開的時候,內核性能剖析陷阱處理器會記錄下中斷髮生時被執行的代碼的地址。(性能剖析器Kernrate)
6.設備IRQL,用來對設別中斷優先級分區
7.DPC/Dispath級別和APC級別是由內核和設備驅動程序產生的軟中斷。
8.被動級別,最低的IRQL優先級別,它不是一箇中斷級別,它是普通線程運行時設置的,容許全部中斷髮生。
對於DPC/Dispatch級別或者更高級別上的代碼,一個重要的限制是它不能等待一個對象。另一個限制DPC/Dispatch或者更高級別的IRQL只能訪問非換出頁內存。若2個限制都違反了系統會崩潰,代碼爲IRQL_NOT_LESS_OR_EQUAL。在驅動上違反限制是一種常見的錯誤。在驅動上違反限制上一種建立的錯誤。
中斷對象,內核提供了一種可移植的機制使得設備驅動程序能夠爲它們的設備註冊ISR。這是一個稱爲中斷對象的內核控制對象。
中斷對象包含了全部「供內核將一個設別的ISR與一個特定級別的中斷關聯起來的全部信息」,包含該ISR的地址,該設備中斷時所在的IRQL級別,以及內核中該ISR關聯的IDT項。
駐留在中斷對象中的代碼調用了實際的中斷分發器,通用是內核的KiInterruptDispatch或者KiChainedDispatch例程,並將指向中斷對象的指針傳遞給它。
下圖顯示了中斷控制流:
將ISR與特定中斷級別關聯起來的稱爲鏈接一箇中斷對象,將ISR與IDT項斷開關聯稱爲斷開一箇中斷對象。這些操做經過內核函數IoconnectInterrupt和IoDisconnectInterrupt完成。
雖然大多數中斷都是硬件產生,可是windows內核也爲各類各樣的任務產生軟中斷。包括:
1.激發線程分發
2.非時間緊急中斷處理
3.處理器定時到期
4.特定線程的環境中異步執行一個過程
5.支持異步I/O操做
分發或者延遲過程調用(DPC)中斷,當一個線程不能繼續執行的時候,好比由於線程已經終止了或者主動進入等待狀態,內核就會直接調用分發器,從而當即致使一個環境切換。可是有時檢測到線程已深刻到許多層代碼中,這時應該進行從新調度,這種狀況下內核請求分發,可是將它推遲到完成了當前的行爲以後再進行。
當齧合對共享的內核數據訪問,會把IRQL拉到DPC/Dispatch級別當內核檢查到須要分發的時候,請求一個DPC/Dispatch中斷。因此只有當內核完成了當前的活動,把IRQL拉低,分發中斷才能處理。
延遲事務也在這個IRQL上運行,DPC是完成一項系統任務,可是不是那麼緊迫,這些函數被稱爲延遲的,是由於不會了當即執行。
DPC賦予操做系統一種能力,產生一箇中斷而且在內核模式下執行系統函數。內核利用dpc來處理定時到期,以及一個線程的時限到期之後從新調度處理器。爲了給硬件中斷提供及時的服務,windows視圖把IRQL保持在低於設備IRQL之下。爲了達到這個目的,讓設備驅動程序ISR執行最少必要的工做來響應他們的設備,將異變的中斷狀態保存起來,並將數據傳輸非時間緊迫的中斷處理推遲到 DPC/Dispatch IRQL級別上的DPC中在執行。
DPC是經過DPC對象來表示的,DPC對象是內核控制對象,對於用戶模式不可見,對於設備驅動和內核代碼是可見的。DPC對象包含最重要的信息是DPC中斷將要調用哪一個系統函數地址。正在等待的DPC被存放在隊列中,每一個處理器都有一個隊列稱爲DPC隊列。要想請求一個DPC,系統會初始化DPC對象,而後放入DPC隊列中。
默認狀況下內核把DPC對象放在發生該DPC請求的處理器的DPC隊列末尾。在設備驅動程序只需指定一個DPC優先級別和指定特定CPU,就能夠改變這種默認方式。指定在某個CPU上叫定向DPC。若是一個DPC的優先級爲低級或者中級則放入隊列尾,不然放入隊列頭部。
當處理器的IRQL從DPC/Dispatch或更高降到某個更低的級別時,內核處理DPC。在處理DPC是IRQL在DPC/Dispatch級別上,而且將DPC來出來運行直到隊列爲空。當隊列爲空內核才讓IRQL下降到DPC/Dispatch如下。讓正常的線程執行過程繼續執行。
DPC優先級能夠以另外一種方式影響到系統行爲。內核一般經過一個DPC/Dispatch級別的中斷來激發隊列「抽乾」的動做。只有當一個DPC被定爲在ISR所在的處理器上,且改DPC的優先級是高級或者中級時,內核才產生一箇中斷,若爲低級只有當DPC請求到一個閥值或一段時間後,內核纔會請求中斷。
若DPC被定爲在一個不一樣於其ISR運行的CPU上,並DPC爲高級。內核當即用一個信號通知CPU,以便」抽乾」它的DPC隊列。若優先級爲中級或者低級則DPC數據超過閥值,內核纔會激發一個DPC/Dispatch中斷。
DPC中斷產生的規則:
DPC優先級別 |
DPC被定爲在ISR的處理器上 |
DPC被定爲在另外一個處理器上 |
低級 |
DPC隊列長度超過最大的DPC隊列長度值,或者DPC請求率小於最小的DPC請求率。 |
DPC隊列長度超過最大的DPC隊列長度或者系統空閒 |
中級 |
老是激發 |
DPC隊列長度超過最大的DPC隊列長度或者系統空閒 |
高級 |
老是激發 |
老是激發 |
DPC主要爲設備驅動提供的,可是內核也使用DPC,內核使用DPC來處理限時到期事件。在系統時鐘每一個」嘀嗒」點上,就發生一個時鐘IRQL級別的中斷。時鐘中斷處理器對系統時間進行更新,將一個記錄了當前線程運行多長時間的計數器遞減。當計數器減到0,線程到期,內核可能須要從新調度該處理器,這個任務在DPC/Dispatch IRQL上完成。
時鐘中斷處理器將一個DPC插入到隊列中以便激發分發過程。而後結束他的工做而且下降IRQL,由於DPC中斷級別較低,因此在時鐘中斷完成前出現還沒有處理的設備中斷,都在DPC中斷以前被處理。
APC異步調用,異步過程調用提供了一種在特定用戶線程環境中執行用戶程序和系統代碼的途徑。APC通過排隊以便在特定線程的環境中執行。
APC是由一個內核控制對象(APC對象)來描述的,正在等待執行的APC駐留在一個由內核管理的APC隊列中。APC隊列是特定線程相關的,即每一個線程有它本身的APC隊列,當內核請求要將APC排隊時,它將一個APC排隊,她將APC插入到未來執行此APC例程的那個線程的隊列中。當內核請求APC級別中斷,當該線程最終開始執行的時候,會執行此APC。
有2種APC:內核和用戶模式。內核模式的APC並不要求從目標獲取許可就能夠運行在改線程的環境中,而用戶模式必須先獲取許可。內核模式的APC有普通和特別2種,將IRQL提高到APC級別或調用KeEnterGuardRegion,就能夠靜止這兩種類型的內核模式APC。
執行體使用內核模式的APC來完成那些必需要在特定線程的地址空間(執行環境)中才能完成的操做系統任務。它能夠利用特殊的內核模式APC來指示某個線程中止執行一個可中斷的系統服務。
用戶模式APC(ReadFileEX,WriteFileEx和QueueUserApc),如ReadfileEx,WritefileEx容許調用者指定一個完成例程,當I/O完成是例程就會被調用。I/O完成機制是經過I/O的線程插入一個APC來實現的。內核APC運行在APC級別上,用戶模式APC運行在被動級別上。
APC交付會致使等跌隊列從新排序,如APC用來把等待資源的線程掛起,那麼該線程就會進入等待訪問這個資源隊列的末尾。
中斷能夠在任什麼時候候發生,異常則是直接由當前正在運行的程序產生。windows引入了一種稱爲結構化異常處理的設施,應用程序能夠在異常發生時得到控制,而後應用程序能夠修正條件,並返回到異常發生處,將棧展開(使引起異常的子例程執行過程當中止),或想系統報告,改異常不可識別,由於系統應該繼續搜索一個有可能處理此異常的異常處理器。
x86上全部異常都在預約義的中斷號,這些中斷號對應IDT項。每一個項指向了某個特定異常的陷阱處理器。
全部異常,除了簡單的經過陷阱處理器,能夠解決的以外,其餘都由異常分發器的內核模塊服務。異常分發器就是找到一個異常處理器,處理要處理的異常。
異常處理對用戶來講都是透明的,有些異常也容許原封不動的回到用戶模式。如內存訪問違例,算法溢出,操做系統不對他們處理。環境子系統能夠創建起基於幀的異常處理器來處理異常。
基於幀是將一個異常處理與一個特定的過程激活動做關聯起來。當一個過程被調用,表明該過程的幀被壓到棧中。一個棧幀能夠關聯多個異常處理器,每一個異常處理器保護源程序中一塊特定代碼。當發生一個異常時,內核查找與當前幀關聯在一塊兒的某個異常處理器。若是沒有找到內核繼續查找與上一個棧幀關聯在一塊兒的某個處理。若是最終仍是沒有找到異常處理器,內核會調用本身默認的異常處理器。
異常發生,CPU硬件將控制權遞交給內核陷阱處理器,內核陷阱處理器建立一個陷阱幀。正因爲陷阱幀處理完異常後,系統能夠從中止的地方恢復。
若是在內核模式下的異常,異常分發器調用一個例程來找到一個基於幀的異常處理器。由它來處理異常。
在用戶模式下,windows子系統有一個調試器端口和異常端口,經過它們來接收windows進程中用戶模式異常的通知。內核在它默認的異常處理器中用了這些端口。
調試器端口是最多見的異常來源,所以異常分發器採起動做,
1.查看引起該異常的進程是否有一個相關的調試器進程。若存在異常分發器發送一個調試器對象信息到調試對象相關的進程。
2.若該進程沒有附載的調試器進程或調試器並無處理該異常,那麼異常分發器切換到用戶模式下。將陷阱幀按照Context數據結構的格式拷貝到用戶棧中,並調用一個例程來找到一個基於幀的異常處理器。
3.若是沒有找到或者雖然找到了可是它不處理該異常,則異常分發器切換到內核模式下,而且再次調用調試器,以便讓用戶作更多的調試。
4.若調試器不在運行,並無找到基於幀的處理器,那麼內核向與該線程的進程關聯在一塊兒的異常端口發送一個信息。該異常端口若是存在的話,則必定是由控制該線程的環境子系統註冊的。環境子系統監聽該端口,在恰當的時機把一個異常轉化爲一個與環境相關的信號或異常。客戶/服務器運行時子系統(CSRSS)簡單的彈出一個消息框來通知用戶發生了錯誤,而且終止進程。
5.當POSIX從內核收到一個消息,指定的一個線程產生了異常,當內核在處理異常過程走得比較深了,而子系統並無處理該異常,那麼內核執行一個默認的異常處理器,它只是簡單的將引起該異常的線程所在的進程終止掉。
未處理的異常
全部windows線程都有一個異常處理器來處理未被處理的異常。該異常處理器是在windows內部的進程啓動函數或線程啓動函數中聲明。如:
若是一個線程的異常沒有被處理,則windows的未處理異常過濾器將會被調用。這個函數目的是,當一個異常未被處理時,能夠提供一種系通通一的行爲和方法。
內核陷阱處理器分發中斷,異常和系統服務調用
在x86 Pentium II處理器以上,使用windows使用sysenter執行觸發一個陷阱,這個是intel特別爲快速系統服務定義的。爲了支持這一指令,windows在引導時刻把內核的服務分發器的地址保存與該指令相關的寄存器中。
執行該指令會致使變化到內核模式下,而且執行系統服務分發器,爲了返回到用戶模式,系統服務分發器一般執行sysexit執行(當處理器單步標記被打開,系統分發器改而使用iretd指令。)
在K6和更高的32位AMD處理器上,windows使用syscall相似於sysenter,系統嗲用號也在EAX上,而調用者參數則保存在棧中。在完成了分發以後,內核執行sysret指令。
在64位體系結構上,windows使用syscall指令進行系統分發(和AMD處理器上syscall相似),系統調用號存在EAX寄存器,前4個參數存放在寄存器彙總其餘參數存放在棧中。
在IA64上使用EPC指令,前8個系統調用參數經過寄存器來傳遞,其餘參數經過棧傳遞。
內核利用傳遞進來的參數找到系統分發表中的服務信息(相似IDT表)。
系統服務分發器KiSystemService將調用的參數從用戶模式棧中複製到內核模式,而後執行服務。若是換地給一個系統服務的參數指向了用戶空間中的緩衝區,那麼在內核模式代碼複製到緩衝區或從緩衝區讀前先要查明緩衝區是否能夠訪問。
每一個線程都有一個指針指向它的系統服務表,windows有2個系統服務表,最多能夠支持4個。系統服務分發器肯定哪一個表包含了全部請求的服務,它將32位系統服務號中的其中2個位解釋成一個索引表。系統服務號低12位被用在該表索引所指定的表中進行的索引。
一個主要的默認數組表(KeDescriptorTable)定義了Ntosrknl.exe中實現的核心執行體系統服務。另外一個默認數組表(KeserviceDeseriptorTableShadow)包含了在windows子系統的內核模式部分win32.sys中實現的windows user和GDI服務。
當windows線程第一次調用一個windows user和GDI服務,該線程的系統服務表的地址被指向一個包含windows user和GDI服務的表格。KeAddSystemSericeTable可讓win32.sys加入到系統服務表中。
針對windows執行體服務的系統服務分發指令位於NTdll.dll中。子系統調用Ntdll.dll來實現。windows user和GDI函數,在這些函數中,系統分發指令是直接在user32.dll和GDI.dll中實現,沒有涉及ntdll.dll。
windows實現了一個對象模型,以便爲執行體的實現各類內部服務提供了一致的,安全的訪問路徑。執行體內部複製建立,刪除,保護和跟蹤對象的組件(windows對象管理器)。
對象管理器把本來可能散落在整個系統各處的資源控制操做集中在一塊兒。對象管理器的設計意圖是爲了實現本章稍後列出的一些功能。
1.提供一種公共,同一個的機制來使用系統資源
2.將對象保護隔離到操做系通通一的區域中,從而能夠作到c2安全等級
3.提供一種來記錄進程使用對象數量的機制,從而能夠對系統資源的使用上加限制。
4.簡歷一套對象命名方案,能夠很方便的融合現有對象。
5.支持各類操做系統環境的須要
6.簡歷統一的規則來維護對象的保持力。
windows內部有兩種類型的對象:執行體對象和內核對象。
執行體對象是指執行體的各類組件所實現的對象(如,進程管理器,內存管理器,I/O子系統)內核對象是指windows內核實現的一組更爲基本的對象。這些對象在執行體內部被建立和使用。
每一個windows環境子系統老是把操做系統的不一樣面貌呈現給它的應用程序。執行體對象和對象服務是環境子系統用於構建其本身版本的對象和其餘資源基礎。
執行體對象每每由在用戶應用程序中通常的環境子系統或由操做系統的組件做爲他們常規操做的一部分而建立。如爲了建立一個文件,windows應用程序調用windows的createfile函數,該函數在windows子系統DLL kernel32.dll中現實中實現的,在通過了一些驗證和初始化工做之後,createfile會調用原生的windows服務ntcreatefile來建立一個執行體文件對象。
windows子系統使用執行體對象來導出它本身對象集合,其中許多對象直接對應於執行體對象。
暴露給windows api的執行體對象:
對象類型 |
所表明的含義 |
符號連接(Symbolic link) |
間接的引用一個對象名字的機制 |
進程(Process) |
虛擬地址空間,以及爲了執行一組線程對象而必需的控制信息 |
線程(Thread) |
進程內部的一個可執行實體 |
做業(Job) |
指一組進程,經過做業機制,能夠像單個實體那樣來管理他們 |
內存區(section) |
共享內存的一個區域(在windows中也稱爲文件映射對象) |
文件(File) |
一個已打開的文件或者I/O設備的實例 |
訪問令牌(Access token) |
一個進程或者線程的安全輪廓(安全ID,用戶權限等) |
事件(Event) |
一個具備持久狀態(有信號,或者無信號的)的對象,可被用於同步或者通知。 |
信號量(Semaphore) |
信號量是一個計數器,它提供了資源門控能力,對該信號量所保護的資源只容許某個最大數目的線程來訪問它。 |
互斥體(Mutex) |
用於順序訪問一個資源的一種同步機制 |
定時器(Timer) |
這是一種當固定長時間過去時,通知一個線程的機制 |
IO完成(IoCompletion) |
使線程可以將」I/O操做完成通知」進出隊列的一種方法,在windows中稱爲IO完成端口。 |
鍵(Key) |
這是一種引用註冊表中數據的機制。 |
窗口站(WindowStation) |
該對象包含了一個剪貼板,一組全局原子和一組桌面對象 |
桌面(Desktop) |
這是一個被包含在窗口站內部的對象。桌面對象有一個邏輯顯示器表面,其中包含了窗口,菜單和鉤子。 |
每一個對象都有一個對象頭和對象體,對象管理器控制了對象頭,而執行體組件則控制了由它們建立的對象類型的對象體。每一個對象頭指向一個進程列表,類表中每一個進程都打開了此對象。對象類型指向了稱爲類型對象的特殊對象。
對象管理器使用對象頭中保存的數據來管理這些對象,而無須關係它們的類型。
標準對象頭的屬性:
屬性 |
用途 |
對象名稱 |
使一個對象對於其餘的進程也是可見的,以便於共享 |
對象目錄 |
提供了一個層次結構來存儲對象名稱 |
安全描述符 |
決定誰可使用該對象,以及容許它們如何使用它(注:對於沒有名稱的對象來講,安全描述符是空[null]) |
配額花費 |
列出了當一個進程打開一個指向該對象的句柄時,針對該進程收取的資源花費額 |
已打開句柄的計數 |
記錄了「打開一個句柄來指向該對象」的次數 |
已打開句柄的列表 |
指向一個進程列表,其中每一個進程都打開了指向該對象的句柄。 |
對象類型 |
指向一個類型對象,該對象包含了正對這個類型的對象都是公共屬性 |
引用計數 |
記錄了一個內核模式組件引用該對象地址的次數 |
除了對象頭之外,每一個對象也有一個對象體,而且其格式和內容只有這種對象類型纔有,同一類型的全部對象共享一樣的對象體格式。一個執行體組件經過建立一個對象類型,而且爲它提供一些服務,就能夠控制和維護全部這個類型的對象體中的數據。
對象管理器提供了少許經過服務,經過這些服務能夠對一個對象頭中保存的屬性進行操做,經過服務能夠用再任何類型的對象上。
雖然通用的對象服務都支持全部對象類型,可是每一個對象都有它本身的建立,打開和查詢服務。
對象頭中包含的數據對於全部對象都是公共的,可是每一個對象實例能夠取不一樣的值。
爲了節省內存,對象管理器只在建立一個新的對象類型時纔會存儲靜態的,特定於對象類型的屬性。
進程對象和進程類型對象:
類型對象的屬性
屬性 |
用途 |
類型名稱 |
此種類型的對象名稱(「process」,」event」,」port」) |
池類型 |
指明瞭這種類型的對象是從換頁的仍是非換頁的內存中分配 |
默認的配額花費 |
默認從進程配額中扣除的換頁內存池值和非換頁內存池值 |
訪問類型 |
當一個線程打開某個指向該類型的對象時能夠請求的訪問類型(「讀」,」寫」,」終止」,」掛起」) |
通用訪問權限的映射關係 |
在4種通用的訪問權限和屬於該類型的訪問權限之間的映射關係。 |
同步 |
指明瞭一個線程是否能夠等待這種的對象 |
方法 |
在一個對象的生命週期的特定點上,對象管理器自動調用的一個或者多個例程。 |
一個對象可否支持同步,取決於該對象是否包含了一個內嵌的分發器對象,在「低IRQL的同步」中有介紹。
上面的表中,最後一個屬性就是方法。方法是由一組內部例程構成的,這些例程相似構造和解析函數(在建立和銷燬時被使用)。
當一個執行體組件建立了一個新的對象類型時,它能夠像對象管理器註冊一個或多個方法,對象管理器在此種類型的對象生命週期中,某些明肯定義的點上調用這些方法。
對象方法:
方法 |
什麼時候調用 |
Open |
當一個對象句柄被打開 |
Close |
當一個對象句柄被關閉 |
Delete |
在對象管理器中刪除一個對象以前 |
Query name |
當一個線程在一個從屬名字空間中查詢一個對象的名稱時 |
Parse |
當對象管理器在一個從屬名字空間中搜索一個對象名稱時 |
Security |
當一個進程讀寫(如文件)在其從屬名字空間中的保護屬性時。 |
對象管理器建立一個指向對象的句柄時會調用open方法,在對象被建立或者打開時候運行。只有一個對象類型(windowstation)定義了open方法。這樣win32.sys可以與服務於桌面相關內存池的進程共享內存。
close方法的例子是IO中,對象管理器關閉一個句柄使用close方法。close方法先檢查看正在關閉該文件句柄進程是否有任務用於該文件而且未完成的鎖,若是有則除去鎖。
對象管理器在內存中刪除臨時對象之前,調用delete方法。內存管理器爲內存區對象類型註冊了delete方法,它會釋放該內存區使用的物理頁面。並在刪除內存區對象前驗證一下內存管理器爲該內存區所分配的任何內部數據結構已被刪除了。
若發現對象存在於對象管理器名字空間外,容許對象管理器把查找一個對象的控制權交給一個從屬的對象管理器。若在搜索路徑上碰到一個關聯了parse的對象,會暫停搜索。對象管理器調用parse方法。將正在搜索的對象名稱的剩餘部分傳給parse方法。除了對象管理器方法外在windows中還有註冊表名字空間和文件系統名字空間。例如打開一個名爲\Device\Floppy0\docs\resume.doc的文件句柄,對象管理器遍歷它的名稱樹,直到到達Floppy0。調用parse,把\docs\resume.doc傳入。I/O管理器的parse例程接受名稱,而且傳給文件系統,文件系統找到文件並打開。
Security也是I/O系統使用方法,相似parse。一旦一個線程視圖查詢或改變那些用於保護一個文件的安全信息時,該方法就會被調用。安全信息是存儲在文件對象中,而不是內存,所以必須調用I/O系統才能找到安全信息,並將它們讀出來或進行修改。
當進程根據名稱來建立或者打開一個對象時,它會接受到一個句柄,經過句柄來訪問一個對象,要比使用名稱訪問快得多。由於對象管理器能夠跳過名稱查找過程,直接找到目標對象。進程也能夠在其建立時刻經過繼承句柄的方式得到句柄或從另外一個進程接收一個複製的句柄。
全部的用戶模式進程在其線程使用一個對象之前,必須先擁有一個指向該對象的句柄。句柄被用作指向系統資源的間接指針,這樣可讓應用程序不與系統數據結構直接交互。
對象句柄仍是提供了額外的一些好處:第一,不一樣句柄沒有什麼區別可使用統一的接口來引用。第二,對象管理器有獨立的權利來建立句柄,查找句柄。也就是對象管理器能夠仔細地審查每一個可能會影響對象的用戶模式動做。
對象句柄是索引與進程相關的句柄表中的項相關。執行體進程(EPROCESS)塊中一個域指向句柄表。句柄表實現方式是3層和虛擬地址到物理地址映射相似。
當進程被建立對象管理器分配了句柄表的最高層結構,其中包含了指向中間層表的指針;同時也建立了中間層,其中包含了第一個指向子句柄表的指針數組,還分配了最底層,其中包含了第一個子句柄表。
把低24位當作3個8位,分別索引到3層結構中的一層。在xp,2003在進程建立時,最底層句柄表被分配,其餘的都會被按需分配。在windows 2000中一個子句柄表是255個可用表項。在xp,2003表項=(頁大小/表項大小)-1。在windows xp,2003上句柄表項:
P:說明了調用者是否容許關閉句柄。I:該進程建立的子進程是否在它們句柄表中有一份該句柄的拷貝。A:關閉該對象時是否應該產生一個升級信息(對象管理器內部使用該標記)。
系統組件和設備驅動程序一般須要打開一些不該該讓用戶訪問的對象。能夠經過內核句柄表來表示。內核句柄表只有在內核模式下能夠飛昂文,能夠在任何進程環境下。
對象管理器看到一個句柄的高位被設置時,就會將它識別爲內核句柄表中的句柄。也就是說內核句柄表中的句柄的引用值大於0x80000000。在windwos 2000中內核句柄表是一張獨立的句柄表,可是在xp和2003中,內核句柄表也被用作system進程的句柄表。
當一個進程打開一個句柄,對象管理器調用安全引用監視器,監視該對象描述符是否容許該進程所請求的訪問類型,若容許,引用監視器返回一組准許的訪問權限,同時對象管理器放入它建立的對象句柄中。
當下次進程要使用句柄時能夠快速的檢查這一組句柄中的准許訪問權限。
對象保持力,分爲暫時和永久的。暫時是當須要的時候使用,不須要的時候釋放。永久是一直保持知道被顯式釋放。
對象管理器經過兩個階段來實現對象保持力。第一階段稱爲名稱保持力,第二個階段,再也不有用時,中止保留對象自己(也就是刪除)。
名稱保持力:當一個進程打開一個對象的句柄。會在該對象頭信息中的已打開句柄計數器+1。當用完關閉句柄,對象管理器已打開句柄-1,。當計數器爲0,對象管理器從全局名字空間中刪除該對象名稱。
再也不有用時刪除:對象專門提供一個引用計數來記錄。
已打開句柄計數器:當進程打開一個對象句柄+1,關閉句柄-1
引用計數:提供一個對象指針+1,用完了-1
當已打開計數器爲0,引用計數大於0.表示對象還在使用。當引用計數爲0,對象管理器會從內存中將它刪除。
進程A,進程B和內核結構引用了一個對象,所以handlecount=2,referencecount=3。
windows對象管理器提供了一箇中心設施來實現資源記帳。每一個對象頭都包含了配額花費。
windows每一個進程都指向一個配額的數據結構,配額爲0表示不限制。
對象名稱能夠知足1.區分對象之間的方法。2.找到並得到特定對象的方法。3.容許程序間共享。
只有2種狀況會使用名稱進行查找,1.建立一個命名對象時,會經過名稱查找,驗證全局名稱空間中不存在。2.當打開一個句柄,句柄指向一個命名對象時,對象管理器查找該名稱並返回一個對象句柄。
對象的名稱存儲位置取決於對象類型
目錄 |
所存儲對象名稱的類型 |
\GLOBAL?? |
Ms-dos設備名(\DosDevices是指向此目錄的符號連接) |
\BaseNameObjects |
互斥體,時間,信號量,可等待的定時器和內存區對象 |
\Callback |
回調對象 |
\Device |
設備對象 |
\Driver |
驅動程序對象 |
\FileSystem |
文件系統驅動程序對象和文件系統識別器對象 |
\KnowDlls |
已知DLL(在啓動時候由系統映射的DLL)的內存區名稱和路徑 |
\Nls |
已映射的國家語言支持表的內存區名稱 |
\ObjectTyoes |
對象類型名稱 |
\RPC Control |
遠程過程調用(RPC)所使用的端口對象 |
\Security |
與安全子系統相關的對象的名稱 |
\Windows |
Windows子系統的端口和窗口站 |
對象的名稱相對於一臺計算機而言是全局的,可是他們的跨越網絡是不可見的。可是對象管理器解析名稱的方法使得有可能訪問其餘機器上的命名對象。
對象目錄是對象管理器支持這種層次型命名結構手段。對象目錄解析對象名稱爲指向對象的指針。對象管理器利用指針來構建對應的句柄,將這些對象句柄返回給用戶模式的調用者。
符號連接,在某些文件系統中,經過符號連接,用戶能夠建立一個文件名或一個目錄名,當被使用的時候,實際上被操做系統轉譯成另一個不一樣的文件或文件名。使用符號連接,是一種讓用戶間接的共享一個文件或目錄的內容。
符號連接對象,完成的功能相似於對象名稱的功能同樣。當對象名稱中有符號連接,對象管理器遍歷它的對象名稱空間,找到該符號連接對象,並找到一個取代該符號連接名的字符串。
一個登錄到控制檯會話上,用戶能夠訪問全局名稱空間,另外會話能夠得到該名稱空間的私有名稱空間實例。\DosDevices,\Windows,\BaseNamedObjects屬於會話局部的名稱空間,都會被放在私有名稱空間中。將名稱空間中相同部分複製,來初始化名稱空間。
對象管理器在\session\X下建立私有版本,對象管理器以透明的方法,將對象的名稱從\BaseNameObjects重定向到\session\2\BaseNamedObjects。
windows子系統DLL將windows應用程序傳過來的位於\DosDevice中對象引用加上\??前綴(c:\windows變成\??\c:\windows)。依賴於EPROCESS中DeviceMap。DeviceMap結構中DosDevicesDirectory域所指的對象目錄管理器表明了進程的局部DosDevices。
在windows 2003和xp上,系統沒有將全局對象拷貝到局部DosDevices目錄中,當看到\??會經過DeviceMap中DosDevicesDirectory找到該進程局部\DosDevices目錄。若在局部中沒有,而且DeviceMap有效,則會在GlobalDosDevicesDirectory查找對象。
當會話中的應用程序要與其餘會話的實例同步在任何對象名稱前加入\Global\ApplicationInitialized被重定向到\BasedNameObjects\ApplicationInitized而不是\Sessions\2\BaseNamedObjects\ApplicationInitialized。
在2003和xp中應用程序只要在\DosDevices中沒有這樣的對象就會訪問全局,不須要使用\Global。
當一個資源不容許共享訪問,或共享訪問致使不可預測的後果則須要互斥。若一段代碼訪問了一個不可共享的資源,則這樣的代碼區成爲臨界區。
在內核執行的各個階段,內核必須保證,在臨界區內部同一時刻只有一個處理器在執行。
在中斷髮生,內核可能在更新一個全局數據結構,而中斷的處理例程可能也要修改此數據結構。在單處理器,能夠用如下方式來避免。如,當線程修改全局數據結構時,禁止全部中斷。windows的做法是執行臨界區時,把IRQL拉高會用到該數據結構的最高IRQL。
這個最簡單的同步方式,依賴於支持多處理器硬件,操做一個整型值來進行比較。
自旋鎖是內核用來實現多處理器互斥的機制
在windows中,全部內核模式自旋鎖都有一個與之關聯的IRQL。當運行自旋鎖就會拉高IRQL。
工做方式:當一個處理器要得到已被其餘處理器持有的隊列自旋鎖鎖時,把本身的標示符放入一個與該自旋鎖關聯的一個隊列中。
當自旋鎖被釋放,將鎖交給隊列中第一個標示符對於的cpu。
在同時處理器等待一個較忙的自旋鎖不是檢查自旋鎖自己而是每一個處理器的標誌。在隊列中位於它以前的處理器會對它設置,代表輪到這個等待的處理器了。
排隊的自旋鎖的天然結果是,他們在每一個處理器標識符上旋轉,而不是全局自旋鎖上旋轉有2個效果:
1.多處理器總線不會由於處理器之間的同步招致繁重的流量。
2.排隊增強了先進先出的順序,處理器之間性能更加一致。
在windows中定義了不少全局隊列自旋鎖,而且在每一個處理器的「處理器控制區域(PCR)」包含了一組數組(保存了指向這些全局隊列的自旋鎖指針)。當調用KeAcquireQueueSpinLock的時候將一個PCR的索引傳進去,能夠得到對應的全局自旋鎖。
除了使用全局定義的靜態排隊自旋鎖,xp和2003還提供了KeAcquireInstackQueuedSpinlock和KeReleaseInstackQueuedSpinlock。來支持動態分配的排隊自旋鎖。
內核提供了不少簡單的創建在自旋鎖基礎上的同步函數。
自旋鎖使用有嚴格的限制:
1.對於受保護的資源,必須快速訪問,不要與其餘代碼有複雜的交互關係
2.臨界區代碼的內存頁不能換出去,不能引用可被換頁的數據,不能調用外部過程,不能中斷或異常。
在自旋鎖不適合是可用:內核分發器對象,快速互斥體和受限互斥體,壓棧鎖,執行體資源。
|
是否暴露給設備驅動程式使用 |
禁止常規的內核模式APC |
禁止特殊的內核模式APC |
支持遞歸獲取操做 |
支持共享的和獨佔的獲取操做 |
內核分發器互斥體 |
是 |
是 |
否 |
是 |
否 |
內核分發器信號量 |
是 |
否 |
否 |
否 |
否 |
快速互斥體 |
是 |
是 |
是 |
否 |
否 |
受限互斥體 |
否 |
是 |
是 |
否 |
否 |
壓棧鎖 |
否 |
否 |
否 |
否 |
是 |
執行體資源 |
是 |
是 |
否 |
是 |
是 |
內核之內核對象的形式,向執行體提供了額外的同步機制,這些內核對象合起來統稱爲分發器對象。每一個支持同步的用戶可見對象都封裝了至少一個內核分發器對象。
一個用戶模式的線程等待一個事件對象的句柄。
內核將該線程的調度狀態從就緒狀態改變成等待狀態,而後將該線程加入到正在等待該事件的線程列表中。
另一個線程設置了該事件,內核沿着該事件的等待線程隊列向前搜索。如有一個線程等待條件知足將線程狀態從等待改成就緒。如果一個可變優先級的線程,則內核可能也要提高它的執行優先級。
由於一個新線程已經變成就緒執行狀態,因此進行從新調度。若是它找到一個正在運行的線程。其優先級低於就緒線程的優先級。那麼會搶佔此低優先級的線程,而且發出一個軟中斷,以便激發一個環境切換,切換到高優先級的線程中。
若是沒有處理器能夠搶佔的話,則分發器將該就緒線程放到分發器就緒隊列中,之後再被調度。
略
typedef struct _DISPATCHER_HEADER {
UCHAR Type;
UCHAR Absolute;
UCHAR Size;
UCHAR Inserted;
LONG SignalState;
LIST_ENTRY WaitListHead;
} DISPATCHER_HEADER;
typedef struct _KWAIT_BLOCK {
LIST_ENTRY WaitListEntry;
struct _KTHREAD *RESTRICTED_POINTER Thread;
PVOID Object;
struct _KWAIT_BLOCK *RESTRICTED_POINTER NextWaitBlock;
USHORT WaitKey;
USHORT WaitType;
} KWAIT_BLOCK, *PKWAIT_BLOCK, *RESTRICTED_POINTER PRKWAIT_BLOCK;
每一個分發器對象都有一個等待列表,列表中一項表明一個等待該對象的線程。因此當線程向一個分發器對象發出信號,內核能夠很快的肯定誰在等待對象。
等待快結構WaitListEntry指向被等待對象,Thread指向等待線程,NextWaitBlock指向下一個等待塊。
快速互斥體也成爲執行體互斥體,比互斥體對象提供了更好的性能。儘管他們也是創建在分發器事件對象基礎上的,可是若是對於快速互斥體沒有競爭的話,他們無須等待事件對象。
受限互斥體,本質上它與快速互斥體是相同的。使用了KGATE同步對象,經過調用KeEnterGuardedRegion來進制全部內核模式APC的事務,主要用戶內存管理器。
執行體資源是一種支持共享和獨佔訪問的同步機制,要求APC事務禁止,若是一個線程正在等待得到一個共享訪問權,則它應該等待一個與該資源相關聯的信號量,若是一個線程正在等待得到一個資源的獨佔訪問權,則應該等待一個事件。
當一個獨佔持有者經過給信號量髮型號來釋放一個資源喚醒共享訪問者。
當一個線程在等待獨佔訪問一個資源,而該資源正在被其餘線程擁有,該線程等待一個同步事件對象。
壓棧鎖是創建在KGATE同步對象基礎之上,相比快速互斥體好處是能夠按照共享的方式獨佔的模式來得到。
有兩種類型壓棧鎖:普通壓棧鎖和能感知緩存的壓棧鎖。
普通壓棧鎖:當一個線程想要得到一個普通的壓棧鎖,若還沒有被使用則壓棧鎖代碼標記爲已被佔用。若已被佔有(共享,獨佔),線程在本身棧上分配一個等待塊,將初始化等待塊中的事件對象,等待塊加入到與壓棧鎖相關聯的等待列表中。向該等待着的等待塊中的事件發出信號。
能感知緩存的壓棧鎖簡歷在基本壓棧鎖之上。爲每一個處理器分配一個壓棧鎖,而後將這些壓棧鎖和處理器關聯起來。當一個線程但願以共享方式得到壓棧鎖時,簡單的得到對應於當前處理器的那個壓棧鎖以獨佔方式得到獨佔鎖時,以獨佔模式得到每一個處理器的壓棧鎖。
壓棧鎖的使用範圍包括對象管理器和內存管理器,對象管理器中,能夠保護全局對象管理器結構和對象安全描述符,在內存管理器,他們能夠保護awe數據結構。
windows在system進程中建立了幾個線程,這些線程稱爲系統輔助線程它們表明其餘線程來完成一些工做。如DPC級別的IRQL不能運行更低IRQL級別才能執行的函數,必須將這樣的處理過程傳遞給一個低於DPC級別的IRQL的執行線程上。
設備驅動程序或執行體組件經過ExQueueWorkItem和IoQueueWorkItem把工做放到一個隊列分發器對象上,系統複製線程在該對象上尋找工做。工做包含一個例程以及一個參數,當輔助線程處理該工做,會把參數傳遞給例程。
系統輔助線程有如下三類:
延遲型輔助線程:優先級12上,處理器非緊急工做項目,當它在等待工做項目時容許棧頁面被換出到頁面文件中。
緊急型輔助線程:優先級13,處理一些緊急工做項目,始終在內存上
超緊急型輔助線程:優先級15,總在內存中。
執行體函數ExpWorkerThreadBalanceManager肯定是否建立新的緊急型輔助線程,新的線程被稱爲動態的輔助線程。建立時必須知足下列條件:
1.在緊急工做隊列下有工做項目
2.不活動的緊急型輔助線程的數目必須少於系統處理器個數
3.動態輔助線程數據少於16個。
略
LPC用於進程間通訊,LPC爲了如下3中方式通訊而設計:
1.短於256字節的信息能夠經過LPC發送。從發送進程拷貝到系統地址空間中,再從系統地址空間中拷貝到接收進程地址空間中。
2.若多於256字節則複製到共享內存區。
3.若超過了內存共享區,能夠直接在地址空間中讀取或寫入。
LPC有多種端口:服務器鏈接端口,服務器通訊端口,客戶通訊端口,未命名的通訊端口
一個公共的基礎設施,向內核和ETW提供痕跡數據應用程序要使用到ETW,要屬於如下3類:
1.控制器,啓動或中止,也管理緩衝區
2.提供者,爲它所能產生的事件類定義GUID,並註冊到ETW以上,並接受控制器命令,啓動,中止它所負責的事件類跟蹤。
3.消費者,選擇一個或多個會話,讀取數據。
控制器啓動內核記錄器(ETW庫)向WMI發送一個IO請求說明要開始跟蹤哪些事件類。當WMI接受到已啓動跟蹤源接收到數據就會寫入buffer,每隔一秒觸發一次寫入日誌文件。
64位windows上win32仿真,也就是能夠在64位上執行32位,x86應用程序。以DLL形式來實現。
wow64.dll實現了文件系統重定向,以及註冊表重定向和反射
wow64cpu.dll實現了cpu從32到64,從64到32之間的切換
wow64win.dll截取了win32k.sys導出GUI系統調用
wow64進程能夠是2G,也能夠是4G虛擬空間。若沒有設置大地址空間感知標誌則最多保留2G,若開啓大地之空間感知標誌最多保留4G。
Wow64.dll鉤住從32位代碼變值原生64爲系統代碼路徑,也鉤住64位原生系統須要調用至32位用戶模式代碼的全部代碼路徑。
啓動應用程序,64位ntdll.dll映射到地址空間,初始化判斷映像頭爲32位x86則加載wow64.dll映射32位ntdll.dll,創建ntdll內部啓動環境切換到32位,執行32位加載器。
64位和32位間經過wow64在之間轉換。
經過wow64來轉化異常分發。
經過wow64來轉化
爲了下降應用程序移植代價,全部相關API將windows\system32替換爲windows\syswow64。使用文件系統重定向來實現。
線程使用wow64enable,wow64FsRedirection函數進制文件系統重定向。
註冊表也使用重定向在註冊表上建立原生和wow642中,視圖爲了容許32和64位com組件互操做,在註冊表中某些特定部分被更新,wow64也會將這些更新映射到迎來一個視圖。
IO除了讀寫硬盤還能夠用於程序通訊,驅動程序通IoIs32bitProcess來檢測是否從一個wow64進程發出的。
主要介紹執行體創建起來的基本系統機制。