從io模型到ppc,tpc,reactor,preactor
所有的系統I/O都分為兩個階段:等待就緒和操作.讀就是等待系統可讀和真正的讀;寫就是等待系統可寫和真正的寫.
1.網路io模型
這是我們常見的一張圖.
1.傳統的bio,就是同步阻塞的.當呼叫socket.read的時候.會阻塞. 知道系統可讀/寫,當真正去執行讀的時候(核心-->使用者),還是阻塞.
2.非阻塞io,當呼叫read時,如果不可讀,那麼直接返回一個標識,告訴你等會再來問.現在不可讀嘞.那麼好的,你就過一會問一下.當可讀時,去呼叫recvFrom系統呼叫.不過此時還是阻塞的.
3. 而多路複用是作業系統提供了一個函式.可以建立一個selector,用於遍歷你傳入的fd(也就是socket,萬物皆檔案).再註冊你關注的事件.同時你會阻塞selector.select().這個會返回你感興趣的事件.例如某個fd可讀了.那麼就是說,這個socket有資料傳送到了讀緩衝區.你可以拿走了,你拿走我的滑塊好滑起來.寫同理
4.前面3種在讀的時候,都是同步讀取的.你必須要不斷的問作業系統,好了沒,好了沒.,那麼Aio是可以使用者程式啥都不管.作業系統會在資料可讀的時候,把資料從核心空間拷貝到使用者空間.並呼叫使用者的回撥函式.執行
1.1 ppc
針對bio模型,在早期的程式設計中,對每個連線可以fork一個子程式(process per connection)
流程:
- 父程式接收連線
- 當收到一個連線後,fork一個子程式去處理請求
- 子程式處理完畢後,close
缺點:
- fork代價高.建立程式需要很多資源.而且需要copy父程式資源到子程式,例如頁表,記憶體等,而且會阻塞.釋放程式消耗也不小
- 父子程式通訊很複雜.需要ipc進行通訊.例如子程式告訴父程式處理了幾個請求,耗時等等.
- cpu一共就那麼幾個核.來100個連線就要有100多個程式.程式切換成本也不小
1.2 tpc
對每個連線建立一個執行緒(thread per connnection).聽起來執行緒比程式代價小,而且程式間通訊也很快,但是缺點仍然很大
流程:
- 主執行緒接收連線
- 當accpet到一個連線後,建立一個執行緒去處理
- 為了避免不斷建立執行緒,可以使用執行緒池來處理
缺點:
- 實際上和ppc一樣.只不過用的是執行緒,不過很多作業系統(linux).執行緒就是用的程式
- 雖然使用了連線池,但是一旦池子滿了,還是阻塞的
1.3reactor
反應堆,理解為事件反映.就是我們只關注事件,當一個連線來,一個連線可寫,一個連線可讀時,對事件作出反應,分發給handler去處理.Reactor 模式的核心組成部分包括 Reactor 和處理資源池.reactor用來處理連線,接收到連線後,會呼叫資源池去處理這個連線.有些文章也稱之為dispatch模式.
根據reactor和資源池多少可以分為以下幾種型別
1.3.1 單reactor單程式/執行緒
a.reactor用於監聽事件.當監聽到連線事件後,分發給acceptor處理;當監聽到讀寫事件後,分發給handle
b:acceptor接收完連線後,會分配handler處理後續請求.並註冊讀寫到selector
c:當可讀後,handler讀取資料,進行處理,然後傳送給client
缺點:可以看到只有一個執行緒在selector.當可讀後,只有一個執行緒處理請求.無法使用到多核優勢.redis在用.不過redis最近也要搞多執行緒.
1.3.2單reactor多執行緒
事件的監聽還是在主執行緒.不過處理事件,會交給其他執行緒,可以搞一個執行緒池
1.3.3多reactor和多執行緒
netty就是這麼實現,有多個reactor在監聽事件.監聽到之後,分配給執行緒池去處理.避免io處理和業務處理相互影響.
對比下netty實現,就很容易理解了.
1.一個reactor用來監聽連線事件(boss執行緒).當監聽到連線事件後,呼叫accept.同時把channel註冊到work reactor中(work執行緒),
2.work reactor用來監聽讀寫.當監聽到可讀後,呼叫業務執行緒池,進行處理,處理結束後,呼叫channel.write(msg);此時work reactor監聽到可寫事件.將資料發給client.ending
1.4 proactor
copy一個圖,大概意思就是
1.使用者會把事件和回撥函式註冊到核心
2.當有對應的事件時(這裡指io事件,連線/可讀/可寫),會自動執行連線/讀資料/寫資料.並呼叫註冊的回撥函式
想法是好的,不過作業系統沒有實現完美,因此很少有用proactor的
2.IO分類
網路io的時候,都是在操作記憶體,如果從磁碟讀寫一個檔案的時候,作業系統又分為哪幾種模式呢
2.1.直接io
直接io:直接把磁碟資料讀取到使用者態記憶體.
例如我們在程式碼中讀取一個檔案
Byte[] byteArray=new Byte[1024];
byteArray=file.read("/data/test.txt")
這裡byteArray就是申請一塊使用者態記憶體.
我們把磁碟資料直接讀到使用者態記憶體,可能耗時很慢.我們知道磁碟的操作和記憶體的操作速度不是一個級別的.尤其如果是隨機讀,那麼會更慢!但是,很多資料庫都會用直接讀寫,為啥呢?它可以自己做控制,
直接i
2.2快取io(標準 I/O)
由於從磁碟讀取到使用者記憶體很慢.因此作業系統給我們做了一層優化,那就是再加一層快取.這種設計也是隨處可見的,例如為了解決cpu和記憶體的效能差異,引入了L1,L2,L3快取.
這就是pageCache.預設情況下,我們讀寫磁碟資料都會使用pageCache,是不是使用直接io一定錯嘞?NO!,總結下直接io的優缺點
優點:
1.節約記憶體.少了一個pageCache的開銷(可以free 檢視pageCache).
2.可以自己控制.更靈活.例如一些自快取的系統,可以自己設計快取實現,比較常見的是資料庫系統.
缺點:
1.無法直接從pageCache讀取,pageCache是記憶體讀取,比較快,pageCache做了預讀優化,減少讀盤次數.
2.讀的話需要讀取磁碟,慢;寫的話,需要刷盤成功,也慢.
例如,mysql中寫binlog為什麼很快,其實預設就是寫到了pageCache中,然後通過設定fSync策略,設定什麼時候重新整理到磁碟.pageCache刷盤有一定條件.
a.使用者程式呼叫sync()或者fsync()
b.系統呼叫空閒記憶體低於特定閾值
c.髒頁的資料在記憶體中駐留的時間超過一個特定閾值
3.如果讀大檔案,會浪費,因為大檔案不會讀很多次.浪費記憶體,導致小的檔案也用不了pageCache,而且大檔案都連續儲存,讀磁碟也可以.即使用了pageCache,還是得讀到記憶體,多了一次拷貝
快取io
2.3 mmap
從快取io中可以看到,使用pageCache多了一次記憶體拷貝.那麼我們能不能優化這次拷貝呢?使用mmap,可以把使用者態的記憶體對映到核心態.回憶一下,使用者虛擬地址的佈局.
虛擬地址佈局
其中在使用者區有一段是用來做記憶體對映的.我們也是基於這段可以申請一段記憶體,和使用者態記憶體對映在同一個地址.
mmap
2.4 sendFile
mmap減少了一次核心空間到使用者空間的拷貝.但是還是避免不了程式從使用者態到核心態的切換.
sendFile
可以看到,只需要磁碟-->pageCache-->socket緩衝區--->網路卡緩衝區.不需要使用者態/核心態切換.
其實socketCache和網路卡直接的拷貝,也可以去除.需要用到一個支援收集操作的網路介面。主要的方式是待傳輸的資料可以分散在儲存的不同位置上,而不需要在連續儲存中存放。這樣一來,從檔案中讀出的資料就根本不需要被拷貝到 socket 緩衝區中去,而只是需要將緩衝區描述符傳到網路協議棧中去,之後其在緩衝區中建立起資料包的相關結構,然後通過 DMA 收集拷貝功能將所有的資料結合成一個網路資料包。網路卡的 DMA 引擎會在一次操作中從多個位置讀取包頭和資料。Linux 2.4 版本中的 socket 緩衝區就可以滿足這種條件,這種方法不但減少了因為多次上下文切換所帶來開銷,同時也減少了處理器造成的資料副本的個數。對於使用者應用程式來說,程式碼沒有任何改變。
2.5.socket緩衝區
再說下socket緩衝區,以write為例,當使用者呼叫socket.write時,其實會把資料從使用者空間拷貝到核心空間.也就是socket緩衝區.每個socket建立時,都會分配一定大小的讀緩衝區和寫緩衝區這兩個互不影響.大小可以自由配置.可以參考另一篇https://mp.csdn.net/editor/html/109929715
相關文章
- Reactor模型React模型
- Netty與Reactor模型NettyReact模型
- 框架篇:見識一下linux高效能網路IO+Reactor模型框架LinuxReact模型
- 從貧血模型到充血模型模型
- 深入Netty邏輯架構,從Reactor執行緒模型開始Netty架構React執行緒模型
- 從時間碎片角度理解阻塞IO模型及非阻塞模型模型
- IO模型模型
- Netty原始碼分析--Reactor模型(二)Netty原始碼React模型
- IO模型學習(一)IO模型分類模型
- 從HTML5 WebSocket到Socket.ioHTMLWeb
- io模型 WSAAsyncSelect模型
- Redis之單執行緒 Reactor 模型Redis執行緒React模型
- 聊聊Netty那些事兒之從核心角度看IO模型Netty模型
- 網路程式設計-Netty-Reactor模型程式設計NettyReact模型
- 【IO】Linux下的五種IO模型Linux模型
- linux的IO模型Linux模型
- IO通訊模型(三)多路複用IO模型
- 使用Project Reactor進行反應式資料流 - spring.ioProjectReactSpring
- 【NIO系列】——之IO模型模型
- (四)五種IO模型模型
- WinSock 重疊IO模型模型
- 從零學Netty(四)Reactor模式(含demo)NettyReact模式
- Reactor:深入理解reactor coreReact
- 從作業系統層面理解Linux下的網路IO模型作業系統Linux模型
- NIO(二)淺析IO模型模型
- 五種網路io模型模型
- 圖解四種 IO 模型圖解模型
- 五種傳統IO模型模型
- 面試官:Netty的執行緒模型可不只是主從多Reactor這麼簡單面試Netty執行緒模型React
- 今天我們來聊Java IO模型,BIO、NIO、AIO三種常見IO模型Java模型AI
- Reactor執行緒模型及其在Netty中的應用React執行緒模型Netty
- Netty原始碼分析之Reactor執行緒模型詳解Netty原始碼React執行緒模型
- 徹底搞懂徹底搞懂事件驅動模型 - Reactor事件模型React
- IO通訊模型(二)同步非阻塞模式NIO(NonBlocking IO)模型模式BloC
- 計算機網路基礎-三種網路模型(OSI七層模型 TPC/IP四層模型 五層模型)的關係計算機網路模型
- 無人車與宏觀交通:從微觀模型到宏觀模型模型
- 無人車與巨集觀交通:從微觀模型到巨集觀模型模型
- 高效能IO模型淺析模型