免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
wdm驅(qū)動(dòng)開發(fā)之路(5)
wdm驅(qū)動(dòng)開發(fā)之路(5)



WDM開發(fā)之路(五)
    上一篇中我們學(xué)習(xí)了核心層編程環(huán)境和異常處理的一部分,這篇我們繼續(xù)學(xué)習(xí)異常處理的余下部分和內(nèi)存管理。
    在上一篇中,我們知道在Try-Except塊中的過濾表達(dá)式值可以有以下三種:
EXCEPTION_EXECUTE_HANDLE (1)
EXCEPTION_CONTINUE_SEARCH (0)
EXCEPTION_CONTINUE_EXECUTION (-1)

EXCEPTION_EXECUTE_HANDLE 指示錯(cuò)誤處理程序執(zhí)行Except塊中的用戶定義異常處理語句。
EXCEPTION_CONTINUE_SEARCH 操作系統(tǒng)將繼續(xù)掃描堆棧以定位相應(yīng)的錯(cuò)誤處理程序。此時(shí)用戶定義的錯(cuò)誤處理程序?qū)⒌貌坏綀?zhí)行,如果找不到合適的其它錯(cuò)誤處理程序來執(zhí)行,此時(shí)系統(tǒng)將崩潰。
EXCEPTION_CONTINUE_EXECUTION 指示操作系統(tǒng)返回到出現(xiàn)異常處重試。在內(nèi)核模式中不能返回此種類型的異常,因?yàn)槟銢]辦法改變導(dǎo)致異常的情況。
更多的有關(guān)異常的信息可以調(diào)用以下兩個(gè)函數(shù)來獲得:(它們是微軟編譯器內(nèi)部所實(shí)現(xiàn)的,這也從一個(gè)側(cè)面說明了其它廠商的編譯器不能生成驅(qū)動(dòng)的原因)
注:微軟的驅(qū)動(dòng)程序使用一些專有的格式,比如,vxd就是使用LE格式,但普通Win32程序則使用PE格式文件,由于微軟不向其它廠商開放使用許可,所以象borland等廠商的編譯器不能生成驅(qū)動(dòng)程序。
GetExceptionCode() 返回當(dāng)前異常的數(shù)值代碼。它是一個(gè)NTSTATUS類型。此函數(shù)只能用于__except表達(dá)式和它后面的處理代碼中。
    GetExceptionInformation() 返回EXCEPTION_POINTERS結(jié)構(gòu)的內(nèi)存地址,此結(jié)構(gòu)包含異常相關(guān)的詳細(xì)信息,包括發(fā)生時(shí)的寄存器詳細(xì)內(nèi)容等。
程序中的bug可以導(dǎo)致異常并使系統(tǒng)調(diào)用異常處理機(jī)制。應(yīng)用程序開發(fā)者應(yīng)該熟悉Win32 API中的RaiseException函數(shù),它可以生成任意異常。在WDM驅(qū)動(dòng)程序中,你可以調(diào)用表中列出的例程。由于下面規(guī)則,我不能給你舉一個(gè)使用這些函數(shù)的例子:
    僅當(dāng)你知道存在一個(gè)異常處理代碼并知道你真正在做什么時(shí),才可以在非任意線程上下文下生成一個(gè)異常。
