I/O模型

事實上,I/O操作根據設備的不同分爲很多種類型,比如內存I/O、網絡I/O、磁盤I/O。

一、名詞解釋:

I/O完成的過程:

一旦發起I/O調用,立即陷入內核模式。首先要把磁盤讀進內存。(這段內存是內核訪問到的)還需將內核中的內存複製到進程的內存空間中去(這段過程纔是真正的I/O過程)。

I/O等待:

造成等待的原因非常多,比如Web服務器在等待用戶的訪問,這便是等待,因爲它不知道誰會來訪問,所以只能等。隨後當某個用戶通過瀏覽器發出請求,Web服務器與瀏覽器建立TCP連接後,又要等待用戶發出HTTP請求數據,用戶的請求數據在網絡上傳輸需要時間,進入服務器接收緩衝區隊列以及被複制到進程地址空間都需要時間。另外,假如瀏覽器和Web服務器採用HTTP長連接模式,那麼在超時關閉連接之前,服務器還要等待瀏覽器發送其他的請求,這也是I/O等待。

同步:

指進程發出一個過程(功能、函數)調用後,在沒有得到結果之前,該調用將不會返回。

阻塞:

阻塞調用是指調用結果返回之前,當前線程會被掛起(線程進入睡眠狀態)。函數只有在得到結果之後纔會返回。

非阻塞:

指在不能立刻得到結果之前,被調用函數不會阻塞當前線程,而會立刻返回。


內存映射:

Linux內核提供一種訪問磁盤文件的特殊方式,它可以將內存中某塊地址空間和我們喲啊指定的磁盤文件相關聯,從而把我們對這款內存的訪問轉換爲對磁盤文件的訪問,這種技術稱爲內存映射(Memory Mapping).也就是說將I/O完成過程的第二部操作將內核中的內存複製到進程的內存空間中省去,大大提高了系統性能。nginx就是採用了這種技術。


二、I/O模型

同步阻塞I/O:

指當進程調用某些涉及I/O操作的系統調用或庫函數時,比如accept()、send()、recv()等,進程便暫停下來,等待I/O操作完成後再繼續運行。這是一種簡單而有效的I/O模型,它可以和多進程結合起來有效的利用CPU資源,但是其代價就是多進程的大量內存開銷。這種等待也叫做忙等待。


同步非阻塞I/O:

在同步阻塞I/O中,進程實際上等待的時間可能包括兩部分,一個是等待數據的就緒,另一個等待數據的複製,對於網絡I/O來說,前者的時間可能要更長一些。

與此不同的是,同步非阻塞I/O的調用不會等待數據的就緒,如果數據不可讀或者不可寫,它會立即告訴進程。比如使用非阻塞recv()接收網絡數據的時候,如果網卡緩衝區中沒有可接收的數據,函數就及時返回,告訴進程沒有數據可讀了。相比於阻塞I/O,這種非阻塞I/O結合反覆輪詢來嘗試數據是否就緒,防止進程被阻塞,最大的好處便在於可以在一個進程裏同時處理多個I/O操作。


多路I/O就緒通知:

在實際應用中,特別是Web服務器,同時處理大量的文件描述符是必不可少的,但是使用同步非阻塞I/O顯然不是最佳的選擇,在這種模型下,如果服務器想要同時接收多個TCP連接的數據,就必須輪流對每個socket調用接受數據的方法,比如recv()。不管這些socket有沒有可以接收的數據,都要詢問一遍,加入大部分socket並沒有數據可以接收,那麼進程便會浪費很多CPU時間用於檢查這些socket,因此多路I/O就緒通知的出現,提供了對大量文件描述就緒檢查的高性能方案,它允許進程通過一種方法來同時監視所有文件描述符,並可以快速得所有就緒的文件描述符,然後知針對這些文件描述符進行數據訪問。

select:

最早於1983年出現在4.2BSD中,它通過一個select()系統調用來監視包含多個文件描述符的數組,當select()返回後,該數組中就緒的文件描述符便會被內核修改標誌位,使得進程可以獲得這些文件描述符從而進行後續的讀寫操作。

缺點:單個進程能夠監視的文件描述符的數量存在最大限制,在Linux上一般爲1024,不過可以通過修改宏定義甚至重新編譯內核的方式提升這一限制。所以假如使用了select的服務器已經維持了1024個連接,那麼你的請求可能會被拒絕。另外,select()所維護的存儲大量文件描述符的數據結構,隨着文件描述符數量的增大,其複製的開銷也線性增長。同時,由於網絡響應時間的延遲使得大量的TCP連接處於非活躍狀態,但調用select()會對所有socket進行一次線性掃描,所以這也浪費了一定的開銷。


poll:

poll在1986年誕生於System V Release 3,顯然UNIX不願意直接沿用BSD的select,而是自己重新實現一遍,它和select在本質上沒有多大差別,但是POLL沒有最大文件描述符數量的限制。


水平觸發(Level Triggered):

如果進程沒有對其進行I/O操作,那麼下次調用sleect()或poll()的時候將再次報告這些文件描述符,所以它們一般不會丟失就緒的消息。


邊緣觸發

只會通知一次,效率更高。


異步I/O

異步則指主動請求數據後便可以繼續處理其他任務,隨後等待I/O操作完畢的通知,這可以使進程在數據讀寫是也不發生阻塞。

POSIX1003.1標準爲異步方式訪問文件定義了一套庫函數,這裏的異步I/O(AIO)實際上就是指當用戶態進程調用庫函數訪問文件時,進行必要的快速註冊,比如進入讀寫操作隊列,然後函數馬上返回,這時候真正的I/O傳輸還沒有開始呢。可以看出,這種機制的真正意義上的異步I/O,而且是非阻塞的,它可以是進程的發起I/O操作後繼續運行,讓CPU處理和I/O操作達到更好的重疊。


同步I/O與異步I/O的區別:

同步和異步、阻塞和非阻塞很容易被混用,其實它們完全不是一回事,而且它們修飾的對象也不同。阻塞和非阻塞是指當進程訪問的數據如果尚未就緒,進程是否需要等待,簡單說這相當於函數內部的實現區別,即未就緒時是直接返回還是等待就緒;而同步和異步是指訪問數據的機制,同步一般指主動請求並等待I/O操作完畢的方式,當數據就緒後在讀寫的時候必須阻塞,則指主動請求數據後便可以繼續處理其他任務,隨後等待I/O操作完畢的通知,這可以使進程在數據讀寫是也不發生阻塞。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章