阻塞 IO 模型
在Linux ,默認情況下所有的 socket 都是阻塞的,一個典型的讀操作流程如圖所示。
阻塞和非阻塞的概念描述的是用戶線程調用內核 IO 操作的方式:阻塞是指 IO 操作需要徹底完成后才返回到用戶空間;而非阻塞是指 IO操作被調用后立即返回給用戶一個狀態值,不需要等到 IO 操作徹底完成。
當應用進程調用了 recvfrom 這個系統調用后,系統內核就開始了 IO 的第一個階段 :準備數據。
對于網絡 IO 來說,很多時候數據在一開始還沒到達時,系統內核就要等待足夠的數據到來。而在用戶進程這邊,整個進程會被阻塞。
當系統內核一直等到數據準備好了,它就會將數據從系統內核中拷貝到用戶內存中,然后系統內核返回結果,用戶進程才解除阻塞的狀態,重新運行起來。所以,阻塞IO 模型的特點就是 IO 執行的兩個階段都被阻塞了。
大部分的 socke接口都是阻塞型的。所謂阻塞型接口是指系統調用時卻不返回調用結果,并讓當前線程一直處于阻塞狀態,只有當該系統調用獲得結果或者超時出錯時才返回結果。
實際上,除非特別指定,幾乎所有的 IO 接口都阻塞型的。這給網絡編程帶來了一個很大的問題,如在調用 send的同時,線程處于阻塞狀態,則在此期間,線程將無法執行任何運算或響應任何網絡請求。
非阻塞 IO 模型
在Linux 下,可以通過設置 socket IO 變為非阻塞狀態。當一個非阻塞的 socket執行 read 操作時,流程如圖:
當用戶進程發出 read 操作時,如果內核中的數據還沒有準備好,那么它并不會 block 用戶進程,而是立刻返回一個錯誤。
從用戶進程角度講,它發起 read 操作后,并不需要等待,而是馬上就得到了一個結果當用戶進程判斷結果是一個錯誤時,它就知道數據還沒有準備好,于是它可以再次發送 read 操作。
一旦內核中的數據準備好了,并且又再次收到了用戶進程的系統調用,那么它馬上就將數據復制到了用戶內存中,然后返回正確的返回值。
所以,在非阻塞式 IO 中,用戶進程其實需要不斷地主動詢問 kernel數據是否準備好。非阻塞的接口相比于阻塞型接口的顯著差異在于被調用之后立即返回,使用如下的函數可以將某句柄歸設為非阻塞狀態:fcntl( fd , F_SETFL, O_NONBLOCK);
在非阻塞狀態下,recv 接口在被調用后立即返回,返回值代表了不同的含義,如下所述。
recv 返回值大于 0,表示接收數據完畢,返回值即是接收到的字節數。
recv 返回 0,表示連接已經正常斷開。
recv 返回 -1 ,且 errno 等于 EAGAIN ,表示 recv 操作還沒執行完成。
recv 返回 -1,且 errno 不等于 EAGAIN ,表示 recv 操作遇到系統錯誤 errno。
可以看到服務器線程可以通過循環調用 recv 接口,可以在單個線程內實現對所有連接的數據接收。但是上述模型絕不被推薦,因為循環調用 recv將大幅度占用 CPU 使用率。
此外,在這個方案 recv 更多的是起到檢測“操作是否完成”的作用,實際操作系統提供了更為高效的檢測“操作是否完成”作用的接口,例如 select多路復用模式,可以次檢測多個連接是存活躍。
-
IO
+關注
關注
0文章
435瀏覽量
39078 -
內核
+關注
關注
3文章
1363瀏覽量
40228 -
Linux
+關注
關注
87文章
11227瀏覽量
208922 -
網絡
+關注
關注
14文章
7516瀏覽量
88627
發布評論請先 登錄
相關推薦
評論