用于生成異常的服務(wù)函數(shù)
服務(wù)函數(shù)    描述
ExRaiseStatus    用指定狀態(tài)代碼觸發(fā)異常
ExRaiseAccessViolation    觸發(fā)STATUS_ACCESS_VIOLATION異常
ExRaiseDatatypeMisalignment    觸發(fā)STATUS_DATATYPE_MISALIGNMENT異常
特別地,不要通過觸發(fā)異常來告訴你的調(diào)用者你一般執(zhí)行狀態(tài)中的信息,你完全可以返回狀態(tài)代碼。應(yīng)該盡量避免使用異常,因?yàn)槎褩;鼐頇C(jī)制非常消耗資源。
系統(tǒng)錯(cuò)誤 BugCheck(導(dǎo)致系統(tǒng)不能繼續(xù)運(yùn)行的致命錯(cuò)誤)
Bug check是系統(tǒng)檢測到的錯(cuò)誤,一旦發(fā)現(xiàn)這種錯(cuò)誤,系統(tǒng)立即以一種可控制的方式關(guān)閉。許多內(nèi)核模式部件運(yùn)行時(shí)都進(jìn)行一致性檢測,如果某個(gè)系統(tǒng)部件發(fā)現(xiàn)一個(gè)不可恢復(fù)的錯(cuò)誤,將生成一個(gè)bug check。如果可能,所有內(nèi)核模式部件都先登記遇到的錯(cuò)誤,然后繼續(xù)運(yùn)行,而不是調(diào)用KeBugCheckEx,除非這種錯(cuò)誤將使系統(tǒng)本身變得不可靠。程序可以在任何IRQL上調(diào)用KeBugCheckEx。如果程序發(fā)現(xiàn)一個(gè)不可恢復(fù)的錯(cuò)誤,并且該程序繼續(xù)運(yùn)行將會(huì)破壞系統(tǒng),那么該程序就調(diào)用KeBugCheckEx函數(shù),這個(gè)函數(shù)將使系統(tǒng)以一種可控制的方式關(guān)閉。
當(dāng)內(nèi)核模式中出現(xiàn)不可恢復(fù)錯(cuò)誤時(shí),會(huì)出現(xiàn)一個(gè)稱為死亡藍(lán)屏(BSOD blue screen of death)的畫面,驅(qū)動(dòng)程序開發(fā)者應(yīng)該十分熟悉它。在內(nèi)部,這種錯(cuò)誤被稱為bug check,它的主要特征是,系統(tǒng)盡可能以正常的方式關(guān)閉并彈出一個(gè)死亡藍(lán)屏。一旦死亡藍(lán)屏出現(xiàn),則表明系統(tǒng)已經(jīng)死掉必須重啟動(dòng)。






可以按下面方式調(diào)用KeBugCheckEx:
KeBugCheckEx(bugcode, info1, info2, info3, info4);
bugcode是一個(gè)數(shù)值,指出出錯(cuò)的原因,info1、info2等是整型參數(shù),將出現(xiàn)在死亡藍(lán)屏中以幫助程序員了解錯(cuò)誤細(xì)節(jié)。該函數(shù)從不返回(!)。
我不將解釋死亡藍(lán)屏中的信息。Microsoft自己的bugcheck代碼在DDK頭文件bugcodes.h中列出;對(duì)該代碼的更完整解釋以及各種參數(shù)的含義可以在KBase文章Q103059 “Descriptions of Bug Codes for Windows NT”中找到。
如果需要,你也可以創(chuàng)建自己的bugcheck代碼。Microsoft定義的值是從1(APC_INDEX_MISMATCH)到0xDE(POOL_CORRUPTION_IN_FILE_AREA)之間的整數(shù)。為了創(chuàng)建你自己的bugcheck代碼,你需要定義一個(gè)整型常量(類似STATUS_SEVERITY_SUCCESS的狀態(tài)代碼),并指出customer標(biāo)志或非0的facility代碼。例如:
#define MY_BUGCHECK_CODE 0x002A0001
...
KeBugCheckEx(MY_BUGCHECK_CODE, 0, 0, 0, 0);
使用非0的facility代碼(例子中為42)或customer標(biāo)志(例子中為0)是為了與Microsoft使用的代碼區(qū)分開。
現(xiàn)在,我已經(jīng)告訴你如何生成自己的BSOD,那么我們什么時(shí)候使用它呢?回答是決不,或者僅在驅(qū)動(dòng)程序的內(nèi)部調(diào)試中使用。我們不可能寫出這樣的驅(qū)動(dòng)程序,它發(fā)現(xiàn)了一個(gè)錯(cuò)誤并且只有通過關(guān)閉系統(tǒng)才能解決。更好的做法是記錄這個(gè)錯(cuò)誤(使用錯(cuò)誤登記工具,如系統(tǒng)事件日志或WMI)并返回一個(gè)狀態(tài)碼。
這是在不得已的情況下為了盡可能減少損失而采取的措施。如果不這樣處理,系統(tǒng)將以一種不可預(yù)料的方式結(jié)束運(yùn)行,可能會(huì)造成不可挽回的損失。我曾經(jīng)在編寫驅(qū)動(dòng)時(shí)發(fā)生過源代碼無緣故地丟失的情況,這也是不健壯的驅(qū)動(dòng)程序產(chǎn)生的副作用。
下面我們將要一起學(xué)習(xí)核心層的內(nèi)存管理。在windows下由于采用了保護(hù)模式,內(nèi)存和實(shí)模式的dos下完全不同。記得在dos下我們可以訪問幾乎所有的內(nèi)存區(qū)域,如訪問硬盤的Bios記取硬的序列號(hào),主板的B ios區(qū)以記取主板序列號(hào),破除BIOS口令等。但在Windows下,系統(tǒng)對(duì)內(nèi)存進(jìn)行分區(qū)訪問,分為系統(tǒng)區(qū)和用戶區(qū)。并且由于采用了硬盤交換技術(shù),我們可以申請(qǐng)超過實(shí)際內(nèi)存大小很多的內(nèi)存。但此時(shí)我們得到的內(nèi)址地址再也不是內(nèi)存的物理地址了,取而代之是的虛擬內(nèi)存地址。
    雖然Windows這樣的帖心保護(hù)給我們寫應(yīng)用程序帶來了很大方便,不用再費(fèi)心考慮內(nèi)存是否夠用的問題,但也給我們寫驅(qū)動(dòng)程序帶來了很多麻煩。我們?cè)隍?qū)動(dòng)程序中使用內(nèi)存時(shí)要加倍小心,否則一丁點(diǎn)小的錯(cuò)誤都會(huì)要了系統(tǒng)的命(死亡藍(lán)屏或立即死機(jī))。
    首先我們來看幾個(gè)概念性的問題:
