網絡編程之IO模型——阻塞IO

網絡編程之IO模型——阻塞IO

阻塞IO

在Linux中,默認狀況下全部的socket都是blocking,一個典型的讀操做流程大概是這樣:程序員

當用戶進程調用了recvfrom這個系統調用,kernel就開始了IO的第一個階段:準備數據。對於network IO來講,不少時候數據在一開始尚未到達(好比,尚未收到一個完整的UDP包),這個時候kernel就要等待足夠的數據到來。web

而在用戶進程這邊,整個進程會被阻塞。當kernel一直等到數據準備好了,它就會將數據從kernel中拷貝到用戶內存,而後kernel返回結果,用戶進程才解除block的狀態,從新運行起來。

因此,blocking IO的特色就是在IO執行的兩個階段(等待數據和拷貝數據兩個階段)都被block了。數據庫

幾乎全部的程序員第一次接觸到的網絡編程都是從listen\(\)、send\(\)、recv\(\) 等接口開始的,
使用這些接口能夠很方便的構建服務器/客戶機的模型。然而大部分的socket接口都是阻塞型的。以下圖
ps:
所謂阻塞型接口是指系統調用(通常是IO接口)不返回調用結果並讓當前線程一直阻塞
只有當該系統調用得到結果或者超時出錯時才返回。

實際上,除非特別指定,幾乎全部的IO接口 ( 包括socket接口 ) 都是阻塞型的。這給網絡編程帶來了一個很大的問題,如在調用recv(1024)的同時,線程將被阻塞,在此期間,線程將沒法執行任何運算或響應任何的網絡請求。編程

一個簡單的解決方案:緩存

在服務器端使用多線程(或多進程)。
多線程(或多進程)的目的是讓每一個鏈接都擁有獨立的線程(或進程),
這樣任何一個鏈接的阻塞都不會影響其餘的鏈接。

該方案的問題是:tomcat

啓多進程或都線程的方式,在遇到要同時響應成百上千路的鏈接請求,
則不管多線程仍是多進程都會嚴重佔據系統資源,
下降系統對外界響應效率,並且線程與進程自己也更容易進入假死狀態。

改進方案:服務器

不少程序員可能會考慮使用「線程池」或「鏈接池」。「線程池」旨在減小建立和銷燬線程的頻率,
其維持必定合理數量的線程,並讓空閒的線程從新承擔新的執行任務。「鏈接池」維持鏈接的緩存池,儘可能重用已有的鏈接、
減小建立和關閉鏈接的頻率。這兩種技術均可以很好的下降系統開銷,都被普遍應用不少大型系統,如websphere、tomcat和各類數據庫等。

改進後方案其實也存在着問題:網絡

「線程池」和「鏈接池」技術也只是在必定程度上緩解了頻繁調用IO接口帶來的資源佔用。並且,所謂「池」始終有其上限,
當請求大大超過上限時,「池」構成的系統對外界的響應並不比沒有池的時候效果好多少。
因此使用「池」必須考慮其面臨的響應規模,並根據響應規模調整「池」的大小。
相關文章
相關標籤/搜索