I/O事件


I/O事件

最近在研究tornado和gevent,里面涉及了非阻塞I/O。在了解非阻塞I/O之前,需要先了解I/O事件

我們知道,內核有緩沖區。假設有兩個進程A,B,進程B想讀進程A寫入的東西(即進程A做寫操作,B做讀操作)。進程A需要先寫入到內核緩沖區中,然后B從內核緩沖區中讀取,如圖:

進程B會監聽內核緩沖區的變化

I/O事件的阻塞與同步

  1. 當內核緩沖區為空的時候,進程B會阻塞住
  2. 當A往內核緩沖區寫入時,內核緩沖區就不是空狀態了,這時候就會喚醒進程B
  3. 如果緩沖區滿了,但是進程B沒有被喚醒,就會通知進程A,告訴A不要再寫入數據了,也就是進程A被阻塞
  4. 當進程B被喚醒后,B就從緩沖區讀取數據,由於B在讀數據,緩沖區就不會是滿的狀態了,這時候就會通知A繼續寫數據,也就是進程A被喚醒
  5. 如果進程A還沒有喚醒,而緩沖區被B讀完了(緩沖區為空),這時候就會阻塞進程B

阻塞I/O的缺點

在阻塞I/O情況下,一個線程只能處理一個流的I/O事件。也就是說,如果想處理多個流的I/O事件,就必須使用多進程(fork),或者多線程——效率太低

處理I/O的第二種方法

除了使用阻塞I/O,還可以使用非阻塞I/O的方式。
最開始能想到的就是用輪詢的方法:依次詢問每個流,如果緩沖區不為空,就進行操作;否則,詢問下一個流
但是這種方法效率很低,會白白浪費掉CPU資源。於是便引入了代理——poll

poll

poll代理可以同時觀察很多I/O流事件,在空閑的時候(即沒有I/O事件的時候),會阻塞當前線程;當有I/O事件的時候,會被喚醒,然后把所有流輪詢一遍
這樣就能通過減少盲目的輪詢來減少對CPU資源的浪費
但是,使用這個也有缺點:由於每次喚醒都需要把所有流都輪詢一遍,當流很多的時候,輪詢的時間會很長

poll進化版——epoll

epoll是基於事件的輪詢,它會記錄是哪個流產生了I/O事件,然后針對這個流來進行操作,大大降低了復雜度


免責聲明!

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



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