Linux通訊總結——阻塞/非阻塞、同步/非同步、IO模型
阿新 • • 發佈:2021-08-09
1.阻塞/非阻塞,同步/非同步
- 一次典型的IO包括兩個階段:資料就緒和資料讀寫
- 資料就緒階段:阻塞和非阻塞
- 阻塞:資料還沒到達之前,呼叫IO方法的執行緒會掛起(並不佔用CPU資源)
- 非阻塞:不會改變執行緒的狀態,會通過返回值判斷緩衝區是否有資料傳來int size = recv(sockfd, buf, 1024, 0);
- size = -1,讀取出錯(但是要特殊判斷下列3種情況:EINTR,EAGAIN,EWOULDBLOCK)
- size = 0,讀取到資料末尾,或者對方連線關閉
- size > 0,讀取到多少資料
- 在處理IO的時候,阻塞和非阻塞都是同步IO,只有使用了特殊API的才是非同步IO。
- 資料讀寫階段:同步和非同步
- 同步:A向B請求呼叫網路IO介面時,資料的讀寫都是由請求方A自己完成
- 非同步:...,A向B傳入請求的事件以及事件發生時通知的方式,A就可以處理其他業務邏輯了,當B監聽到事件處理完成之後,會用事先約定好的通知方式,通知A處理結果。
- Linux非同步IO介面 aio_read(),aio_write()
- 同步的意思就是當呼叫一個I/O syscall後,直到有確定結果返回後,應用程式都是被阻塞的核心態,時間片被換出,完全卡住不能執行。非同步就是呼叫完syscall後,不存在任何阻塞核心態,應用程式繼續執行直到時間片用完。
- 全非同步控制非常困難並且要求很高,現在常用的是同步非阻塞,這裡面也是使用同步呼叫read/write,但是fd是非阻塞的,這時候呼叫這兩個syscall只會短暫的切換到核心態如果沒有資料會立刻返回給應用EAGAIN,這樣應用可以選擇等一會再來一次,或者等待EPOLL的事件之後再來一次。這種方式被廣泛用於現在主流的各種伺服器的網路socket操作,反向代理等等軟體上比如nginx。為什麼只有socket操作用會比較好呢,因為網路操作底層kernel都是在處理buffer,小規模的操作並不會導致太大的延遲,所以返回都比較快,不會造成很大延遲。
- 需要注意這時候呼叫read的時候也會產生上下文切換,這時候嚴格說也是阻塞的,但是阻塞時間非常短。
- 全非同步的aio_read不存在這個短暫時間,效率更高,但是也更難控制,因為需要自己處理流程和buffer的問題,非常難搞。這個一般用於即使較小的規模的I/O操作也可能造成很大延時的操作,典型的就是磁碟讀寫。例子就是nginx的讀檔案的操作。
- 阻塞IO
- 呼叫者呼叫了某個函式,等待這個函式返回,期間什麼都不做,不停檢查這個函式有沒有返回,必須等該函式返回才能進行下一步動作。
- 非阻塞IO
- 每隔一段時間去檢測IO時間是否就緒,沒有就緒就可以進行其他操作。
- 非阻塞IO執行系統呼叫總是立即返回,不管事件是否已經發生,若沒發生則返回-1,此時根據errno區分兩種情況,對於accept、recv和send,事件未發生時,errno被設定為EAGAIN。
- IO複用
- select、poll、epoll函式實現,這些函式也會使程序阻塞,但是和阻塞IO不同的是,這些函式可以同時阻塞多個IO操作。
- 可以同時對多個讀操作、寫操作的IO函式進行檢測,直到有資料可讀或可寫時,才真正呼叫IO操作函式。
- 訊號驅動IO
- 註冊新號處理函式,程序繼續執行並不阻塞,當IO事件就緒時,程序受到SIGIO新號,然後處理IO事件。
- 核心在第一個階段是非同步,在第二個階段是同步。
- 與非阻塞IO的區別在於它提供了訊息通知機制,不需要使用者程序不斷的輪詢檢查,減少了系統API的呼叫次數,提高了效率。
- 非同步IO
- LInux中,呼叫aio_read函式告訴核心描述字緩衝區指標和緩衝區大小 、檔案偏移及通知的方式,然後立即返回,當核心將資料拷貝到緩衝區後,再通知應用程式。