文章目錄
- 檔案傳輸(讀取與發送)中的拷貝與背景關系切換
- 零拷貝技術
- sendfile
- sendfile + SG-DMA
- mmap + write
- splice
- Direct I/O
- 經典應用
檔案傳輸(讀取與發送)中的拷貝與背景關系切換
如果服務端要提供檔案傳輸的功能,最簡單的方式是:
1、將磁盤上的檔案讀取出來
2、通過網路協議將內容發送給客戶端
傳統IO的作業方式是,資料讀取和寫入從用戶空間到內核空間來回賦值,內核空間資料通過IO介面從磁盤讀取/寫入,
就如同下面這兩個api的使用:
File.read(file, buf, len);
Socket.send(socket, buf, len);
這個場景下會發生4次資料拷貝+4次背景關系切換:
read系統呼叫,從用戶態到內核態 切換 ,CPU從磁盤 拷貝 資料到內核pagecache,
read回傳,從內核態 切換 到用戶態,CPU從pagecache 拷貝 資料到用戶緩沖區,
send,可以看作write,
write系統呼叫,從用戶態到內核態切換,CPU從用戶緩沖區拷貝資料到內核socket緩沖區
然后CPU從內核socket緩沖區拷貝資料到網卡上
最后write回傳,從內核態 切換 到用戶態,
當然可以使用DMA技術,替代CPU在IO外設與內核緩沖區之間的拷貝,因為DMA僅僅只能用于設備之間交換資料時的資料拷貝,記憶體之間的資料拷貝用不了DMA,
這樣優化下來會發生2次CPU資料拷貝+2次DMA資料拷貝+4次背景關系切換,接下來的講解都是基于這個成本來的,
想要提高性能就需要減少背景關系切換和CPU拷貝的次數,
零拷貝技術
零拷貝是一種高效的資料傳輸機制,在追求低延遲的傳輸場景中經常使用,具體思想是計算機執行操作時,CPU不需要將資料從某處記憶體復制到另外一個特定區域,
現存的比較常用的零拷貝方法有下面幾個:
- sendfile
- mmap + write
- splice
- Direct I/O
不同的技術使用的場景也是不同的,使用時請結合業務邏輯,
sendfile
應用場景:用戶從磁盤讀取檔案資料后不需要經過CPU計算/處理就直接通過網路傳輸出去
典型應用:MQ
Linux版本:2.1
ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
// out_fd:目的端檔案描述符
// in_fd:源端檔案描述符
// offset:源端偏移量
// count:資料長度
// 回傳值:實際復制資料的長度
我們只需要傳遞檔案描述符就可以代替資料的拷貝了,直接替代read+write操作,sendfile一次系統呼叫就相當于之前的兩次系統呼叫,這是因為page cache和socket buffer均在內核空間,sendfile直接把內核緩沖區資料拷貝到socket緩沖區上了,直接省略掉用戶態,
成本:1次系統呼叫,2次背景關系切換,1次CPU資料拷貝,2次DMA資料拷貝
sendfile + SG-DMA
Linux版本:2.4
如果網卡支持SG-DMA(The Scatter-Gather Direct Memory Access)技術,可以直接將內核態緩沖區資料直接SG-DMA到網卡上,省略了內核態緩沖區->socket緩沖區->網卡的步驟,
成本:1次系統呼叫,2次背景關系切換,1次DMA資料拷貝,1次SG-DMA資料拷貝
這就是真正的zero-copy,完全沒有通過記憶體層面去拷貝資料,全程使用DMA傳輸,
局限性:當然sendfile也是有局限性的,它直接隔離了應用程式對資料操作,如果需要從資料中提取統計資訊或者進行加解密,sendfile根本使用不了,
mmap + write
mmap:memory map,一種記憶體映射檔案的方法,即將一個檔案或者其他物件映射到行程的地址空間,實作檔案磁盤地址和行程虛擬地址空間中一段虛擬地址直接對映,這樣行程就可以采用指標的方式直接讀寫操作這一塊記憶體,系統自動回寫臟頁到對應的檔案磁盤上,這樣對檔案操作就不需要呼叫read+write了,并且內核空間對這段區域的修改也直接反映在了用戶空間,從而實作不同行程間的檔案共享,