虛擬內(nèi)存:
    把一定的空間劃分成固定大小的塊,這些塊在技術(shù)上叫“頁”,X86系列的處理器的頁的大小為4k,alpha系統(tǒng)的為8K,這些頁可以放在內(nèi)存中,也可以放到磁盤上。對(duì)于程序來說,它們所使用的內(nèi)存就是由一系列處理過的內(nèi)存地址表示的區(qū)域(虛擬內(nèi)存地址),可能在物理內(nèi)存中也可能在磁盤上的文件中。它并不清楚所使用的內(nèi)存的真實(shí)形態(tài)。這一切由操作系統(tǒng)透明處理。當(dāng)然,應(yīng)用程序也可以顯式地申請(qǐng)操作系統(tǒng)告訴它這些內(nèi)存的物理地址。
如上圖所示,假設(shè)一個(gè)程序使用了24K的內(nèi)存,我們把它分成6頁。如果系統(tǒng)只有12K的物理內(nèi)存可用時(shí),它會(huì)把頻繁使用或?qū)⒁褂玫膮^(qū)域放到物理內(nèi)存中,以加快運(yùn)行速度和滿足內(nèi)存分配需要。但如果所有的應(yīng)用程序經(jīng)常所需的內(nèi)存總和大大超過物理內(nèi)存的數(shù)量,那操作系統(tǒng)就會(huì)不斷地在物理內(nèi)存和磁盤文件之間換進(jìn)換出這些分頁塊,雖然還能運(yùn)行程序,但這將導(dǎo)致系統(tǒng)變得很慢。這是用時(shí)間換空間的方法了。
分頁一(物理內(nèi)存中)
分頁二(磁盤上)
分頁三(物理內(nèi)存中)
分頁四(磁盤上)
分頁五(物理內(nèi)存中)
分頁六(磁盤上)









可分頁內(nèi)存和不可分頁內(nèi)存:
    上面講了內(nèi)存可以分頁,但并不是每個(gè)分頁內(nèi)存區(qū)都可以換到磁盤文件中。我們把可以換出來的分頁叫作“可分頁內(nèi)存”,只能永久駐留在物理內(nèi)存中而不能被換出來頁的叫作“非分頁內(nèi)存”。
