• <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>
            posts - 297,  comments - 15,  trackbacks - 0
            簡介

            一般來說,簡單的異步(Asynchronous) 調(diào)用是這樣一種調(diào)用方式:發(fā)起者請求一個異步調(diào)用,通知執(zhí)行者,然后處理其他工作,在某一個同步點(diǎn)等待執(zhí)行者的完成;執(zhí)行者執(zhí)行調(diào)用的實(shí)際操作,完成后通 知發(fā)起者。可以看出,在異步調(diào)用中有兩種角色:發(fā)起者和執(zhí)行者,它們都是能主動運(yùn)行的對象,我們稱為主動對象,同時還有一個同步點(diǎn),主動對象在同步點(diǎn)協(xié)調(diào) 同步。在本文中,我們討論主要是通用計(jì)算機(jī)、多進(jìn)程多線程的分時操作系統(tǒng)上的異步調(diào)用。在操作系統(tǒng)的角度上來看,主動對象包括了進(jìn)程、線程和硬件上的IC等,至于中斷,可以看作總是在某個進(jìn)程或者線程的上下文借用一下CPU。而同步操作可以通過操作系統(tǒng)得各種同步機(jī)制:互斥鎖,信號燈等等來完成。

            我們可以先看看異步調(diào)用在Windows(本文中一般不加指出的話,都是特指NT/2000)讀寫文件中的應(yīng)用。Windows中的ReadFile和WriteFile都提供了異步的接口。以ReadFile為例,

            BOOL ReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped);

            如果最后一個參數(shù)lpOverlapped不為NULL,并且文件以FILE_FLAG_OVERLAPPED 標(biāo)志打開,那么這個調(diào)用就是異步的:ReadFile會立刻返回,如果操作沒有立刻完成(返回FALSE并且GetLastError()返回 ERROR_IO_PENDING),那么調(diào)用者可以在某個時刻通過WaitForSingleObject等函數(shù)來等待中的hEvent來等待操作完成 (可能已經(jīng)完成)進(jìn)行同步,當(dāng)操作完成以后,可以調(diào)用GetOverlappedResult者獲得操作的結(jié)果,比如是否成功,讀取了多少字節(jié)等等。這里 的發(fā)起者就是應(yīng)用程序,而執(zhí)行者就是操作系統(tǒng)本身,至于執(zhí)行者是怎么執(zhí)行的,我們會在后面的篇幅討論。而兩者的同步就是通過一個Windows Event來完成。

            把這個異步調(diào)用的過程再抽象和擴(kuò)展一些,我們可以把異步調(diào)用需要解決的問題歸結(jié)為兩個:一個是執(zhí)行的動力,另一個是主動對象的調(diào)度。簡單來說,前者是各個主動對象(線程、進(jìn)程或者一些代碼)是如何獲得CPU,后者是各個主動對象如何協(xié)同工作, 保證操作的流程是協(xié)調(diào)正確的。一般來說,進(jìn)程和線程都可以由操作系統(tǒng)直接調(diào)度而獲得CPU,而更細(xì)粒度的,比如一些代碼的調(diào)度,往往就需要一個更復(fù)雜的模 型(比如在操作系統(tǒng)內(nèi)部的實(shí)現(xiàn),這時候線程的粒度太粗了)。而主動對象的調(diào)度,當(dāng)參與者較少的時候,可以通過基本的同步機(jī)制來完成,在更復(fù)雜的情況下,可 能通過一個schedule機(jī)制來做會更實(shí)際一些。

            動力和調(diào)度

            如前所述,異步調(diào)用主要需要解決兩 個問題:執(zhí)行的動力和執(zhí)行的調(diào)度。最普遍的情況就是,一個主導(dǎo)流程的調(diào)用者進(jìn)程(線程),一個或多個工作者進(jìn)程(線程),通過操作系統(tǒng)提供的同步機(jī)制來完 成異步調(diào)用。這個同步機(jī)制在擴(kuò)展化的情形下,是一個或多個柵欄Barrier,對應(yīng)于每個同步的執(zhí)行點(diǎn)。所有需要在這個執(zhí)行點(diǎn)同步的主動對象會等待相應(yīng)的 Barrier,直到所有對象都完成。在一些簡化的情形,比如說工作者并不關(guān)心調(diào)用者的同步,那么這個Barrier可以簡化成信號燈,在只有一個工作者 的情況下,可以簡化成一個Windows事件Event或者條件變量 Condition Variable。

            現(xiàn)在來考慮復(fù)雜的情 形。假設(shè)我們用一些線程來協(xié)作完成一項(xiàng)工作,各個線程的執(zhí)行之間有先后順序上的限制,而操作系統(tǒng)就是這項(xiàng)工作的調(diào)度者,負(fù)責(zé)在適當(dāng)?shù)臅r候調(diào)度適當(dāng)?shù)木€程來 獲得CPU。顯然,并發(fā)執(zhí)行中的一個線程對于另外一個線程來說,本質(zhì)上就是異步的,假如它們之間有調(diào)用關(guān)系,那也就是一個異步調(diào)用。而操作系統(tǒng)可以通過基 本的同步機(jī)制使得合適的線程才被調(diào)度,其他未完成的線程則處于等待狀態(tài)。舉例說,我們有4個線程A,B,C,D來完成一項(xiàng)工作,其中的順序限制是 A>B;C>D,“>”表示左邊的線程完成必須先于右邊的線程執(zhí)行,而“;”表示兩個線程可以同時進(jìn)行。同時假設(shè)B的一個操作需要調(diào)用 C來完成,顯而易見,這時候這個操作就是一個異步調(diào)用。我們可以在每個“>”的位置設(shè)定一個同步點(diǎn),然后通過一個信號燈來完成同步。線程B,C等待 第一個信號燈,而D會等待第二個信號燈。這個例子的動力和調(diào)度都是通過操作系統(tǒng)的基本機(jī)制(線程調(diào)度和同步機(jī)制)來完成。

            把這個過程抽象一下,可以描述為:若干個主動對象(包括代碼)協(xié)調(diào)來完成一項(xiàng)工作,通過一個調(diào)度器來調(diào)度,實(shí)際上,這個調(diào)度器可能只是一些調(diào)度規(guī)則。顯 然,進(jìn)程或者線程只要被調(diào)度就能獲得CPU,所以我們主要考慮代碼(比如一個函數(shù))怎么樣才能獲得執(zhí)行。用工作者線程來調(diào)用這個函數(shù)顯然是直觀和通用的一 個方案。事實(shí)上,在用戶空間(user space)或者用戶態(tài)(user mode),這個方法是很常用的。而在內(nèi)核態(tài)(kernel mode),則可以通過中斷來獲得CPU,這個通過注冊IDT入 口和觸發(fā)軟中斷就可以完成。硬件設(shè)備上的IC是另一個動力之源。而主動對象的調(diào)度,最基本的也是前面說的各種同步機(jī)制。另一個常用的機(jī)制就是回調(diào)函數(shù),需 要注意的是,回調(diào)函數(shù)一般會發(fā)生在跟調(diào)用者不一樣的上下文,比如說同一個進(jìn)程的不同線程,這個差別會帶來一些限制。如果需要回調(diào)發(fā)生在調(diào)用者的進(jìn)程(線 程)上下文,則需要一些類似Unix下的signal或者Windows下的APC機(jī)制,這一點(diǎn)我們在后面會有所闡述。那么在回調(diào)函數(shù)里面一般作些什么事情呢?最常用的, 跟同步機(jī)制結(jié)合在一起,當(dāng)然就是釋放一個互斥鎖,信號燈或者Windows Event(Unix的條件變量)等等,從而使得等待同步的其他對象可以得到調(diào)度而重新執(zhí)行,實(shí)際上,也可以看作是通知調(diào)度器(操作系統(tǒng))某些主動對象 (等待同步的)可以重新被調(diào)度了,從而調(diào)度器重新調(diào)度。但是對于另外一些調(diào)度器,在這個過程中可能不需要同步對象的參與。在一些極端一些的例子里,調(diào)度甚 至不要求嚴(yán)格有序的。

            在實(shí)際應(yīng)用中,根據(jù)環(huán)境的限制,異步調(diào)用的動力和調(diào)度的實(shí)現(xiàn)方式可以有很大差別。我們會在后面的例子里加以說明。 操作系統(tǒng)中的異步:Windows的異步I/O。

            Windows NT/2000是一個搶占式的分時操作系統(tǒng)。Windows的調(diào)度單位是線程,它的 I/O架構(gòu)是完全異步的,也就是說同步的I/O實(shí)際上都基于異步I/O來完成。一個用戶態(tài)的線程請求一個I/O的時候會導(dǎo)致一個運(yùn)行狀態(tài)從user mode到kernel mode的轉(zhuǎn)變(操作系統(tǒng)把內(nèi)核映射到每個進(jìn)程的2G-4G的地址上,對于每個進(jìn)程都是一樣的)。這個過程是通過中斷調(diào)用內(nèi)核輸出的一些System Service來完成,比如說ReadFile實(shí)際上會執(zhí)行NtReadFile(ZwReadFile),需要注意的是,運(yùn)行上下文仍然是當(dāng)前線程。 NtReadFile的實(shí)現(xiàn)則基于Windows內(nèi)核的異步I/O框架,在I/O Manager的協(xié)助下完成。需要指出的是,I/O Manager只是由若干API構(gòu)成的一個抽象概念,并沒有一個真正的I/O Manager線程在運(yùn)行。

            Windows的I/O驅(qū)動程序是層次堆積的。每個驅(qū)動程序會提供一致的接口以供初始化、清理和功能調(diào)用。驅(qū)動程序的調(diào)用基于I/O請求包(I/O Request Packet, IRP),而不是像普通的函數(shù)調(diào)用那樣使用棧來傳遞參數(shù)。操作系統(tǒng)和PnP管理器根據(jù)注冊表在 適當(dāng)?shù)臅r機(jī)初始化和清理相應(yīng)的驅(qū)動程序。在一般的功能調(diào)用的時候,IRP里面會指定功能調(diào)用號碼以及相應(yīng)的上下文或者參數(shù)(I/O stack location)。一個驅(qū)動程序可能調(diào)用別的驅(qū)動程序,這個過程可能是同步的(線程上下文不改變),也可能是異步的。NtReadFile的實(shí)現(xiàn),大致 是向最上層的驅(qū)動程序發(fā)出一個或多個IRP,然后等待相應(yīng)事件的完成(同步的情況),或者直接返回(帶Overlapped的情況),這些都在發(fā)起請求的 線程執(zhí)行。

            當(dāng)驅(qū)動程序處理IRP的時候,它可能立刻完成,也可能在中斷里才能完成,比如說,往硬件設(shè)備發(fā)出一個請求(通常可以是寫 I/O port),當(dāng)設(shè)備完成操作的時候會觸發(fā)一個中斷,然后在中斷處理函數(shù)里得到操作結(jié)果。Windows有兩類中斷,硬件設(shè)備的中斷和軟中斷,分成若干個不 同的優(yōu)先級(IRQL)。軟中斷主要有兩種:DPC(Delayed Procedure Call)和APC(Asynchronous Procedure Call),都處于較低的優(yōu)先級。驅(qū)動程序可以為硬件中斷注冊ISR(Interrupt Service Routine),一般就是修改IDT某個條目的入口。同樣,操作系統(tǒng)也會為DPC和APC注冊適當(dāng)?shù)闹袛嗵幚砝蹋ㄒ彩窃贗DT中)。

            值得指出的是,DPC是跟處理器相關(guān)的,每個處理器會有一個DPC隊(duì)列,而APC是跟線程相關(guān)的,每個線程會有它的APC隊(duì)列(實(shí)際上包括一個 Kernel APC隊(duì)列和User APC隊(duì)列,它們的調(diào)度策略有所區(qū)別),可以想象,APC并不算嚴(yán)格意義上的中斷,因?yàn)橹袛嗫赡馨l(fā)生在任何一個線程的上下文中,它被稱為中斷,主要是因?yàn)?IRQL的提升(從PASSIVE到APC),APC的調(diào)度一般在線程切換等等情形下進(jìn)行。當(dāng)中斷發(fā)生的時候,操作系統(tǒng)會調(diào)用中斷處理例程,對于硬件設(shè)備 的ISR,一般處理是關(guān)設(shè)備中斷,發(fā)出一個DPC請求,然后返回。不在設(shè)備的中斷處理中使用太多的CPU時間,主要考慮是否則可能丟失別 的中斷。由于硬件設(shè)備中斷的IRQL比DPC中斷的高,所以在ISR里面DPC會阻塞,直到ISR返回IRQL回到較低的水平,才會觸發(fā)DPC中斷,在 DPC中斷里執(zhí)行從硬件設(shè)備讀取數(shù)據(jù)以及重新請求、開中斷等操作。ISR或者DPC可能在任何被中斷的線程上下文(arbitrary thread context)執(zhí)行,事實(shí)上線程的上下文是不可見的,可以認(rèn)為是系統(tǒng)借用一下時間片而已。

            總的來說,Windows的異步I/O架 構(gòu)中,主要有兩種動力,一是發(fā)起請求的線程,一部分內(nèi)核代碼會在這個線程上下文執(zhí)行,二是ISR和DPC,這部分內(nèi)核代碼會在中斷里完成,可能使用任何一 個線程的上下文。而調(diào)度常見使用回調(diào)和事件(KEVENT),比如說在往下一層的驅(qū)動程序發(fā)出請求的時候,可以指定一個完成例程Completion Routine,當(dāng)下層的驅(qū)動完成這個請求的時候會調(diào)用這個例程,而往往在這個例程里,就是簡單的觸發(fā)一下一個事件。 另外可以順便提一下Linux。Linux 2.6也有類似的中斷機(jī)制,它有更多的軟中斷優(yōu)先級,即不同優(yōu)先級的softirq,而類似于DPC,Linux也提供了專門的軟中斷,對應(yīng)DPC的就是 tasklet。Linux沒有一個像windows這么一致的層次驅(qū)動程序架構(gòu),所以它的異步I/O稍微粗糙一些,主要是通過以前的一些阻塞點(diǎn),現(xiàn)在直 接返回-EIOCBRETRY,而讓調(diào)用者在合適的時機(jī)繼續(xù)重試。在這個方法中,可以認(rèn)為整個操作由一個函數(shù)完成,每次操作有進(jìn)展時,都把這個函數(shù)從頭執(zhí) 行一遍,當(dāng)然已經(jīng)完成的部分就不會再有實(shí)際的I/O。這樣的最大好處是原有的文件系統(tǒng)和驅(qū)動程序不用完全重寫。而對于同步調(diào)用,只要阻塞就可以了,這樣對 系統(tǒng)的修改較小。這時候,要提供POSIX aio的語義,就可能需要提供一些用戶線程來完成重試的過程了(回想Windows可以通過中斷和DPC完成的)。而對于Solaris,也是類似的處理,如果設(shè)備支持異步I/O,那就通過中斷可以完成,否則就使用內(nèi)部的LWP來模擬。
            應(yīng)用程序:一個異步的HTTP服務(wù)器的設(shè)計(jì)

            假設(shè)我們要設(shè)計(jì)一個HTTP服務(wù)器,它的設(shè)計(jì)目標(biāo)包括:高并發(fā)性、精簡 (部分支持HTTP/1.1)、支持plug-in結(jié)構(gòu)。在不少場合可能都有這個需求。總體上來說,HTTP服務(wù)器可以類比成一個基于多線程的操作系 統(tǒng):OS調(diào)度每個工作線程在適當(dāng)?shù)臅r候獲得執(zhí)行,而工作線程提供服務(wù)(也就是處理HTTP請求)。在這個基礎(chǔ)上,主要的考慮就是調(diào)度粒度的大小,粒度太大 的時候并發(fā)性會降低,而粒度太小又可能因?yàn)槿蝿?wù)切換(考慮OS的Context Switching)而導(dǎo)致效率降低,所以這又是一個折衷的結(jié)果。類似于Apache(以及其他的HTTP服務(wù)器),我們可以把一個HTTP處理過程分為 若干個狀態(tài),基于這些狀態(tài)可以構(gòu)造出一個HTTP處理的狀態(tài)機(jī)。這種情況下,我們就可以把每個狀態(tài)的處理作為調(diào)度的粒度。一個調(diào)度過程就是:一個工作線程 從全局的任務(wù)隊(duì)列里取出一個HTTP_Context結(jié)構(gòu);根據(jù)當(dāng)前的狀態(tài)完成相應(yīng)處理;然后根據(jù)狀態(tài)機(jī)設(shè)置下一個狀態(tài);再放回到全局的任務(wù)隊(duì)列里。這樣 子,若干個HTTP狀態(tài)就可以通過這個調(diào)度策略構(gòu)成一個完整HTTP處理過程。顯而易見,一個狀態(tài)對于下一個狀態(tài)處理的調(diào)用都可以認(rèn)為是異步的。一個 HTTP狀態(tài)機(jī)的設(shè)計(jì)如下圖所示。

                
            圖1. HTTP狀態(tài)機(jī)

            工作線程的函數(shù)其實(shí)就是兩個操作:從狀態(tài)隊(duì)列里取出一個HTTP_Context,調(diào)用HTTP_Context的service()函數(shù),周而復(fù)此。 在這個架構(gòu)上,就很容易引入異步I/O和Plug-in的機(jī)制了。事實(shí)上我們也可以使用基于事件(例如select/poll)的I/O策略來模擬異步I /O,實(shí)現(xiàn)中使用一個用戶線程就可以了。

            對于異步I/O和Plug-in的調(diào)用,我們也是采用類似于Linux 2.6里面aio的重試方案,而異步完成的時候采用回調(diào)函數(shù)。在某個狀態(tài)上,如果系統(tǒng)需要I/O操作(recv或者send),則會請求一個異步I /O(操作系統(tǒng)提供的異步I/O或者由用戶線程模擬的異步I/O),這時候相應(yīng)的HTTP_Context不會重新回到狀態(tài)隊(duì)列里,而在I/O完成的回調(diào) 函數(shù)里面才會重新放回到狀態(tài)隊(duì)列,得到重新調(diào)度的機(jī)會。HTTP_Context得到重新調(diào)度的時候會檢查I/O狀態(tài)(這個可以通過一些標(biāo)志位來完成), 如果已經(jīng)完成,則處理然后設(shè)置下一狀態(tài),重新調(diào)度,否則可以重新請求一個新的I/O請求。Plug-in也可以使用類似的方案,比如說一個Plug-in 要跟外部的一個服務(wù)器通信,這時候就可以在通信完成的時候才把HTTP_Context重新放回到狀態(tài)隊(duì)列。顯然,Plug-in跟HTTP狀態(tài)是多對多 的關(guān)系,一個Plug-in可以在若干個關(guān)心的狀態(tài)注冊自身,同時還可以設(shè)置一些short-path來提高處理的效率。

            結(jié)論

            總的來說,異步調(diào)用的設(shè)計(jì)和應(yīng)用歸根結(jié)底就是對多個主動對象的管理問題:如何提供執(zhí)行的動力以及如何保證執(zhí)行的順序邏輯。主要考慮的問題是主動對象的粒 度以及執(zhí)行方式,同步或者回調(diào)來完成順序的調(diào)度,或者使用近似的調(diào)度而加一些魯棒的錯誤處理機(jī)制來保證語義的正確。后者可以考慮在使用基于事件的 socket的時候,readable事件的通知可以是冗余的,或者說可以比實(shí)際中發(fā)生的readable事件更多,這個時候使用非阻塞的socket, 有些read()(或者recv())會直接返回EWOULDBLOCK,系統(tǒng)只要考慮處理這種情況(使用non blocking socket而不是blocking socket),當(dāng)例外的情況不多的時候是可以接受的。這時候可以說事件的報告就只是近似的。
            from:

            posted on 2010-09-06 17:33 chatler 閱讀(814) 評論(0)  編輯 收藏 引用 所屬分類: OSwindows
            <2025年8月>
            272829303112
            3456789
            10111213141516
            17181920212223
            24252627282930
            31123456

            常用鏈接

            留言簿(10)

            隨筆分類(307)

            隨筆檔案(297)

            algorithm

            Books_Free_Online

            C++

            database

            Linux

            Linux shell

            linux socket

            misce

            • cloudward
            • 感覺這個博客還是不錯,雖然做的東西和我不大相關(guān),覺得看看還是有好處的

            network

            OSS

            • Google Android
            • Android is a software stack for mobile devices that includes an operating system, middleware and key applications. This early look at the Android SDK provides the tools and APIs necessary to begin developing applications on the Android platform using the Java programming language.
            • os161 file list

            overall

            搜索

            •  

            最新評論

            閱讀排行榜

            評論排行榜

            a级毛片无码兔费真人久久| 免费一级做a爰片久久毛片潮| 欧美大香线蕉线伊人久久| 综合人妻久久一区二区精品| 亚洲va国产va天堂va久久| 久久久久国产一级毛片高清版| 无码任你躁久久久久久老妇| 乱亲女H秽乱长久久久| 精品国产一区二区三区久久蜜臀| 波多野结衣久久一区二区 | 久久精品中文无码资源站| 狠狠色噜噜狠狠狠狠狠色综合久久| 久久久无码精品午夜| 久久99精品综合国产首页| 99久久99久久精品国产片果冻| 嫩草影院久久国产精品| 人妻丰满AV无码久久不卡| 色综合久久中文字幕综合网| 久久伊人精品青青草原高清| 无码人妻少妇久久中文字幕蜜桃| 久久综合色之久久综合| 国产精品亚洲综合专区片高清久久久| 日韩人妻无码一区二区三区久久99| 日韩一区二区久久久久久| 激情伊人五月天久久综合| 国产精品99久久久精品无码| 久久九九免费高清视频| 国产精品熟女福利久久AV| 日本久久久精品中文字幕| 精品国产福利久久久| 久久婷婷激情综合色综合俺也去 | 国产精品xxxx国产喷水亚洲国产精品无码久久一区 | 久久精品国产只有精品2020| 一本色道久久99一综合| 麻豆久久久9性大片| 一级做a爰片久久毛片毛片| 久久国产精品一区| 欧美国产成人久久精品| 日本亚洲色大成网站WWW久久| 久久人人爽人人爽人人片AV东京热| 久久激情亚洲精品无码?V|