• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            aurain
            技術(shù)文摘
            posts - 137,  comments - 268,  trackbacks - 0
            原文地址 http://hi.baidu.com/wukongafei/blog/item/7f96853e14fb47fe838b1345.html

            Windows NT為每個(gè)硬件中斷和少數(shù)軟件事件賦予了一個(gè)優(yōu)先級(jí),即中斷請(qǐng)求級(jí)(interrupt request level - IRQL)。IRQL為單CPU上的活動(dòng)提供了同步方法,它基于下面規(guī)則:

            一旦某CPU執(zhí)行在高于PASSIVE_LEVEL的IRQL上時(shí),該CPU上的活動(dòng)僅能被擁有更高IRQL的活動(dòng)搶先。

            圖4-1顯示了x86平臺(tái)上的IRQL值范圍。(通常,這個(gè)IRQL數(shù)值要取決于你所面對(duì)的平臺(tái)) 用戶模式程序執(zhí)行在PASSIVE_LEVEL上,可以被任何執(zhí)行在高于該IRQL上的活動(dòng)搶先。許多設(shè)備驅(qū)動(dòng)程序例程也執(zhí)行在PASSIVE_LEVEL上。第二章中討論的DriverEntryAddDevice例程就屬于這類,大部分IRP派遣例程也屬于這類。

            某些公共驅(qū)動(dòng)程序例程執(zhí)行在DISPATCH_LEVEL上,而DISPATCH_LEVEL級(jí)要比PASSIVE_LEVEL級(jí)高。這些公共例程包括StartIo例程,DPC(推遲過程調(diào)用)例程,和其它一些例程。這些例程的共同特點(diǎn)是,它們都需要訪問設(shè)備對(duì)象和設(shè)備擴(kuò)展中的某些域,它們都不受派遣例程的干擾或互相干擾。當(dāng)任何一個(gè)這樣的例程運(yùn)行時(shí),上面陳述的規(guī)則可以保證它們不被任何驅(qū)動(dòng)程序的派遣例程搶先,因?yàn)榕汕怖瘫旧韴?zhí)行在更低級(jí)的IRQL上。另外,它們也不會(huì)被同類例程搶先,因?yàn)槟切├踢\(yùn)行的IRQL與它們自己的相同。只有擁有更高IRQL的活動(dòng)才能搶先它們。

            注意
            派遣例程(Dispatch routine)和DISPATCH_LEVEL級(jí)名稱類似。之所以稱做派遣例程是因?yàn)镮/O管理器向這些函數(shù)派遣I/O請(qǐng)求。而存在派遣級(jí)(DISPATCH_LEVEL)這個(gè)名稱是因?yàn)閮?nèi)核線程派遣器運(yùn)行在這個(gè)IRQL上,它決定下一次該執(zhí)行哪個(gè)線程。(現(xiàn)在,線程調(diào)度程序通常運(yùn)行在SYNCH_LEVEL級(jí)上)

            圖4-1. 中斷請(qǐng)求級(jí)

            在DISPATCH_LEVEL級(jí)和PROFILE_LEVEL級(jí)之間是各種硬件中斷級(jí)。通常,每個(gè)有中斷能力的設(shè)備都有一個(gè)IRQL,它定義了該設(shè)備的中斷優(yōu)先級(jí)別。WDM驅(qū)動(dòng)程序只有在收到一個(gè)副功能碼為IRP_MN_START_DEVICE的IRP_MJ_PNP請(qǐng)求后,才能確定其設(shè)備的IRQL。設(shè)備的配置信息作為參數(shù)傳遞給該請(qǐng)求,而設(shè)備的IRQL就包含在這個(gè)配置信息中。我們通常把設(shè)備的中斷級(jí)稱為設(shè)備IRQL,或DIRQL。

            其它IRQL級(jí)的含義有時(shí)需要依靠具體的CPU結(jié)構(gòu)。這些IRQL通常僅被Windows NT內(nèi)核內(nèi)部使用,因此它們的含義與設(shè)備驅(qū)動(dòng)程序的編寫不是特別密切相關(guān)。例如,我將要在本章后面詳細(xì)討論的APC_LEVEL,當(dāng)系統(tǒng)在該級(jí)上為某線程調(diào)度APC(異步過程調(diào)用)例程時(shí)不會(huì)被同一CPU上的其它線程所干擾。在HIGH_LEVEL級(jí)上系統(tǒng)可以執(zhí)行一些特殊操作,如系統(tǒng)休眠前的內(nèi)存快照、處理bug check、處理假中斷,等等。

            IRQL的變化

            為了演示IRQL的重要性,參見圖4-2,該圖顯示了發(fā)生在單CPU上的一系列事件。在時(shí)間序列的開始處,CPU執(zhí)行在PASSIVE_LEVEL級(jí)上。在t1時(shí)刻,一個(gè)中斷到達(dá),它的服務(wù)例程執(zhí)行在DIRQL1上,該級(jí)是在DISPATCH_LEVEL和PROFILE_LEVEL之間的某個(gè)DIRQL。在t2時(shí)刻,另一個(gè)中斷到達(dá),它的服務(wù)例程執(zhí)行在DIRQL2上,比DIRQL1低一級(jí)。我們討論過搶先規(guī)則,所以CPU將繼續(xù)服務(wù)于第一個(gè)中斷。當(dāng)?shù)谝粋€(gè)中斷服務(wù)例程在t3時(shí)刻完成時(shí),該中斷服務(wù)程序可能會(huì)請(qǐng)求一個(gè)DPC。而DPC例程是執(zhí)行在DISPATCH_LEVEL上。所以當(dāng)前存在的未執(zhí)行的最高優(yōu)先級(jí)的活動(dòng)就是第二個(gè)中斷的服務(wù)例程,所以系統(tǒng)接著執(zhí)行第二個(gè)中斷的服務(wù)例程。這個(gè)例程在t4時(shí)刻結(jié)束,假設(shè)這之后再?zèng)]有其它中斷發(fā)生,CPU將降到DISPATCH_LEVEL級(jí)上執(zhí)行第一個(gè)中斷的DPC例程。當(dāng)DPC例程在t5時(shí)刻完成后,IRQL又落回到原來的PASSIVE_LEVEL級(jí)。

            圖4-2. 變化中的中斷優(yōu)先級(jí)

            基本同步規(guī)則

            遵循下面規(guī)則,你可以利用IRQL的同步效果:

            所有對(duì)共享數(shù)據(jù)的訪問都應(yīng)該在同一(提升的)IRQL上進(jìn)行。

            換句話說,不論何時(shí)何地,如果你的代碼訪問的數(shù)據(jù)對(duì)象被其它代碼共享,那么你應(yīng)該使你的代碼執(zhí)行在高于PASSIVE_LEVEL的級(jí)上。一旦越過PASSIVE_LEVEL級(jí),操作系統(tǒng)將不允許同IRQL的活動(dòng)相互搶先,從而防止了潛在的沖突。然而這個(gè)規(guī)則不足以保護(hù)多處理器機(jī)器上的數(shù)據(jù),在多處理器機(jī)器中你還需要另外的防護(hù)措施——自旋鎖(spin lock)。如果你僅關(guān)心單CPU上的操作,那么使用IRQL就可以解決所有同步問題。但事實(shí)上,所有WDM驅(qū)動(dòng)程序都必須設(shè)計(jì)成能夠運(yùn)行在多處理器的系統(tǒng)上。

            IRQL與線程優(yōu)先級(jí)

            線程優(yōu)先級(jí)是與IRQL非常不同的概念。線程優(yōu)先級(jí)控制著線程調(diào)度器的調(diào)度動(dòng)作,決定何時(shí)搶先運(yùn)行線程以及下一次運(yùn)行什么線程。然而,當(dāng)IRQL級(jí)高于或等于DISPATCH_LEVEL級(jí)時(shí)線程切換停止,無論當(dāng)前活動(dòng)的是什么線程都將保持活動(dòng)狀態(tài)直到IRQL降到DISPATCH_LEVEL級(jí)之下。而此時(shí)的“優(yōu)先級(jí)”僅指IRQL本身,由它控制到底哪個(gè)活動(dòng)該執(zhí)行,而不是該切換到哪個(gè)線程的上下文。

            IRQL和分頁(yè)

            執(zhí)行在提升的IRQL級(jí)上的一個(gè)后果是,系統(tǒng)將不能處理頁(yè)故障(系統(tǒng)在APC級(jí)處理頁(yè)故障)。這意味著:

            執(zhí)行在高于或等于DISPATCH_LEVEL級(jí)上的代碼絕對(duì)不能造成頁(yè)故障。

            這也意味著執(zhí)行在高于或等于DISPATCH_LEVEL級(jí)上的代碼必須存在于非分頁(yè)內(nèi)存中。此外,所有這些代碼要訪問的數(shù)據(jù)也必須存在于非分頁(yè)內(nèi)存中。最后,隨著IRQL的提升,你能使用的內(nèi)核模式支持例程將會(huì)越來越少。

            DDK文檔中明確指出支持例程的IRQL限定。例如,KeWaitForSingleObject例程有兩個(gè)限定:

            • 調(diào)用者必須運(yùn)行在低于或等于DISPATCH_LEVEL級(jí)上。
            • 如果調(diào)用中指定了非0的超時(shí),那么調(diào)用者必須嚴(yán)格地運(yùn)行在低于DISPATCH_LEVEL的IRQL上。

            上面這兩行想要說明的是:如果KeWaitForSingleObject真的被阻塞了指定長(zhǎng)的時(shí)間(你指定的非0超時(shí)),那么你必定運(yùn)行在低于DISPATCH_LEVEL的IRQL上,因?yàn)橹挥性谶@樣的IRQL上線程阻塞才是允許的。如果你所做的一切就是為了檢測(cè)事件是否進(jìn)入信號(hào)態(tài),則可以執(zhí)行在DISPATCH_LEVEL級(jí)上。但你不能在ISR或其它運(yùn)行在高于DISPATCH_LEVEL級(jí)上的例程中調(diào)用KeWaitForSingleObject例程。

            IRQL的隱含控制

            在大部分時(shí)間里,系統(tǒng)都是在正確的IRQL上調(diào)用驅(qū)動(dòng)程序中的例程。雖然我們還沒有詳細(xì)地討論過這些例程,但我希望舉一個(gè)例子來表達(dá)這句話的含義。你首先遇到的I/O請(qǐng)求就是I/O管理器調(diào)用你的某個(gè)派遣例程來處理一個(gè)IRP。這個(gè)調(diào)用發(fā)生在PASSIVE_LEVEL級(jí)上,因?yàn)槟阈枰枞{(diào)用者線程,還需要調(diào)用其它支持例程。當(dāng)然,你不能在更高的IRQL級(jí)上阻塞一個(gè)線程,而PASSIVE_LEVEL也是唯一能讓你無限制地調(diào)用任何支持例程的IRQL級(jí)。

            如果你的派遣例程通過調(diào)用IoStartPacket來排隊(duì)IRP,那么你第一個(gè)遇到的請(qǐng)求將發(fā)生在I/O管理器調(diào)用你的StartIo例程時(shí)。這個(gè)調(diào)用發(fā)生在DISPATCH_LEVEL級(jí),因?yàn)橄到y(tǒng)需要在沒有其它例程(這些例程能在隊(duì)列中插入或刪除IRP)干擾的情況下訪問I/O隊(duì)列。回想一下前面提到的規(guī)則:所有對(duì)共享數(shù)據(jù)的訪問都應(yīng)該在同一(提升的)IRQL級(jí)上進(jìn)行。因?yàn)槊總€(gè)能訪問IRP隊(duì)列的例程都執(zhí)行在DISPATCH_LEVEL級(jí)上,所以任何例程在操作隊(duì)列期間都不可能被打斷(僅指在單CPU系統(tǒng))。

            之后,設(shè)備可能生成一個(gè)中斷,而該中斷的服務(wù)例程(ISR)將在DIRQL級(jí)上被調(diào)用。設(shè)備上的某些寄存器也許不能被安全地共享。但是,如果你僅在DIRQL上訪問那些寄存器,可以保證在單CPU計(jì)算機(jī)上沒人能妨礙你的ISR執(zhí)行。如果驅(qū)動(dòng)程序的其它代碼需要訪問這些關(guān)鍵的硬件寄存器,你應(yīng)該讓這些代碼僅執(zhí)行在DIRQL級(jí)上。KeSynchronizeExecution服務(wù)函數(shù)可以幫助你強(qiáng)制執(zhí)行這個(gè)規(guī)則,我將在第七章的“與中斷處理連接”段中討論這個(gè)函數(shù)。

            再往后,你應(yīng)該安排一個(gè)DPC調(diào)用。DPC例程執(zhí)行在DISPATCH_LEVEL級(jí)上,它們需要訪問你的IRP隊(duì)列,并取出隊(duì)列中的下一個(gè)請(qǐng)求,然后把這個(gè)請(qǐng)求發(fā)送給StartIo例程。你可以調(diào)用IoStartNextPacket服務(wù)函數(shù)從隊(duì)列中提取下一個(gè)請(qǐng)求,但必須在DISPATCH_LEVEL級(jí)上調(diào)用。該函數(shù)在返回前將調(diào)用你的StartIo例程。注意,這里的IRQL吻合得相當(dāng)巧妙:隊(duì)列訪問,調(diào)用IoStartNextPacket,和調(diào)用StartIo都需要發(fā)生在DISPATCH_LEVEL級(jí)上,并且系統(tǒng)也是在這個(gè)IRQL級(jí)上調(diào)用DPC例程的。

            盡管明確地控制IRQL也是可能的,但幾乎沒有理由這樣做,因?yàn)槟阈枰腎RQL和系統(tǒng)調(diào)用你時(shí)使用的IRQL總是相應(yīng)的。所以不必不時(shí)地提高IRQL,例程希望的IRQL和系統(tǒng)使用的IRQL幾乎總是正確對(duì)應(yīng)的。

            IRQL的明確控制

            如果必要,你還可以在當(dāng)前處理器上臨時(shí)提升IRQL,然后再降回到原來的IRQL,使用KeRaiseIrqlKeLowerIrql函數(shù)。下面代碼運(yùn)行在PASSIVE_LEVEL級(jí)上:

            KIRQL oldirql;        <--1
                        ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);    <--2
                        KeRaiseIrql(DISPATCH_LEVEL, &oldirql);     <--3
                        ...
                        KeLowerIrql(oldirql);       <--4

            1. KIRQL定義了用于保存IRQL值的數(shù)據(jù)類型。我們需要一個(gè)變量來保存當(dāng)前IRQL。
            2. 這個(gè)ASSERT斷定了調(diào)用KeRaiseIrql的必要條件:新IRQL必須大于或等于當(dāng)前IRQL。如果這個(gè)關(guān)系不成立,KeRaiseIrql將導(dǎo)致bug check。(即用死亡藍(lán)屏報(bào)告一個(gè)致命錯(cuò)誤)
            3. KeRaiseIrql把當(dāng)前的IRQL提升到第一個(gè)參數(shù)指定的IRQL級(jí)上。它同時(shí)還把當(dāng)前的IRQL值保存到第二個(gè)參數(shù)指定的變量中。在這個(gè)例子中,我們把IRQL提升到DISPATCH_LEVEL級(jí),并把原來的IRQL級(jí)保存到oldirql變量中。
            4. 執(zhí)行完任何需要在提升的IRQL上執(zhí)行的代碼后,我們調(diào)用KeLowerIrql把IRQL降低到調(diào)用KeRaiseIrql時(shí)的級(jí)別。

            DDK文檔中提到,你必須用與你最近的KeRaiseIrql調(diào)用所返回的值調(diào)用KeLowerIrql。這在大的方面是對(duì)的,因?yàn)槟闾嵘薎RQL就必須再降低它。然而,由于你調(diào)用的代碼或者調(diào)用你的代碼所做的各種假設(shè)會(huì)使后面的決定變得不正確。所以,文檔中的這句話從嚴(yán)格意義上講是不正確的。應(yīng)用到KeLowerIrql函數(shù)的唯一的規(guī)則就是新IRQL必須低于或等于當(dāng)前IRQL。

            當(dāng)系統(tǒng)調(diào)用你的驅(qū)動(dòng)程序例程時(shí),你降低了IRQL(系統(tǒng)調(diào)用你的例程時(shí)使用的IRQL,或你的例程希望執(zhí)行的IRQL),這是一個(gè)錯(cuò)誤,而且是嚴(yán)重錯(cuò)誤,盡管你在例程返回前又提升了IRQL。這種打破同步的結(jié)果是,某些活動(dòng)可以搶先你的例程,并能訪問你的調(diào)用者認(rèn)為不能被共享的數(shù)據(jù)對(duì)象。

            有一個(gè)函數(shù)專用于把IRQL提升到DISPATCH_LEVEL級(jí):

            KIRQL oldirql = KeRaiseIrqlToDpcLevel();
                        ...
                        KeLowerIrql(oldirql)

            注意:該函數(shù)僅在NTDDK.H中聲明,WDM.H中并沒有聲明該函數(shù),因此WDM驅(qū)動(dòng)程序不應(yīng)該使用該函數(shù)。

            posted on 2009-08-13 11:34 閱讀(1141) 評(píng)論(0)  編輯 收藏 引用 所屬分類: windows驅(qū)動(dòng)

            <2008年10月>
            2829301234
            567891011
            12131415161718
            19202122232425
            2627282930311
            2345678

            常用鏈接

            留言簿(17)

            隨筆分類(138)

            隨筆檔案(137)

            網(wǎng)絡(luò)開發(fā)

            最新隨筆

            搜索

            •  

            積分與排名

            • 積分 - 497604
            • 排名 - 36

            最新隨筆

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            亚洲国产精久久久久久久| 久久久久亚洲精品男人的天堂| 久久久久这里只有精品| 99久久精品国产一区二区蜜芽| 人人狠狠综合久久88成人| 中文字幕亚洲综合久久菠萝蜜| 久久天天躁狠狠躁夜夜2020老熟妇| 91精品观看91久久久久久| 久久精品国产免费一区| 99久久精品免费观看国产| 婷婷久久综合九色综合98| 国产精品免费久久| 久久天天躁狠狠躁夜夜av浪潮| 日本加勒比久久精品| 久久久久久午夜精品| 久久亚洲精品无码VA大香大香| 亚洲AV日韩精品久久久久久久| 久久99国产精品尤物| 国产精品xxxx国产喷水亚洲国产精品无码久久一区 | 久久国产精品无码HDAV| 2021久久国自产拍精品| 久久99精品久久久久久不卡| 日韩久久无码免费毛片软件| 亚洲第一极品精品无码久久| 久久电影网一区| 日本加勒比久久精品| 久久偷看各类wc女厕嘘嘘| 一本久久久久久久| 一本色道久久综合狠狠躁| 久久综合中文字幕| 久久婷婷人人澡人人爽人人爱| 狠狠狠色丁香婷婷综合久久俺| 欧美精品福利视频一区二区三区久久久精品 | 精品欧美一区二区三区久久久| 久久只有这精品99| 中文字幕成人精品久久不卡| 久久久无码精品亚洲日韩蜜臀浪潮 | 国产精品美女久久久久网| 欧美亚洲另类久久综合婷婷| 国产精品久久影院| 久久婷婷五月综合97色直播|