mmap技術特點如下:
1、用戶空間的mmap file使用虛擬記憶體,實際上不占有物理記憶體,只有內核空間的kernel buffer cache才占據實際物理記憶體
2、mmap需要配合write
3、mmap僅僅避免內核空間到用戶空間的CPU資料包被,但是內核空間內部還是需要CPU負責資料拷貝
使用mmap流程如下:
1、用戶呼叫mmap,從用戶態切換到內核態,將內核緩沖區映射到用戶快取區
2、DMA控制器將資料從磁盤拷貝到內核緩沖區
3、mmap回傳,從內核態切換到用戶態
4、用戶行程呼叫write,嘗試把檔案資料寫到內核socket buffer中,從用戶態切換到內核態
5、CPU將內核緩沖區資料拷貝到socket buffer
6、DMA控制器將資料從socket buffer拷貝到網卡
7、write回傳
成本:2次系統呼叫、4次背景關系切換、1次CPU資料拷貝、2次DMA資料拷貝
應用場景
1、多個執行緒以只讀方式同時訪問一個檔案,mmap機制下的多執行緒共享同一個物理記憶體空間,節約了記憶體,
例子:多個行程可以依賴于同一個元件,利用mmap可以實作記憶體僅僅加載一份元件,多個行程共享此庫
2、mmap可用于行程間通信,對于同一個檔案對應的mmap分配的物理記憶體天然多執行緒共享,可以依賴于作業系統的同步原語
3、mmap比sendfile多了一次CPU參與的記憶體拷貝,但是用戶空間與內核空間之間不需要資料拷貝,所以效率也很高
splice
Linux版本:2.6.17
#include <fcntl.h>
ssize_t splice(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags);
splice用于在兩個檔案描述符之間移動資料, 也是零拷貝,
fd_in引數是待輸入描述符,如果它是一個管道檔案描述符,則off_in必須設定為NULL;否則off_in表示從輸入資料流的何處開始讀取,此時若為NULL,則從輸入資料流的當前偏移位置讀入,
fd_out/off_out與上述相同,不過是用于輸出,
len引數指定移動資料的長度,
flags引數則控制資料如何移動:
SPLICE_F_NONBLOCK:splice 操作不會被阻塞,然而,如果檔案描述符沒有被設定為不可被阻塞方式的 I/O ,那么呼叫 splice 有可能仍然被阻塞,
SPLICE_F_MORE:告知作業系統內核下一個 splice 系統呼叫將會有更多的資料傳來,
SPLICE_F_MOVE:如果輸出是檔案,這個值則會使得作業系統內核嘗試從輸入管道緩沖區直接將資料讀入到輸出地址空間,這個資料傳輸程序沒有任何資料拷貝操作發生,
2. 使用splice時, fd_in和fd_out中必須至少有一個是管道檔案描述符,
呼叫成功時回傳移動的位元組數量;它可能回傳0,表示沒有資料需要移動,這通常發生在從管道中讀資料時而該管道沒有被寫入的時候,
失敗時回傳-1,并設定errno
splice系統呼叫直接在內核空間的read buffer 和socket buffer之間建立了管道,避免了用戶緩沖區和socket buffer之間的CPU拷貝
成本:1次splice系統呼叫、1次pipe呼叫、2次背景關系切換、2次DMA資料拷貝
局限性:
1、用戶程式不能對資料進行操作,與sendfile類似
2、Linux管道緩沖機制,可以用于任意兩個檔案描述符中傳輸資料,但是其中一個必須是管道設備
Direct I/O
快取檔案I/O:用戶空間要讀取一個檔案并不是直接與磁盤進行互動看,而是中間夾了一層快取,即page cache
直接檔案I/O:用戶空間讀取檔案直接與磁盤互動,資料直接存盤在用戶空間中,沒有中間page cache曾,繞過了內核,
部分作業系統中,在直接檔案I/O模式下,write雖然能夠保證檔案資料落盤,但是檔案元資料不一定落盤,所以還需要執行一次fsync操作,
局限性:
1、設備之間資料傳輸通過DMA,所以用戶空間的資料緩沖區記憶體頁必須進行頁鎖定,這是為了防止其物理頁地址被交換到磁盤或者被移動到新的地址導致DMA去拷貝資料時在指定地址找不到記憶體頁從而引發缺頁例外,而頁鎖定的開銷也不小,所以應用程式必須分配和注冊一個持久的記憶體池,用戶資料緩沖,(應用程式手動做快取池)
2、如果在應用程式的快取中沒有找到,那么就直接從磁盤加載,十分緩慢
3、應用層引入快取管理以及底層硬體管理(頁鎖定),很麻煩
經典應用
在之前的筆記中有談到kafka高性能的原因之一就是使用了zero-copy:訊息佇列重要機制講解以及MQ設計思路(kafka、rabbitmq、rocketmq,這里稍微拓展一下:
生產者發訊息給kafka,kafka將訊息持久化落盤,
消費者從kafka拉取訊息,kafka從磁盤讀取一批資料,通過網卡發送,
接收訊息持久化的時候使用到了mmap機制,對接收的資料持久化,發送訊息的時候使用sendfile從持久化介質中讀取資料然后對外發送,
sendfile避免了內核空間到用戶空間的CPU資料拷貝,同時sendfile基于page cache實作,如果有多個消費者同時消費一個topic訊息,訊息會在page cache上快取,就只需要一次磁盤IO了,
所以我們應該熟悉掌握sendfile 和 mmap
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/433393.html
標籤:其他
