天天看點

Socket 網絡程式設計 同步異步、阻塞非阻塞同步異步、阻塞非阻塞5種IO模型

同步異步、阻塞非阻塞

        個人認為同步和異步是更抽象的概念,是相對的任務而言的。而阻塞和非阻塞就就字面的意思是對目前的程序或線程而言的。

概念之間的差別

同步與異步

        同步與異步在不同的場景下有不同的概念,在IO模型中的同步異步,主要差別在當任務A調用任務B的過程中,程序A是否繼續進行。 

        如果A等待B的結果,則為同步;

        如果A不等待B的結果,則為異步;

        同步狀态下任務A的執行時依賴于任務B的,任務A成功是依賴于成功B的。而異步模式下兩者是不相關的。

        異步的實作方式大概有三種:狀态、通知和回調。狀态就是任務A去查詢任務B的結果如何 ;通知就是等任務B執行完成之後通知任務A來實作;回調就是任務A定義個回調函數,當任務B結束後會自動調用回調函數;

阻塞與非阻塞

        阻塞和非阻塞的主要差別在,任務A等待B的結果的過程中,任務A是否會被挂起? 如果A等待的過程中不會挂起,則為阻塞; 如果A不等待的過程中不會挂起,則為非阻塞。

        同步阻塞的情況下,任務A會挂起,同步非阻塞的情況下任務A并不挂起。不挂起的情況下任務A保留有響應信号的能力。

        非阻塞的情況下并不會導緻線程切換(隻是不強制進行線程切換,如果該線程的時間片用完還是會切換的),可能效率更高,cpu使用率也更高,但是cpu可能會無意義空轉,這樣又會導緻性能降低,是以使用何種方式需要看目前的系統情況。

        上面兩點似乎被分的很清楚,但是實際上這兩個概念我認為指的是同一件事情,站的角度不同而已,過分的強調概念是無意義的。同步和異步更多的是兩個任務之間資料通信方式,而阻塞非阻塞,則是站在目前線程自身的角度考慮是否可以在保留程序不挂起而繼續進行任務來看的。

5種IO模型

        要了解IO模型先要了解一linux類的系統下計算機的IO基本概念,5種IO模型實際上指的都是網絡程式設計中的IO,資料從網絡讀取後先會被放入核心區,而後從核心區傳入使用者區。 

5種IO模型中前四種全部都是同步IO隻有異步IO,這裡的同步和異步差別在于同步IO會在資料到達核心區的等待過程中進行各種方式的檢查,如果查到了有資料到達了核心區則進行阻塞,是以整個IO的流程是分成2個階段的。而異步IO的模式是隻有資料完成了從核心區到使用者區的複制之後才有通知。 而同步IO中根據第一階段的不同政策又分成很多的不同模型。

阻塞式I/O

        應用程序在調用recvfrom,後阻塞直至資料到達使用者區之後才恢複。從應用程序的角度上說這是很合理而高效的,從性能的角度來說可以見上文中的讨論,首先他會引起線程切換,其次其cpu使用率低。 

Socket 網絡程式設計 同步異步、阻塞非阻塞同步異步、阻塞非阻塞5種IO模型

非阻塞式I/O

        應用程序在調用recvfrom的情況下并阻塞,而是傳回一個為準備好的傳回值,這時候應用程序可以繼續運作,處理一些其他事情。通過這種輪詢的方式查詢是否有資料到達核心區,如果核心區有資料則需要阻塞應用線程,等待資料讀取至使用者區之後進行處理。 

        非阻塞I/O看起來很傻,不停的循環,但是這樣有兩個好處,第一個是不會強制進行線程的切換,線程切換的代價是很大的,其次在兩次查詢直接可以用來做一些其他的事情,使用者線程保有一定的響應能力。 

Socket 網絡程式設計 同步異步、阻塞非阻塞同步異步、阻塞非阻塞5種IO模型

I/O複用模型

        非阻塞式I/O中說到了采用輪詢的方式檢視是否有資料到達核心區,單線程的來看這個問題其實很傻,但是網絡IO通常不是單個線程的。會有很多線程同時進行I/O讀寫,是以我們可以依次檢測多個I/O讀寫任務,如果有某個任務所指定的資料到達則傳回這樣效率就高得多了,這就是所謂的I/O複用。 

        其實這是網絡模型中最為常用的模式,平時所謂的select、poll和epoll都是I/O複用模型,隻是細節上略有差別,具體的差別在後面讨論。 

        讀取的兩個階段有2次調用2次傳回,在第一個階段是很多I/O讀取任務共用的,是以效率還是比較高的。 

Socket 網絡程式設計 同步異步、阻塞非阻塞同步異步、阻塞非阻塞5種IO模型

