阻塞I/O、非阻塞I/O和I/O多路復用


一、阻塞I/O

首先,要從你常用的IO操作談起,比如read和write,通常IO操作都是阻塞I/O的,也就是說當你調用read時,如果沒有數據收到,那么線程或者進程就會被掛起,直到收到數據。阻塞的意思,就是一直等着。阻塞I/O就是等着數據過來,進行讀寫操作。應用的函數進行調用,但是內核一直沒有返回,就一直等着。應用的函數長時間處於等待結果的狀態,我們就稱為阻塞I/O。每個應用都得等着,每個應用都在等着,浪費啊!很像現實中的情況。大家都不干活,等着數據過來,過來工作一下,沒有的話繼續等着。

二、非阻塞I/O

非阻塞IO很簡單,通過fcntl(POSIX)或ioctl(Unix)設為非阻塞模式,這時,當你調用read時,如果有數據收到,就返回數據,如果沒有數據收到,就立刻返回一個錯誤,如EWOULDBLOCK。這樣是不會阻塞線程了,但是你還是要不斷的輪詢來讀取或寫入。相當於你去查看有沒有數據,告訴你沒有,過一會再來吧!應用過一會再來問,有沒有數據?沒有數據,會有一個返回。但是依舊很不好。應用必須得過一會來一下,問問內核有木有數據啊。這和現實很像啊!好多情況都得去某些地方問問好了沒有?木有,明天再過來。明天,好了木有?木有,后天再過來。。。。。忙碌的應用。。。。

三、I/O多路復用

多路復用是指使用一個線程來檢查多個文件描述符(Socket)的就緒狀態,比如調用select和poll函數,傳入多個文件描述符(FileDescription,簡稱FD),如果有一個文件描述符(FileDescription)就緒,則返回,否則阻塞直到超時。得到就緒狀態后進行真正的操作可以在同一個線程里執行,也可以啟動線程執行(比如使用線程池)。蝦米意思?就是派一個代表,同時監聽多個文件描述符是否有數據到來。等着等着,如有有數據,就告訴某某你的數據來啦!趕緊來處理吧。有沒有很感動,一個人待着,幫了很多人。醫院的黃牛,一個人排隊,大家只要把錢給它,它就會把號給需要的人,開個玩笑。。。。
參考如下:

作者:用心閣
鏈接:https://www.zhihu.com/question/28594409/answer/74003996
來源:知乎
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
 

四、多路復用的三種方式(都是上面的I/O的多路復用,但是進行了改進)

1、select

 

  【1】每次調用select()都需要把fd(文件描述符)從用戶態拷貝到內核態,開銷比較大

  【2】每次都需要在內核遍歷傳入的fd(文件描述符)

  【3】select支持文件數量比較小,默認是1024

2、poll
  poll的實現和select非常相似,只是描述fd集合的方式不同,poll使用pollfd結構而不是select的fd_set結構,支持的文件數量比較多,不僅僅是1024
3、epoll

  select/poll只提供了一個函數,selct/poll函數,但是epoll一下子就提供了3個函數,真是人多力量大,難怪這么強,如下3個函數:

epoll_create,epoll_ctl和epoll_wait,epoll_create是創建一個epoll句 柄;epoll_ctl是注冊要監聽的事件類型;epoll_wait則是等待事件的產生。

epoll既然是對select和poll的改進,就應該能避免上述的三個缺點。那epoll都是怎么解決的呢?在此之前,我們先看一下epoll和select和poll的調用接口上的不同,select和poll都只提供了一個函數——select或者poll函數。而epoll提供了三個函數,epoll_create,epoll_ctl和epoll_wait,epoll_create是創建一個epoll句柄;epoll_ctl是注冊要監聽的事件類型;epoll_wait則是等待事件的產生。

  對於第一個缺點,epoll的解決方案在epoll_ctl函數中。每次注冊新的事件到epoll句柄中時(在epoll_ctl中指定EPOLL_CTL_ADD),會把所有的fd拷貝進內核,而不是在epoll_wait的時候重復拷貝。epoll保證了每個fd在整個過程中只會拷貝一次。

  對於第二個缺點,epoll的解決方案不像select或poll一樣每次都把current輪流加入fd對應的設備等待隊列中,而只在epoll_ctl時把current掛一遍(這一遍必不可少)並為每個fd指定一個回調函數,當設備就緒,喚醒等待隊列上的等待者時,就會調用這個回調函數,而這個回調函數會把就緒的fd加入一個就緒鏈表)。epoll_wait的工作實際上就是在這個就緒鏈表中查看有沒有就緒的fd(利用schedule_timeout()實現睡一會,判斷一會的效果,和select實現中的第7步是類似的)。

  對於第三個缺點,epoll沒有這個限制,它所支持的FD上限是最大可以打開文件的數目,這個數字一般遠大於2048,舉個例子,在1GB內存的機器上大約是10萬左右,具體數目可以cat /proc/sys/fs/file-max察看,一般來說這個數目和系統內存關系很大。

總結:

(1)select,poll實現需要自己不斷輪詢所有fd集合,直到設備就緒,期間可能要睡眠和喚醒多次交替。而epoll其實也需要調用epoll_wait不斷輪詢就緒鏈表,期間也可能多次睡眠和喚醒交替,但是它是設備就緒時,調用回調函數,把就緒fd放入就緒鏈表中,並喚醒在epoll_wait中進入睡眠的進程。雖然都要睡眠和交替,但是select和poll在“醒着”的時候要遍歷整個fd集合,而epoll在“醒着”的時候只要判斷一下就緒鏈表是否為空就行了,這節省了大量的CPU時間。這就是回調機制帶來的性能提升。

(2)select,poll每次調用都要把fd集合從用戶態往內核態拷貝一次,並且要把current往設備等待隊列中掛一次,而epoll只要一次拷貝,而且把current往等待隊列上掛也只掛一次(在epoll_wait的開始,注意這里的等待隊列並不是設備等待隊列,只是一個epoll內部定義的等待隊列)。這也能節省不少的開銷。

參考:http://www.cnblogs.com/Anker/p/3265058.html

五、python的select模塊

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM