Linux磁盤IO、網絡IO、零拷貝詳解


一、什么是I/O?

 
在計算機操作系統中,所謂的I/O就是輸入(input)和輸出(output),也可以理解為讀(read)和寫(write),針對不同的對象,I/O模式可以划分為磁盤IO模型和網絡IO模型
 

二、IO操作本質是用戶空間和內核空間的轉換,規則如下:

 
  • 內存空間分為用戶空間和內核空間,也稱為用戶緩沖區和內核緩沖區
  • 用戶的應用程序不能直接操作內核空間,需要將數據從內核空間拷貝到用戶空間才能使用
  • 無論是read操作,還是write操作,都只能在內核空間里執行
  • 磁盤IO操作、網絡請求加載到內存的數據一開始都是先放到內核緩沖區的
 

三、IO調用步驟之讀(read)操作和寫(write)

注:綠色的圖型表示數據存儲的位置,綠色的箭頭則表示數據的復制

圖1解析:

1、從左到右:Linux IO包含兩部分,磁盤IO(Disk I/O)和網絡IO(Network I/O

2、從上到下:存儲又被划分為三部分:用戶空間(User space)、內核空間(Kerner space)及物理設備(Physical devices

從上到下,為什么划分為三層?
Linux操作系統為了安全考慮,其內核管理了幾乎所有的硬件設備,不允許用戶進程直接訪問。因此,邏輯上計算機被分為用戶空間和內核空間(外設及其驅動是被划分在內核空間的)
運行在用戶空間的進程就是用戶態,運行在內核空間的進程就是內核態。用戶態的進程,訪問不了內核空間的數據,所以就需要由內核態的進程把數據拷貝到用戶態。
 

3、緩存I/O(Buffered I/O

 
3.1、磁盤IO(Disk I/O):
 
讀操作:當應用程序調用read()方法時,操作系統檢查內核緩沖區是否存在需要的數據,如果存在,那么就直接把內核空間的數據copy到用戶空間,供用戶的應用程序使用;如果內核緩沖區沒有需要的數據,則通過DMA方式從磁盤中讀取數據到內核緩沖區( DMA Copy),然后把內核空間的數據copy到用戶空間( Cpu Copy)(上圖綠色實線部分)
 
寫操作:當應用程序調用write()方法時,應用程序將數據從用戶空間copy到內核空間的緩沖區(如果用戶空間沒有相應的數據,則需要從磁盤-->內核緩沖區-->用戶緩沖區依次讀取),這時對用戶程序來說寫操作就已經完成,至於什么時候把數據再寫到磁盤,由操作系統決定。操作系統將要寫入磁盤的數據先保存於系統為寫緩存分配的內存空間中,當保存到內存池中的數據達到一個程度時,便將數據保存在硬盤中。這樣可以減少實際的磁盤操作,有效的保護磁盤免於重復的讀寫操作而導致的損壞,也能減少寫入所需的時間。除非應用程序顯式的調用了sync命令,立即把數據寫入磁盤。如果應用程序沒准備好寫的數據,則必須先從磁盤讀取數據才能執行寫操作,這時會涉及到四次緩沖區的copy:
a、第一次從磁盤的緩沖區讀取數據到內核緩沖區(DMA Copy);
b、第二次從內核緩沖區復制到用戶緩沖區(Cpu Copy);
c、第三次從用戶緩沖區寫到內核緩沖區(Cpu Copy);
d、第四次從內核緩沖區寫到磁盤(DMA Copy);(上圖綠色實線部分雙向箭頭)
 
磁盤IO延時
  • 尋道時間:把磁頭移動到指定磁道上所經歷的時間
  • 旋轉延時間:指定扇區移動到磁頭下面所經歷的時間
  • 傳輸時間:數據的傳輸時間(數據讀出或寫入的時間)
 
Page cache 和Buffer cache:
Page cache也叫頁緩沖或文件緩沖。是由好幾個磁盤塊構成,大小通常是4K,在64位系統上為8K。構成的幾個磁盤塊在物理磁盤上不一定連續,文件的組織單位為一頁,也就是一個Page cache大小。Page cache是建立在文件系統(Ex4)之上的,因此其緩存的是邏輯數據。Buffer cache是建立在塊層之上的,因此其緩存的是物理輯數據。Linux大約在2.4.10之后,Page cache與Buffer cache合並了
(所以圖中Buffer cache是灰色的,為了更容易理解IO原理,黃色和灰色部分都可以不考慮了)
 
DMA(直接內存訪問)方式:
DMA是一種與CPU共享內存總線的設備,它可以代替CPU,把數據從內存到設備之間進行拷貝。僅在傳送一個或多個數據塊的開始和結束時,才需CPU干預(發送DMA中斷),整塊數據的傳送是在DMA的控制器的控制下完成的。
 
3.2、網絡I/O(Network I/O)
 
讀操作:網絡IO即可以從物理磁盤中讀數據,也可以從socket中讀數據(從網卡中獲取)。當從物理磁盤中讀數據的時候,其流程和磁盤IO的讀操作一樣。當從socket中讀數據,應用程序需要等待客戶端發送數據,如果客戶端還沒有發送數據,對應的應用程序將會被阻塞,直接客戶端發送了數據,該應用程序才會被喚醒,從Socket協議棧(即網卡)中讀取客戶端發送的數據到內核空間(DMA copy),然后把內核空間的數據copy到用戶空間
 
寫操作:為了簡化描述,我們假設網絡IO的數據從磁盤中獲取,讀寫操作流程如下:
  • 當應用程序調用read()方法時,通過DMA方式將數據從磁盤拷貝到內核緩沖區(DMA copy);
  • 由cpu控制,將內核緩沖區的數據拷貝到用戶空間的緩沖區中,供應用程序使用(CPU copy);
  • 當應用程序調用write()方法時,cpu會把用戶緩沖區的數據copy到內核緩沖區的Socket Buffer中(CPU copy);
  • 最后通過DMA方式將內核空間中的Socket Buffer拷貝到Socket協議棧(即網卡設備)中傳輸(DMA copy);
 
網絡IO的延時:網絡IO主要延時是由服務器響應延時 + 帶寬限制 + 網絡延時 + 跳轉路由延時 + 本地接收延時 決定。一般為幾十到幾千毫秒,受環境影響較大。所以一般來說,網絡IO延時要大於磁盤IO延時
 
緩存I/O的一致性和安全性:如果出現進程死,內核死,掉電這樣事件發生。數據會丟失嗎?
  • 進程死:如果數據還處在application cache或clib cache的時候,數據會丟失;
  • 內核死:即使進入了page cache(完成了write),如果沒有進行sync操作,數據還是會丟失;
  • 掉電:進行了sync,數據就一定寫入了磁盤了嗎?答案是:不一定;
  • 注意到圖1中,磁盤旁邊的綠色圖型了嗎?它表示的是磁盤上的緩存。寫數據達到一個程度時才真正寫入磁盤
 
緩存I/O的缺點:在緩存I/O機制中,DMA方式可以將數據直接從磁盤讀到頁緩存中,或者將數據從頁緩存直接寫回到磁盤上,而不能直接在應用程序地址空間和磁盤之間進行數據傳輸。這樣的話,數據 在傳輸過程中需要在應用程序地址空間和頁緩存之間進行多次數據拷貝操作,這些數據拷貝操作所帶來的CPU以及內存開銷是非常大的。對於某些特殊的應用程序來說,避開操作系統內核緩沖區,而直接在應用程序地址空間和磁盤之間傳輸數據,會比使用操作系統內核緩沖區獲取更好的性能,因此引入"Direct I/O"。
 
 

4、直接I/O(Direct I/O

 
凡是通過直接 I/O 方式進行數據傳輸,數據均直接在用戶地址空間的緩沖區和磁盤之間直接進行傳輸,完全不需要頁緩存的支持。
 
進程在打開文件的時候設置對文件的訪問模式為 O_DIRECT ,這樣就等於告訴操作系統進程在接下來使用 read() 或者 write() 系統調用去讀寫文件的時候使用的是直接 I/O 方式,所傳輸的數據均不經過操作系統內核緩存空間。
直接I/O優點:減少操作系統緩沖區和用戶地址空間的拷貝次數。降低CPU開銷和內存帶寬 。對於某些應用程序來說簡單是福音,將會大大提高性能。
直接I/O缺點:直接 I/O 並不總能讓人如意。直接 I/O 的開銷也很大,應用程序沒有控制好讀寫,將會導致磁盤讀寫的效率低下。磁盤的讀寫是通過磁頭的切換到不同的磁道上讀取和寫入數據,如果需要寫入數據在磁盤位置相隔比較遠,就會導致尋道的時間大大增加,寫入讀取的效率大大降低。
Direct I/O 本質是 DMA 設備把數據從用戶空間拷貝到設備,或是從設備拷貝到用戶空間。

 

5、mmap

mmap 本質是內存共享機制,它把 page cache 地址空間映射到用戶空間,換句話說,mmap 是一種特殊的 Buffered I/O
offset 是文件中映射的起始位置,length 是映射的長度。
mmap內存映射原理:
mmap 內存映射過程:
    • 進程在虛擬地址空間中為映射創建虛擬映射區域。
    • 內核把文件物理地址和進程虛擬地址進行映射。
    • 進程發起對這片映射空間的訪問,引發缺頁異常,實現文件內容到物理內存(主存)的拷貝。
    • 換句話說,在調用 mmap 后,只是在進程的虛擬空間中分配了一段空間,真實的物理地址還不會分配的。
    • 當進程第一次訪問這段空間(當作內存一樣),CPU 陷入 OS 內核執行異常處理。然后異常處理會在這個時間分配物理內存,並用文件的內容填充這片內存,然后才返回進程的上下文,這時進程才會感知到這片內存里有數據。
 
mmap本質:
mmap 本質是內存共享機制,它把 page cache 地址空間映射到用戶空間,換句話說,mmap 是一種特殊的 Buffered I/O。
因為底層有 CPU 的 MMU 支持,自然會轉換到物理區域,對於進程而言是無感知。所以,磁盤數據加載到 page cache 后,用戶進 程可以通過指針操作直接讀寫 page cache,不再需要系統調用和內存拷貝。
因此,offset 必須是按 page size 對齊的(不對齊的話就會映射失敗)。
mmap 映射區域大小必須是物理頁大小(page size)的整倍數(32 位系統中通常是 4k)。length 對齊是靠內核來保證的,比如文件長度是 10KB,你映射了 5KB,那么內核會將其擴充到 8KB。
 

6、Linux五大網絡IO模型

1.BIO:阻塞模式IO

舉個例子:
一個人去 商店買一把菜刀,
他到商店問老板有沒有菜刀(發起系統調用)
如果有(表示在內核緩沖區有需要的數據)
老板直接把菜刀給買家(從內核緩沖區拷貝到用戶緩沖區)
這個過程買家一直在等待


如果沒有,商店老板會向工廠下訂單(IO操作,等待數據准備好)
工廠把菜刀運給老板(進入到內核緩沖區)
老板把菜刀給買家(從內核緩沖區拷貝到用戶緩沖區)
這個過程買家一直在等待
是同步io

2.NIO:非阻塞模式IO

用戶進程發起請求,如果數據沒有准備好,那么立刻告知用戶進程未准備好;此時用戶進程可選擇繼續發起請求、或者先去做其他事情,稍后再回來繼續發請求,直到被告知數據准備完畢,可以開始接收為止; 數據會由用戶進程完成拷貝
舉個例子:
一個人去 商店買一把菜刀,
他到商店問老板有沒有菜刀(發起系統調用)
老板說沒有,在向工廠進貨(返回狀態)
買家去別地方玩了會,又回來問,菜刀到了么(發起系統調用)
老板說還沒有(返回狀態)
買家又去玩了會(不斷輪詢)
最后一次再問,菜刀有了(數據准備好了)
老板把菜刀遞給買家(從內核緩沖區拷貝到用戶緩沖區)


整個過程輪詢+等待:輪詢時沒有等待,可以做其他事,從內核緩沖區拷貝到用戶緩沖區需要等待
是同步io

3.I/O多路復用模型

類似BIO,只不過找了一個代理,來掛起等待,並能同時監聽多個請求; 數據會由用戶進程完成拷貝
舉個例子:多個人去 一個商店買菜刀,
多個人給老板打電話,說我要買菜刀(發起系統調用)
老板把每個人都記錄下來(放到select中)
老板去工廠進貨(IO操作)
有貨了,再挨個通知買到的人,來取刀(通知/返回可讀條件)
買家來到商店等待,老板把到給買家(從內核緩沖區拷貝到用戶緩沖區)


多路復用:老板可以同時接受很多請求(select模型最大1024個,epoll模型),
但是老板把到給買家這個過程,還需要等待,
是同步io
 
select本質也是輪詢最多可以監聽1024個,而epoll模型是事件驅動,好了會主動告訴你
-select:小明,你寫好了么?小紅你寫好了么?.......
-epoll:同學寫好了,舉手告訴老師來檢查(nginx、tornado用的是epoll)windows平台不支持epoll,用的是select

4.信號驅動IO

事先發出一個請求,當有數據后會返回一個標識回調,這時你可以去請求數據(不是輪詢請求,而是收到返回標識后請求)。好比銀行排號,當叫到你的時候,你就可以去處理業務了(復制數據時阻塞)。
信號驅動IO,調用sigaltion系統調用,當內核中IO數據就緒時以SIGIO信號通知請求進程,請求進程再把數據從內核讀入到用戶空間,這一步是阻塞的

5.異步IO--AIO

發起請求立刻得到回復,不用掛起等待; 數據會由內核進程主動完成拷貝,目前不成熟
舉個例子:還是買菜刀
現在是網上下單到商店(系統調用)
商店確認(返回)
商店去進貨(io操作)
商店收到貨把貨發個賣家(從內核緩沖區拷貝到用戶緩沖區)
買家收到貨(指定信號)


整個過程無等待
異步io
 
總結:
  • 同步I/O與異步I/O判斷依據是,是否會導致用戶進程阻塞
  • BIO中socket直接阻塞等待(用戶進程主動等待,並在拷貝時也等待)
  • NIO中將數據從內核空間拷貝到用戶空間時阻塞(用戶進程主動詢問,並在拷貝時等待)
  • IO Multiplexing中select等函數為阻塞、拷貝數據時也阻塞(用戶進程主動等待,並在拷貝時也等待)
  • AIO中從始至終用戶進程都沒有阻塞(用戶進程是被動的)


免責聲明!

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



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