• <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
            簡(jiǎn)介

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

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

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

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

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

            動(dòng)力和調(diào)度

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

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

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

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

            Windows NT/2000是一個(gè)搶占式的分時(shí)操作系統(tǒng)。Windows的調(diào)度單位是線程,它的 I/O架構(gòu)是完全異步的,也就是說(shuō)同步的I/O實(shí)際上都基于異步I/O來(lái)完成。一個(gè)用戶態(tài)的線程請(qǐng)求一個(gè)I/O的時(shí)候會(huì)導(dǎo)致一個(gè)運(yùn)行狀態(tài)從user mode到kernel mode的轉(zhuǎn)變(操作系統(tǒng)把內(nèi)核映射到每個(gè)進(jìn)程的2G-4G的地址上,對(duì)于每個(gè)進(jìn)程都是一樣的)。這個(gè)過(guò)程是通過(guò)中斷調(diào)用內(nèi)核輸出的一些System Service來(lái)完成,比如說(shuō)ReadFile實(shí)際上會(huì)執(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)成的一個(gè)抽象概念,并沒(méi)有一個(gè)真正的I/O Manager線程在運(yùn)行。

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

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

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

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

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

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

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

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

            結(jié)論

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

            posted on 2010-09-06 17:33 chatler 閱讀(798) 評(píng)論(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
            • 感覺(jué)這個(gè)博客還是不錯(cuò),雖然做的東西和我不大相關(guān),覺(jué)得看看還是有好處的

            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

            搜索

            •  

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            久久久久人妻精品一区| 久久婷婷国产综合精品| 国产精品久久久99| 久久本道久久综合伊人| 亚洲国产一成久久精品国产成人综合 | 国产精品久久久久一区二区三区| 国内精品久久久久久不卡影院| 久久综合一区二区无码| 少妇久久久久久久久久| 精品亚洲综合久久中文字幕| 午夜精品久久影院蜜桃| 丰满少妇高潮惨叫久久久| 色婷婷久久久SWAG精品| 久久91综合国产91久久精品| 一级女性全黄久久生活片免费 | 漂亮人妻被黑人久久精品| 久久精品国产亚洲7777| 久久国产精品77777| 天天综合久久一二三区| 亚洲国产成人久久精品影视 | 久久综合狠狠综合久久激情 | 久久久久久国产a免费观看不卡| 囯产极品美女高潮无套久久久| 亚洲精品高清久久| 2021久久国自产拍精品| 无码人妻久久久一区二区三区| 亚洲国产成人久久笫一页| 国产福利电影一区二区三区久久老子无码午夜伦不 | 精品久久久噜噜噜久久久 | 久久综合久久综合九色| 综合人妻久久一区二区精品| 久久激情五月丁香伊人| 亚洲国产精品久久久久久| 久久人人爽人人爽人人片av高请| 一级女性全黄久久生活片免费 | 亚洲综合日韩久久成人AV| 亚洲午夜久久久| 国产精品一区二区久久精品涩爱| 国产亚洲美女精品久久久| 国产精品欧美亚洲韩国日本久久| 久久99九九国产免费看小说|