【Linux网络编程学习】阻塞、非阻塞、同步、异步以及五种I/O模型
文章目錄
- 1. 基本概念
- 1.1 阻塞與非阻塞
- 1.2 同步與異步
- 1.3 為什么沒有“異步阻塞”
- 2. 五種IO模型
- 2.1 阻塞 blocking
- 2.2 非阻塞 non-blocking
- 2.3. IO復用(IO multiplexing)
- 2.4 信號驅動(signal-driven)
- 2.5 異步(asynchronous)
1. 基本概念
首先回顧并理解一下網絡IO中阻塞、非阻塞;同步、異步的概念。
首先我們要知道:一個典型的網絡IO接口調用,分為兩個階段,分別是“數據就緒”和“數據讀寫”。
阻塞與非阻塞是“數據就緒”階段的概念,同步與異步是“數據讀寫”階段的概念。
1.1 阻塞與非阻塞
所謂阻塞,就是IO在沒有準備好數據的時候,調用IO方法的線程會被阻塞,直至收到數據時解除阻塞;而非阻塞IO在沒有準備好數據的時候IO方法一樣會返回,線程不會被阻塞。以recv方法為例,我們一般這樣調用:
int size = recv(sockfd, buf, 1024, 0);我們創建的sockfd默認是阻塞的,那么當執行這個函數時,如果sockfd對應的socket上數據沒有就緒(即TCP接收緩存區中無數據),則該線程會阻塞,再往下的代碼不會執行;但如果我們使用fcntl設置sockfd對應的socket為非阻塞,該socket工作在非阻塞狀態下,那么執行該函數時,無論是否有數據準備好,該函數都會返回,然后我們可以通過返回的size值和errno來判斷是否讀到數據:
size == -1 && errno == EAGAIN: 表示沒有讀到數據,是因為非阻塞才返回;
size == 0:表示對端斷開連接;
size > 0 : 表示讀到數據,size為數據的長度;
1.2 同步與異步
再來說網絡IO的同步和異步。剛才我們討論阻塞和非阻塞時,關注的是數據準備階段且數據沒有準備好的情況,現在我們討論同步和異步,關注的是數據已經準備好(TCP接收緩存區中有數據),進入數據讀寫階段時的情況。
同步:同步表示A向B請求調用一個網絡IO接口時,數據的讀寫都是由請求方A自己來完成的(不管是阻塞還是非阻塞)
比如調用recv方法(recv是同步IO方法)的時候其檢測到數據已經準備好,然后把數據從內核的TCP接受緩存區拷貝到我們傳入的buf(recv方法的第二個參數)中,這一拷貝過程耗費的是調用recv的線程的時間,直至拷貝結束,recv才返回;
異步:異步表示A向B請求調用一個網絡IO接口時,向B傳入請求的事件以及事件發生時通知的方式,A就可以處理其它邏輯了,當B監聽到事件處理完成后,會用事先約定好的通知方式,通知A處理結果。
比如某個應用程序調用了一個異步IO的API來處理數據讀入(如aio_read),那么應用程序只需把需要監聽的sockfd給內核傳入,內核來負責數據的準備和讀入。在此期間,應用程序可以做自己的事情。數據讀入完畢以后,再通過信號等方式來通知應用程序。
1.3 為什么沒有“異步阻塞”
把阻塞、非阻塞,同步、異步兩兩組合有:
- 同步阻塞
- 同步非阻塞
- 異步阻塞
- 異步非阻塞
其中,所有平時使用的Linux網絡IO的API都是同步的(如write、read、recv等等),我們可以通過設置其關注的socket為阻塞或者非阻塞,這樣就有了同步阻塞和同步非阻塞的API。
但是異步的API一定是非阻塞的,因為異步IO是要讓內核幫我們完成數據的讀寫,讓調用它的應用程序可以去做自己的事情,如果異步的同時又阻塞,那么應用程序無法做自己的事情,異步就失去了意義。
2. 五種IO模型
有了以上的知識鋪墊,再學習Linux的五種IO模型就通暢很多。
2.1 阻塞 blocking
調用者調用了某個函數,等待這個函數返回,期間什么也不做,不停的去檢查這個函數有沒有返回,必須等這個函數返回才能進行下一步動作。下圖以read為例。
2.2 非阻塞 non-blocking
非阻塞等待,每隔一段時間就去檢測IO事件是否就緒。沒有就緒就可以做其他事。非阻塞I/O執行系統調用總是立即返回,不管事件是否已經發生,若事件沒有發生,則返回-1,此時可以根據 errno 區分這兩種情況,對于accept,recv 和 send,事件未發生時,errno 通常被設置成 EAGAIN。
2.3. IO復用(IO multiplexing)
Linux 用 select/poll/epoll 函數實現 IO 復用模型,這些函數也會使進程阻塞,但是和阻塞IO所不同的是這些函數可以同時阻塞多個IO操作。而且可以同時對多個讀操作、寫操作的IO函數進行檢測。直到有數據可讀或可寫時,才真正調用IO操作函數。
2.4 信號驅動(signal-driven)
Linux 用套接口進行信號驅動 IO,安裝一個信號處理函數,進程繼續運行并不阻塞,當IO事件就緒,進程收到SIGIO 信號,然后處理 IO 事件。
注意區分信號驅動和異步IO的區別:信號驅動中,內核在數據準備階段是異步,但在數據讀寫階段是同步,仍然要花費應用程序自己的時間;與非阻塞IO的區別在于它提供了消息通知機制,不需要用戶進程不斷的輪詢檢查,減少了系統API的調用次數,提高了效率。
2.5 異步(asynchronous)
Linux中,可以調用 aio_read 函數告訴內核描述字緩沖區指針和緩沖區的大小、文件偏移及通知的方式,然后立即返回,當內核將數據拷貝到緩沖區后,再通知應用程序。從圖中可以看出,異步IO是內核負責把數據從內核空間拷貝到用戶空間,不耗費應用程序自己的時間,而前面四種IO方式在數據讀寫階段都需要應用程序自己負責。
總結
以上是生活随笔為你收集整理的【Linux网络编程学习】阻塞、非阻塞、同步、异步以及五种I/O模型的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 我的世界怎么不让水结冰
- 下一篇: 在linux下利用ls命令进行模糊查找