i/o從直觀上來看是輸入/輸出;
從計算機架構的角度上來說:任何涉及到計算機核心(cpu和記憶體)與其他裝置間的資料遷移的過程就是i/o。本體是計算機核心(cpu和記憶體)。例如從硬碟上讀取資料到記憶體,是一次輸入,將記憶體中的資料寫入到硬碟就產生了輸出。在計算機的世界裡,這就是io的本質。
最後,從程式設計的角度去理解i/o
此事,i/o主體是其應用程式的執行態,即程序,特別強調的是我們的應用程式其實並不存在實質的io過程,真正的io過程是作業系統的事情,這裡把應用程式的io操作分為兩種動作:io呼叫和io執行。io呼叫是由程序發起,io執行是作業系統的工作。因此,更準確些來說,此時所說的io是應用程式對作業系統io功能的一次觸發,即io呼叫。
io呼叫的目的是將程序的內部資料遷移到外部即輸出,或將外部資料遷移到程序內部即輸入。這裡,外部資料指非程序空間資料,在程式設計時,通常討論的場景是來自外部儲存裝置的資料,如硬碟、cd-rom、以及需要socket通訊傳輸的網路資料。
以乙個程序的輸入型別的io呼叫為例,它將完成或引起如下工作內容:
程序向作業系統請求外部資料
作業系統將外部資料載入到核心緩衝區
作業系統將資料從核心緩衝區拷貝到程序緩衝區
程序讀取資料繼續後面的工作
從上面的描述來看,我們更容易理解乙個io操作,應用程式和作業系統都幹了些什麼,也幫助我們更容器理解阻塞和非阻塞,非同步和同步的相關io程式設計概念。同步(synchronous) io和非同步(asynchronous) io,阻塞(blocking) io和非阻塞(non-blocking)io分別是什麼,到底有什麼區別?這個問題其實不同的人給出的答案都可能不同,比如wiki,就認為asynchronous io和non-blocking io是乙個東西。這其實是因為不同的人的知識背景不同,並且在討論這個問題的時候上下文(context)也不相同。所以,為了更好的回答這個問題,我先限定一下本文的上下文。
本文討論的背景是linux環境下的network io。
在linux中預設情況下所有的socket都是阻塞的,乙個典型的讀操作流程大概是這樣:
當使用者程序呼叫了recvfrom這個系統呼叫,kernel就開始了io的第一階段:準備資料。對於network io來說,很多時候資料在一開始還沒有到達(比如,還沒收到乙個完整的udp包),這個時候核心就要等待足夠的資料到來。而在使用者程序這邊,整個程序會被阻塞。當kernel一直等到資料準備好了,它會將資料從kernel拷貝到程序記憶體空間,然後kernel返回結果,使用者程序才解除block的狀態,重新執行起來。
所以,blocking io的特點是在io執行的兩個階段都被阻塞了。
non-blocking io
linux下,可以通過設定socket使其變為non-blocking。當對乙個non-blocking socket執行讀操作時,流程是這個樣子:
從圖中可以看出,當使用者程序發出read操作時,如果kernel中的資料還沒有準備好,那麼它並不會block使用者程序,而是立刻返回乙個error。從使用者程序角度講 ,它發起乙個read操作後,並不需要等待,而是馬上就得到了乙個結果。使用者程序判斷結果是乙個error時,它就知道資料還沒有準備好,於是它可以再次傳送read操作。一旦kernel中的資料準備好了,並且又再次收到了使用者程序的system call,那麼它馬上就將資料拷貝到了使用者記憶體,然後返回。
所以,使用者程序其實是需要不斷的主動詢問kernel資料好了沒有。
io multiplexing
io multiplexing這個詞可能有點陌生,但是如果我說select,epoll,大概就都能明白了。有些地方也稱這種io方式為event driven io。我們都知道,select/epoll的好處就在於單個process就可以同時處理多個網路連線的io。它的基本原理就是select/epoll這個function會不斷的輪詢所負責的所有socket,當某個socket有資料到達了,就通知使用者程序。它的流程如圖:
當使用者程序呼叫了select,那麼整個程序會被block,而同時,kernel會「監視」所有select負責的socket,當任何乙個socket中的資料準備好了,select就會返回。這個時候使用者程序再呼叫read操作,將資料從kernel拷貝到使用者程序。
這個圖和blocking io的圖其實並沒有太大的不同,事實上,還更差一些。因為這裡需要使用兩個system call (select 和 recvfrom),而blocking io只呼叫了乙個system call (recvfrom)。但是,用select的優勢在於它可以同時處理多個connection。(多說一句。所以,如果處理的連線數不是很高的話,使用select/epoll的web server不一定比使用multi-threading + blocking io的web server效能更好,可能延遲還更大。select/epoll的優勢並不是對於單個連線能處理得更快,而是在於能處理更多的連線。)
在io multiplexing model中,實際中,對於每乙個socket,一般都設定成為non-blocking,但是,如上圖所示,整個使用者的process其實是一直被block的。只不過process是被select這個函式block,而不是被socket io給block。
asynchronous i/o
linux下的asynchronous io其實用得很少。先看一下它的流程:
使用者程序發起read操作之後,立刻就可以開始去做其它的事。而另一方面,從kernel的角度,當它受到乙個asynchronous read之後,首先它會立刻返回,所以不會對使用者程序產生任何block。然後,kernel會等待資料準備完成,然後將資料拷貝到使用者記憶體,當這一切都完成之後,kernel會給使用者程序傳送乙個signal,告訴它read操作完成了。
本文**於:
理解計算機系統
1 可以將系統抽象為5個部分 輸入,輸出,儲存,計算 處理 通訊網路 2 其實這也是計算機的基本體系架構,也是人本身的模型抽象 3 系統作為乙個整體向使用者提供服務,這種模型可以稱作面向系統,當然把系統看做是乙個物件,就可以叫物件導向,如果你把關注點放在系統的輸入,輸出,進行運算和處理的過程,這叫面...
《深入理解計算機系統》 系統級I O
關於i o可以先參考這些文章,但是這裡可能還是有所不同。分析系統級別的i o有什麼不一樣的地方。檔案i o 高階i o 標準庫i o 開篇介紹了三個級別的i o的區別之處。所有語言的執行時系統都提供執行i o的較高階別的工具。例如,標準i o庫 在unix系統中,是通過使用由核心提供的系統級i o函...
深入理解計算機系統 計算機系統漫遊
第一章 計算機系統漫遊 計算機系統是由硬體和系統軟體組成的。所有計算機系統都是由相似的硬體和軟體組成,它們又執行著相似的功能。以hello程式為例。1.1資訊就是位 上下文 hello程式的生命是從源程式 原始檔 開始的。源程式是程式設計師編寫的,hello.c。源程式是 0和1 的位元位,8個一組...