一、什么是IO?我們都知道unix世界里、一切皆文件、而文件是什么呢?文件就是一串二進制流而已、不管socket、還是FIFO、管道、終端、對我們來說、一切都是文件、一切都是流、在信息交換的過程中、我們都是對這些流進行數(shù)據(jù)的收發(fā)操作、簡稱為I/O操作(input and output)、往流中讀出數(shù)據(jù)、系統(tǒng)調用read、寫入數(shù)據(jù)、系統(tǒng)調用write、不過話說回來了、計算機里有這么多的流、我怎么知道要操作哪個流呢?做到這個的就是文件描述符、即通常所說的fd、一個fd就是一個整數(shù)、所以對這個整數(shù)的操作、就是對這個文件(流)的操作、我們創(chuàng)建一個socket、通過系統(tǒng)調用會返回一個文件描述符、那么剩下對socket的操作就會轉化為對這個描述符的操作、不能不說這又是一種分層和抽象的思想、 二、IO交互通常用戶進程中的一個完整IO分為兩階段:
所以、對于一個網絡輸入操作通常包括兩個不同階段: IO有內存IO、網絡IO和磁盤IO三種、通常我們說的IO指的是后兩者 三、POSIX對IO底層交互感興趣的小伙伴可以好好了解一下POSIX(Portable Operating System Interface for Computing System)、我對深沉次原理也不怎么熟、之所以寫此篇博文也是為了后面的Java IO學習、深入淺出點到即可、此章節(jié)給有興趣的朋友一個引子、 四、IO模型《UNIX網絡編程》說得很清楚、5種IO模型分別是阻塞IO模型、非阻塞IO模型、IO復用模型、信號驅動的IO模型、異步IO模型、前4種為同步IO操作、只有異步IO模型是異步IO操作、請仔細閱讀IO交互便于理解IO模型 (一)阻塞IO模型當用戶進程調用了recvfrom這個系統(tǒng)調用、內核就開始了IO的第一個階段:準備數(shù)據(jù)、對于網絡IO來說、很多時候數(shù)據(jù)在一開始還沒有到達(比如、還沒有收到一個完整的UDP包)、這個時候內核就要等待足夠的數(shù)據(jù)到來、而在用戶進程這邊、整個進程會被阻塞、當內核一直等到數(shù)據(jù)準備好了、它就會將數(shù)據(jù)從內核中拷貝到用戶內存、然后返回結果、用戶進程才解除阻塞的狀態(tài)、重新運行起來、幾乎所有的程序員第一次接觸到的網絡編程都是從listen()、send()、recv()等接口開始的、這些接口都是阻塞型的、
(二)非阻塞IO模型當用戶進程發(fā)出read操作時、如果內核中的數(shù)據(jù)還沒有準備好、那么它并不會block用戶進程、而是立刻返回一個error、從用戶進程角度講、它發(fā)起一個read操作后、并不需要等待、而是馬上就得到了一個結果、用戶進程判斷結果是一個error時、它就知道數(shù)據(jù)還沒有準備好、于是它可以再次發(fā)送read操作、一旦內核中的數(shù)據(jù)準備好了、并且又再次收到了用戶進程的系統(tǒng)調用、那么它馬上就將數(shù)據(jù)拷貝到了用戶內存、然后返回、非阻塞的接口相比于阻塞型接口的顯著差異在于、在被調用之后立即返回、
(三)IO復用模型多個的進程的IO可以注冊到一個復用器(select)上、當用戶進程調用該select、select會監(jiān)聽所有注冊進來的IO、如果select所有監(jiān)聽的IO在內核緩沖區(qū)都沒有可讀數(shù)據(jù)、select調用進程會被阻塞、而當任一IO在內核緩沖區(qū)中有可數(shù)據(jù)時、select調用就會返回、而后select調用進程可以自己或通知另外的進程(注冊進程)來再次發(fā)起讀取IO、讀取內核中準備好的數(shù)據(jù)、多個進程注冊IO后、只有一個select調用進程被阻塞 IO復用相對阻塞和非阻塞更難簡單說明、所以額外解釋一段、其實IO復用模型和阻塞IO模型并沒有太大的不同、事實上、還更差一些、因為這里需要使用兩個系統(tǒng)調用(select和 recvfrom)、而阻塞IO模型只有一次系統(tǒng)調用(recvfrom)、但是、用select的優(yōu)勢在于它可以同時處理多個連接、所以如果處理的連接數(shù)不是很高的話、使用select/epoll的web server不一定比使用多線程加阻塞IO的web server性能更好、可能延遲還更大、select/epoll的優(yōu)勢并不是對于單個連接能處理得更快、而是在于能處理更多的連接
(四)信號驅動式IO模型信號驅動式IO就是指進程預先告知內核、向內核注冊一個信號處理函數(shù)、然后用戶進程返回不阻塞、當內核數(shù)據(jù)就緒時會發(fā)送一個信號給進程、用戶進程便在信號處理函數(shù)中調用IO讀取數(shù)據(jù)、從圖中明白實際IO內核拷貝到用戶進程的過程還是阻塞的、信號驅動式IO并沒有實現(xiàn)真正的異步、因為通知到進程之后、依然是由進程來完成IO操作、 這和后面的異步IO模型很容易混淆、需要理解IO交互并結合五種IO模型的比較閱讀
(五)異步IO模型用戶進程發(fā)起aio_read(POSIX異步IO函數(shù)aio_或者lio_開頭)操作之后、給內核傳遞描述符、緩沖區(qū)指針、緩沖區(qū)大小和read相同的三個參數(shù)以及文件偏移(與lseek類似)、告訴內核當整個操作完成時、如何通知我們、立刻就可以開始去做其它的事、而另一方面、從內核的角度、當它受到一個aio_read之后、首先它會立刻返回、所以不會對用戶進程產生任何阻塞、然后、內核會等待數(shù)據(jù)準備完成、然后將數(shù)據(jù)拷貝到用戶內存、當這一切都完成之后、內核會給用戶進程發(fā)送一個信號、告訴它aio_read操作完成了 異步IO的工作機制是:告知內核啟動某個操作、并讓內核在整個操作完成后通知我們、這種模型與信號驅動的IO區(qū)別在于、信號驅動IO是由內核通知我們何時可以啟動一個IO操作、這個IO操作由用戶自定義的信號函數(shù)來實現(xiàn)、而異步IO模型是由內核告知我們IO操作何時完成、 這和前面的信號驅動式IO模型很容易混淆、需要理解IO交互并結合五種IO模型的比較閱讀
(六)五種IO模型的比較
五、總結全篇最大的難點在于真正理解數(shù)據(jù)是如何從設備空間扭轉到內核空間再到用戶空間
|
|