如果在DISPATCH_LEVEL或者更高的中斷級(jí)中訪問分頁內(nèi)存,就會(huì)引起缺頁故障,內(nèi)核會(huì)崩潰。如果在PASSIVE_LEVEL中斷級(jí)訪問沒有駐留在物理內(nèi)存中的分頁內(nèi)存時(shí),內(nèi)核會(huì)阻塞我們的線程,直到內(nèi)存管理器把此頁重新裝回物理內(nèi)存中。
    不要隨意使用非分頁內(nèi)存,因?yàn)橄到y(tǒng)的資源是有限的,如果永久駐留在物理內(nèi)存中的頁太多,將導(dǎo)致可分頁內(nèi)存更加頻繁地進(jìn)行交換,降低系統(tǒng)性能。
    在開發(fā)驅(qū)動(dòng)程序時(shí)我們必須遵守這樣幾個(gè)原則:
決不(或幾乎從不)直接引用用戶模式的內(nèi)存地址。
因?yàn)槲覀儾荒艽_切知道用戶模式內(nèi)存地址所指向的真實(shí)物理地址。
執(zhí)行在高于或等于DISPATCH_LEVEL級(jí)的代碼不可以引發(fā)頁故障。
在驅(qū)動(dòng)程序的checked版中,你可以使用PAGED_CODE預(yù)處理宏(在wdm.h中聲明)來幫助發(fā)現(xiàn)有違背這個(gè)規(guī)則的代碼。例如:
NTSTATUS DispatchPower(PDEVICE_OBJECT fdo, PIRP Irp)
{
  PAGED_CODE()
  ...
}
PAGED_CODE包含條件編譯語句。在checked-build方式中,如果當(dāng)前IRQL太高,它就打印出一行信息并生成一個(gè)斷言失敗。在free-build方式中,它不做任何事。如果測試驅(qū)動(dòng)程序時(shí)包含DispatchPower代碼的頁正好在內(nèi)存中,那么你不會(huì)發(fā)現(xiàn)已經(jīng)在一個(gè)提升的IRQL上調(diào)用了DispatchPower函數(shù)。即使這樣,PAGED_CODE仍能查出問題。如果該頁碰巧不在內(nèi)存中,系統(tǒng)將產(chǎn)生一個(gè)bug check。
我們可以調(diào)用以下幾個(gè)函數(shù)來分配可分而和非可分頁內(nèi)存塊:
ExAllocatePool(…)
調(diào)用方式如下:
PVOID p = ExAllocatePool(type, nbytes);
type參數(shù)是表2中列出的POOL_TYPE枚舉常量,nbytes是要分配的字節(jié)數(shù)。返回值是一個(gè)內(nèi)核模式虛擬地址指針,指向已分配的內(nèi)存塊。如果內(nèi)存不足,則返回一個(gè)NULL指針。如果指定的內(nèi)存池類型為“must succeed”類型,即NonPagedPoolMustSucceed或NonPagedPoolCacheAlignedMustS,那么內(nèi)存不足將導(dǎo)致一個(gè)代碼為MUST_SUCCEED_POOL_EMPTY的bug check。
表2
內(nèi)存池類型    描述
NonPagedPool    從非分頁內(nèi)存池中分配內(nèi)存
PagedPool    從分頁內(nèi)存池中分配內(nèi)存
NonPagedPoolMustSucceed    從非分頁內(nèi)存池中分配內(nèi)存,如果不能分配則產(chǎn)生bugcheck
NonPagedPoolCacheAligned    從非分頁內(nèi)存池中分配內(nèi)存,并確保內(nèi)存與CPU cache對(duì)齊
NonPagedPoolCacheAlignedMustS    與NonPagedPoolCacheAligned類似,但如果不能分配則產(chǎn)生bugcheck
PagedPoolCacheAligned    從分頁內(nèi)存池中分配內(nèi)存,并確保內(nèi)存與CPU cache對(duì)齊
調(diào)用ExAllocatePool時(shí)的最基本原則是被分配內(nèi)存塊是否可以交換出內(nèi)存。這取決于驅(qū)動(dòng)程序的哪一部分需要訪問這塊內(nèi)存。如果在大于或等于DISPATCH_LEVEL級(jí)上使用該內(nèi)存塊,那么必須從非分頁池中分配內(nèi)存。如果你總是在低于DISPATCH_LEVEL級(jí)上使用內(nèi)存塊,那么既可以從非分頁池中分配內(nèi)存也可以從分頁池中分配內(nèi)存。
你獲得的內(nèi)存塊至少是按8字節(jié)邊界對(duì)齊的。如果把某結(jié)構(gòu)的實(shí)例放到分配的內(nèi)存中,那么編譯器賦予結(jié)構(gòu)成員的4或8字節(jié)偏移在新內(nèi)存中也將是4或8字節(jié)偏移。但在某些RISC平臺(tái)上,結(jié)構(gòu)成員可能以雙字和四字對(duì)齊。出于性能上的考慮,希望內(nèi)存塊能適合處理器cache行的最少可能數(shù),使用XxxCacheAligned類型代碼可以達(dá)到這個(gè)要求。如果請(qǐng)求的內(nèi)存多于一頁,那么內(nèi)存塊將從頁的邊界開始。
ExAllocatePoolWithTag
調(diào)用ExAllocatePool是從內(nèi)核模式堆中分配內(nèi)存的標(biāo)準(zhǔn)方式。另一個(gè)函數(shù)ExAllocatePoolWithTag,與ExAllocatePool稍有不同,它提供了一個(gè)有用的額外特征。當(dāng)使用ExAllocatePoolWithTag時(shí),系統(tǒng)在你要求的內(nèi)存外又額外地多分配了4個(gè)字節(jié)的標(biāo)簽。這個(gè)標(biāo)簽占用了開始的4個(gè)字節(jié),位于返回指針?biāo)赶虻刂返那懊?。調(diào)試時(shí),如果你查看分配的內(nèi)存塊會(huì)看到這個(gè)標(biāo)簽,它幫助你識(shí)別有問題的內(nèi)存塊。例如:
PVOID p = ExAllocatePoolWithTag(PagedPool, 42, 'KNUJ');
在這里,我使用了一個(gè)32位整數(shù)常量作為標(biāo)簽值。在小結(jié)尾的計(jì)算機(jī)如x86上,組成這個(gè)標(biāo)簽的4個(gè)字節(jié)的順序與正常拼寫相反。
WDM.H中聲明的內(nèi)存分配函數(shù)受一個(gè)預(yù)處理宏P(guān)OOL_TAGGING控制。WDM.H(NTDDK.H中也是)中無條件地定義了POOL_TAGGING,結(jié)果,無標(biāo)簽的函數(shù)實(shí)際上是宏,它真正執(zhí)行的是有標(biāo)簽函數(shù)并加入標(biāo)簽‘ mdW’(指明為WDM的內(nèi)存塊)。如果在未來版本的DDK中沒有定義POOL_TAGGING,那么帶標(biāo)簽函數(shù)將成為無標(biāo)簽函數(shù)的宏。Microsoft現(xiàn)在還沒打算改變POOL_TAGGING的設(shè)置。
由于POOL_TAGGING宏的存在,當(dāng)你在程序中調(diào)用ExAllocatePool時(shí),最終被調(diào)用的將是ExAllocatePoolWithTag。如果你關(guān)閉了該宏,自己去調(diào)用ExAllocatePool,但ExAllocatePool內(nèi)部仍舊調(diào)用ExAllocatePoolWithTag并帶一個(gè)‘enoN’(即None)的標(biāo)簽。因此你無法避免產(chǎn)生內(nèi)存標(biāo)簽。所以你應(yīng)該明確地調(diào)用ExAllocatePoolWithTag并加上一個(gè)你認(rèn)為有意義的標(biāo)簽。實(shí)際上,Microsoft強(qiáng)烈鼓勵(lì)你這樣做。
ExAllocatePool的其它形式
盡管ExAllocatePoolWithTag函數(shù)是分配堆內(nèi)存時(shí)應(yīng)該使用的函數(shù),但在某些特殊場合你也可以使用該函數(shù)的另外兩種形式: 
•    ExAllocatePoolWithQuota 分配一塊內(nèi)存并充入當(dāng)前線程的調(diào)度配額中,該函數(shù)僅用于頂層驅(qū)動(dòng)程序,如文件系統(tǒng)驅(qū)動(dòng)程序或其它運(yùn)行在非任意線程上下文中的驅(qū)動(dòng)程序。 
•    ExAllocatePoolWithQuotaTag 同上,但加入一個(gè)標(biāo)簽。 
釋放內(nèi)存塊
調(diào)用ExFreePool可以釋放由ExAllocatePool分配的內(nèi)存塊:
ExFreePool((PVOID) p);
你確實(shí)需要記錄分配的內(nèi)存以便在該內(nèi)存不再需要時(shí)釋放它,因?yàn)闆]有人為你做這些事。例如,在AddDevice函數(shù)中,有一個(gè)IoRegisterDeviceInterface調(diào)用,該函數(shù)存在副作用:它分配了一塊內(nèi)存以保存接口名。你有責(zé)任在以后釋放該內(nèi)存。
不用說,訪問從內(nèi)核模式內(nèi)存池中分配來的內(nèi)存必須格外小心。因?yàn)轵?qū)動(dòng)程序代碼可能執(zhí)行在處理器的最高特權(quán)模式下,在這里,系統(tǒng)對(duì)內(nèi)存數(shù)據(jù)沒有任何保護(hù)。
運(yùn)行時(shí)控制分頁能力
表3列出了一些服務(wù)函數(shù),你可以在運(yùn)行時(shí)使用它們調(diào)整驅(qū)動(dòng)程序的分頁布局。這些函數(shù)的功能是釋放被不再需要的代碼和數(shù)據(jù)所占用的物理內(nèi)存。在第八章中,我將講述如何向電源管理器寄存你的設(shè)備,這樣,在一段不活動(dòng)時(shí)期后設(shè)備可以自動(dòng)掉電。掉電期間是釋放鎖定內(nèi)存頁的最佳時(shí)期。
表3. 動(dòng)態(tài)鎖定和解鎖驅(qū)動(dòng)程序占用內(nèi)存頁的例程
服務(wù)函數(shù)    描述
MmLockPagableCodeSection    鎖定含有給定地址的代碼段
MmLockPagableDataSection    鎖定含有給定地址的數(shù)據(jù)段
MmLockPagableSectionByHandle    用MmLockPagableCodeSection返回的句柄鎖定代碼段(僅用于Windows 2000)
MmPageEntireDriver    解鎖所有屬于某驅(qū)動(dòng)程序的頁
MmResetDriverPaging    恢復(fù)整個(gè)驅(qū)動(dòng)程序的編譯時(shí)分頁屬性
MmUnlockPagableImageSection    為一個(gè)鎖定代碼段或數(shù)據(jù)段解鎖
限于篇幅,這類函數(shù)詳細(xì)的用法可以參見DDK文檔說明。這部分我們主要學(xué)習(xí)了內(nèi)存管理部分。如我們前面所說,內(nèi)存管理在內(nèi)核編程中是非常重要的。通常我們?cè)趹?yīng)用程序編寫中的不良習(xí)慣都不應(yīng)該帶到驅(qū)動(dòng)程序開發(fā)中。你必須清楚一點(diǎn):在用戶態(tài)最多只是彈出一個(gè)提示框的故障,在內(nèi)核模式中等待你的將是“死機(jī)”。
本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
用WDM開發(fā)USB驅(qū)動(dòng)程序
DriverEntry程序
The Windows Driver Model Simplifies Managemen...
電腦故障不求人—藍(lán)屏代碼及解決辦法
虛擬地址空間 (Windows Drivers)
Window XP驅(qū)動(dòng)開發(fā)(二十)Window驅(qū)動(dòng)的內(nèi)存管理
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服