I/O的概念
操做系統的分爲socket的I/O還有用戶界面的輸入輸出,通常一個輸入操做分爲兩個不一樣的階段,1)等待數據準備好;2)從內核向進程複製數據
從理論上來說,阻塞I/O、非阻塞I/O、複用I/O、信號驅動I/O都是同步IO模型,異步I/O就是異步IO,同步I/O嚮應用程序通知的是I/O就緒事件,異步I/O嚮應用程序通知的是I/O完成事件。
同步和異步:都是針對應用程序和操做系統之間交互而言的。同步的話,若是上層應用不主動詢問操做系統的話,操做系統是不會主動通知應用程序的。異步的話,操做系統會主動通知應用程序。
阻塞:若是條件不知足的話,操做系統將應用程序掛起或者休眠,此時應用程序處於阻塞狀態,只有當數據準備好以後或者數據已經從內核空間拷貝到用戶空間,操做系統通知線程,此時喚醒線程。
非阻塞:若是條件不知足的話,該方法不會阻塞當前線程,而是當即返回一個標誌信息(數據尚未準備好),或者timeou以後返回,此時應用線程還能夠進行其餘的操做。
在linux中並不存在真正的異步I/O,而是經過多路複用I/O的方式去模擬異步IO
注意:同步並不等同於阻塞,同步調用的時候,即使調用不返回,可是應用線程還處於運行狀態,能夠進行其餘的操做,而阻塞調用時,應用線程處於阻塞狀態。
操做系統5種通訊模型
解釋:應用程序會調用一個IO函數,致使應用程序阻塞,程序會一直停留在此處,若是數據準備好了,內核會將數據拷貝至應用程序
僞代碼:
{ read(socket, buffer); process(buffer); }
特色:當處理socket數量較少的時候,使用阻塞式I/O比較合適,當socket數量較大,爲每個socket都分配一個讀線程、處理線程以及同步的事件,那麼對操做系統的開銷會變得很大,而且不支持大數據量的socket連接
解釋:非阻塞IO就是經過反覆調用IO函數,若是沒有數據,那麼會當即返回一個錯誤的接口,不斷去調用,不斷去輪詢,查看是否能夠獲取數據,一般來講,這種比較耗費大量的cpu時間,在數據拷貝階段,仍是阻塞的,原理就是應用程序在socket非阻塞的時候,告訴操做系統,不要在沒有數據的時候把我掛起或者休眠,而是返回一個錯誤的信號
僞代碼:
{ while(read(socket, buffer) != SUCCESS) ; process(buffer); }
特色:不容易使用,須要編寫更多的代碼,若是非阻塞IO在控制創建多個鏈接,在數據的收發量不均,時間不定的狀況下,更具優點
解釋:主要就是select、poll和epoll,有了I/O複用,咱們可使用select或者poll或者epoll,兩次調用,兩次返回,相對於阻塞I/O沒有什麼優越性,關鍵是能實現同時對多個IO端口進行監聽,會使進程阻塞,能夠同時阻塞多個I/O操做。
多路複用I/O模型是目前使用比較多的模型,所謂多路複用,能夠理解爲在一個應用線程裏面能夠處理多個socket鏈接,或者同一個端口接受來自多個客戶端的請求(鏈接、讀、寫),多路複用I/O與傳統的非阻塞IO的區別在於,在傳統的非阻塞I/O中,是由用戶去輪詢socket狀態,而在多路複用I/O中,由內核去輪詢向select註冊的socket的狀態,因此多路複用IO須要操做系統的支持。
最大的優點是,在同一個線程中,能夠同時處理多個socket的IO請求,在同步阻塞模型中,必須經過多線程的方式才能夠達到這個目的。while循環以前,將socket添加到select監視中,而後while循環一直調用select獲取被激活的socket,一旦socket可讀,便調用read函數將socket中的數據讀取出來。可是每一個IO請求的過程仍是阻塞的(在select函數上阻塞),平均時間甚至比同步阻塞IO模型還要長。
僞代碼:
{ select(socket); while(true){ sockets = select(); for(socket in sockets){ if(can_read(socket)){ read(socket, buffer); process(buffer); } } } }
特色:使用場景:
1)當客戶處理多個描述符(一般是交互式輸入和socket)時,必須使用I/O複用
2)一個客戶同時處理多個socket是可能的
3)若是一個TCP服務器既要處理監聽socket,又要處理已經鏈接的socket,通常就要使用I/O複用
4)若是一個服務器既要處理TCP,又要處理UDP,通常就要使用I/O複用
解釋:首先咱們容許socket進行信號驅動I/O,而且安裝一個信號處理函數,進行繼續運行不阻塞,當數據準備好以後,進程收到一個SIGIO信號,能夠在信號處理函數中調用I/O操做函數處理數據
特色:這種模型的優點在於等待數據包到達期間,進程不會被阻塞,只須要等待信號處理函數的通知,既能夠是數據準備好被處理,也能夠是數據包已經準備好被讀取
解釋:先異步調用,可是不會當即返回數據,而是等待被調用部件完成後,經過狀態、通知和回調來通知調用者的輸入輸出操做,這個與信號驅動I/O的區別在於:1)信號驅動I/O是由內核通知咱們如何啓動一個I/O操做;2)而異步I/O模型是由內核通知咱們I/O操做什麼時候完成,異步I/O模型纔是最理想的I/O模型,在異步I/O模型中,當用戶線程發起read操做以後,馬上就能夠開始去作其餘的事情。而另一方面,從內核的角度,當它收到asynchronous read以後,它會馬上返回,說明read請求已經成功發起,所以不會對用戶線程產生任何block。而後內核會等待數據準備完成,而後將數據拷貝到用戶線程,當這一切都完成以後,內核就會給用戶線程發一個信號,告訴它read操做完成了。也就說用戶線程徹底不須要實際的整個I/O操做是如何進行的,只須要先發起一個請求,當接受內核返回成功的信號時表示I/O操做已經完成,能夠直接去使用數據了,異步I/O模型中,由操做系統內核去等待數據就緒,copy數據到用戶空間,用戶線程始終沒有阻塞。
特色:咱們調用aio_read函數,給內核傳遞描述符、緩衝區指針,緩衝區大小和文件偏移,並告訴內核當整個操做完成的時候如何通知咱們。該系統調用後就當即返回,並且在等待I/O完成期間,咱們的進程不被阻塞
參考資料: