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

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

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

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

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

            動力和調度

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

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

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

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

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

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

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

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

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

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

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

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

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

            結論

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

            posted on 2010-09-06 17:33 chatler 閱讀(806) 評論(0)  編輯 收藏 引用 所屬分類: OSwindows
            <2010年9月>
            2930311234
            567891011
            12131415161718
            19202122232425
            262728293012
            3456789

            常用鏈接

            留言簿(10)

            隨筆分類(307)

            隨筆檔案(297)

            algorithm

            Books_Free_Online

            C++

            database

            Linux

            Linux shell

            linux socket

            misce

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

            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

            搜索

            •  

            最新評論

            閱讀排行榜

            評論排行榜

            亚洲国产精品一区二区久久| 韩国三级大全久久网站| 99久久精品国产一区二区蜜芽| 国产∨亚洲V天堂无码久久久| 99久久精品免费国产大片| 久久久久亚洲AV综合波多野结衣 | 久久婷婷色香五月综合激情| 合区精品久久久中文字幕一区| 亚洲精品乱码久久久久66| 久久亚洲日韩精品一区二区三区| 青青青青久久精品国产h| 久久精品国产精品亚洲下载| 精品久久久中文字幕人妻| 久久国产色AV免费看| 性做久久久久久久久| 久久精品成人国产午夜| 欧美亚洲国产精品久久高清| 国产精品久久国产精麻豆99网站| 久久夜色精品国产| 狠狠狠色丁香婷婷综合久久五月| 亚洲精品tv久久久久| 91久久精一区二区三区大全| 久久综合视频网| 久久精品亚洲欧美日韩久久| 国产精品久久久亚洲| 亚洲中文字幕无码久久精品1| 久久久噜噜噜久久| 91精品国产高清久久久久久91 | 亚洲国产成人久久综合野外| 大伊人青草狠狠久久| 亚洲色大成网站www久久九| 久久久久亚洲精品无码网址| 亚洲国产成人久久精品影视| 99re久久精品国产首页2020| 亚洲香蕉网久久综合影视| 综合久久给合久久狠狠狠97色| 爱做久久久久久| 国产999精品久久久久久| 久久久久夜夜夜精品国产| 777米奇久久最新地址| 国产成人久久激情91|