信号驅動I/O 

        信号驅動I/O我并不是很了解,目前的了解是linux類的程式下允許注冊一個信号處理函數,我們可以用這種方式來處理IO,首先注冊一個信号處理函數,然後應用程式繼續進行,當有資料到達核心區後,會有一個信号傳回給目前應用程序,目前引用程序進入信号處理過程,信号處理過程中會将資料拷貝至使用者區的過程依然是阻塞的。          其實這裡其實看出來,所謂的阻塞、非阻塞和同步、異步,其實是一件事情,隻是在不同的語境下有些細微的差別而已,信号驅動I/O的第一個階段實際上就是典型的異步通知過程。但是因為其第二個階段需要阻塞,是以整個信号驅動I/O被歸類為同步I/O。 

Socket 網絡程式設計 同步異步、阻塞非阻塞同步異步、阻塞非阻塞5種IO模型

異步I/O 

        異步I/O的模型其實就是兩個階段均為異步的方式,使用者程序異步調用函數後,檢測資料的信号,但是使用者線程自身并不挂起,而是繼續運作,當資料完成了2個階段的過程,讀取至使用者區之後才會通知應用程式。 

這種方式和阻塞式I/O的差別僅僅在于使用者線程是否在兩個階段均阻塞。 

Socket 網絡程式設計 同步異步、阻塞非阻塞同步異步、阻塞非阻塞5種IO模型

模型之間的讨論 

        其實對于典型的伺服器環境,基本上都是預設采用I/O複用模型的,這一點從模型自身的特性就可以看出。網絡I/O中即使采用非阻塞的方式其實本身也沒有太大的意義,因為沒有資料到來,那處理程式也沒有其他的事情要做。而且從程式設計的角度上來說,I/O複用這種同步的程式設計模式也更利于了解,不會使得程式顯得很混亂。 

關于效率問題,在上一章的讨論中以及讨論過了并不存在非阻塞效率更高的說法,隻是在不同場景下有各自優勢而且,但是在絕大多數場景下依然是IO複用更高效。

        下面附上幾中不同的I/O狀态比較圖:

Socket 網絡程式設計 同步異步、阻塞非阻塞同步異步、阻塞非阻塞5種IO模型

IO複用的幾中方式select、poll、epoll

select

        首先select隻有一個函數,建立、注冊等待都是一次完成的。 

特性 

        首先将fd_set從使用者空間拷貝到核心區,然後注冊回調函數 __pollwait。 

回調函數的主要工作就是把目前線程挂到裝置的等待隊列中,不同的裝置有不同的等待隊列,如果該裝置有個響應的響應(比如網絡中讀取的I/O)則會喚醒該裝置等待隊列上的程序,___pollwait方法會傳回一個描述讀寫操作是否就行的mask掩碼,根據這個掩碼給fd_set指派。 如果周遊fd_set都沒有一個可讀寫的mask掩碼,這調用會 schedule_timeout将select線程進入睡眠,如果裝置驅動自身資源可讀或者,逾時一定時間限都沒人喚醒,則會喚醒當代隊列上的線程重新周遊fd_set判斷有沒有就緒的fd。 

缺點 

        每次調用select,都需要把fd集合從使用者态拷貝到核心态,這個開銷在fd很多時會很大 ;同時每次調用select都需要在核心周遊傳遞進來的所有fd,這個開銷在fd很多時也很大 ,尤其是在連接配接數量很多,但是活躍連接配接并不多的情況下。 select支援的檔案描述符數量太小了,預設是1024(64位系統是2048)。

poll

        poll和select基本上類似,差別是采用連結清單的形式去組織資料,是以沒有數量限制。但是前兩個問題依然存在

epoll

        epoll的實作和上面兩者有很大的不同,epoll分為三個函數,分别表示建立,注冊和阻塞三個情況。

這樣在注冊時會将epoll的句柄從使用者區拷貝到核心區,是以隻有一次複制,而不是每次等待都要複制,不存在第一個問題。 

epoll為每個fd指定一個回調函數,當裝置喚醒的時候就會調用這個回調函數,而這個回調函數會把就緒的fd加入一個就緒連結清單,epoll_wait要做的工作其實就是定期檢視這個連結清單有沒有就緒的fd就好,是以不需要周遊fd,不存在第二個問題。

        而且由于不需要周遊也不存在低活躍連接配接數量下效率低下的問題。 epoll沒有fd上限,一般1G記憶體可以有10w個連接配接,與記憶體大小相關,而且可以修改。不存在第三個問題。

        此外還有另一種說法,epoll采用mmap記憶體映射技術将核心區與使用者區映射為同一片地方以減少系統複制代價,不清楚具體用在哪裡。

        epoll和select、poll之間最主要的差別還是前者基于回調機制進行響應,而select和poll基于系統調用,讓核心周遊所有fd進行查詢是否有裝置就緒。

        epoll本身還分為電平觸發和邊緣觸發 

        電平觸發(條件觸發)LT模式:就是當fd就緒後進行通知,如果此次通知後沒有操作響應,則下次依然通知,可用在阻塞模式也可以用在非阻塞模式。 

        邊緣觸發ET模式:就是當fd就緒後進行通知,但是如果此次沒有響應操作,則下次不會通知。但隻可以用在非阻塞模式下。 

通常來說邊緣觸發效率更高,因為可以減少重複epoll的次數。

繼續閱讀