什么是Zero-Copy?
歡迎跳轉到本文的原文鏈接:https://honeypps.com/backend/what-is-zero-copy/
概述
考慮這樣一種常用的情形:你需要將靜態內容(類似圖片、文件)展示給用戶。那么這個情形就意味著你需要先將靜態內容從磁盤中拷貝出來放到一個內存buf中,然后將這個buf通過socket傳輸給用戶,進而用戶或者靜態內容的展示。這看起來再正常不過了,但是實際上這是很低效的流程,我們把上面的這種情形抽象成下面的過程:
read(file, tmp_buf, len); write(socket, tmp_buf, len);首先調用read將靜態內容,這里假設為文件A,讀取到tmp_buf, 然后調用write將tmp_buf寫入到socket中,如圖:
在這個過程中文件A的經歷了4次copy的過程:
從上面的過程可以看出,數據白白從kernel模式到user模式走了一圈,浪費了2次copy(第一次,從kernel模式拷貝到user模式;第二次從user模式再拷貝回kernel模式,即上面4次過程的第2和3步驟。)。而且上面的過程中kernel和user模式的上下文的切換也是4次。
幸運的是,你可以用一種叫做Zero-Copy的技術來去掉這些無謂的copy。應用程序用Zero-Copy來請求kernel直接把disk的data傳輸給socket,而不是通過應用程序傳輸。Zero-Copy大大提高了應用程序的性能,并且減少了kernel和user模式上下文的切換。
詳述
Zero-Copy技術省去了將操作系統的read buffer拷貝到程序的buffer,以及從程序buffer拷貝到socket buffer的步驟,直接將read buffer拷貝到socket buffer. Java NIO中的FileChannal.transferTo()方法就是這樣的實現,這個實現是依賴于操作系統底層的sendFile()實現的。
public void transferTo(long position, long count, WritableByteChannel target);他底層的調用時系統調用**sendFile()**方法:
#include <sys/socket.h> ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);下圖展示了在transferTo()之后的數據流向:
下圖展示了在使用transferTo()之后的上下文切換:
使用了Zero-Copy技術之后,整個過程如下:
這顯然是一個偉大的進步:這里把上下文的切換次數從4次減少到2次,同時也把數據copy的次數從4次降低到了3次。
但是這是Zero-Copy么,答案是否定的。
進階
Linux 2.1內核開始引入了sendfile函數(上一節有提到),用于將文件通過socket傳送。
sendfile(socket, file, len);該函數通過一次系統調用完成了文件的傳送,減少了原來read/write方式的模式切換。此外更是減少了數據的copy, sendfile的詳細過程如圖:
通過sendfile傳送文件只需要一次系統調用,當調用sendfile時:
這個過程就是第二節(詳述)中的那個步驟。
sendfiel與read/write模式相比,少了一次copy。但是從上述過程中也可以發現從kernel buffer中將數據copy到socket buffer是沒有必要的。
Linux2.4 內核對sendfile做了改進,如圖:
改進后的處理過程如下:
經過上述過程,數據只經過了2次copy就從磁盤傳送出去了。
這個才是真正的Zero-Copy(這里的零拷貝是針對kernel來講的,數據在kernel模式下是Zero-Copy)。
正是Linux2.4的內核做了改進,Java中的TransferTo()實現了Zero-Copy,如下圖:
Zero-Copy技術的使用場景有很多,比如Kafka, 又或者是Netty等,可以大大提升程序的性能。
參考資料
歡迎跳轉到本文的原文鏈接:https://honeypps.com/backend/what-is-zero-copy/
歡迎支持筆者新作:《深入理解Kafka:核心設計與實踐原理》和《RabbitMQ實戰指南》,同時歡迎關注筆者的微信公眾號:朱小廝的博客。
總結
以上是生活随笔為你收集整理的什么是Zero-Copy?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MySQL主备复制原理、实现及异常处理
- 下一篇: 数据库相关中间件收录集