首先須要知道應用場景:
適用於靜態資源從磁盤到網絡的發送(中間不對資源進行改變),這在web server提供的功能中很常見,一個例子是:保存在磁盤上的一張圖片應某個網絡請求被從磁盤中取出並經過socket發送至請求方。linux
Linux系統分爲user space和kernel space,user space中的不少操做須要經過系統調用陷入到內核中操做,好比文件讀取操做。程序員經過某種編程語言提供的編程接口進行文件讀取操做,好比C中的read,這個C語言的實現是經過調用kernel space提供的系統調用read實現的。在從user space陷入到kernel space的過程當中,須要進行contex switch。同理,在從kernel space到user space的過程當中也須要進行contex switch。程序員
網絡端口發送數據的時候,有一塊socket buffer做爲要發送數據的緩存,而後驅動從其中讀取數據並組包發送。web
對於上述的一張圖片的請求和發送,從操做結果或者效果的角度看:我須要作的是將磁盤中的一部分數據直接(不加改變地)搬運到socket的buffer中進行組包再送到發送隊列中等待發送的。能夠想到:咱們從user space發起任務到回到user space,至少要經歷兩次contex switch,數據從磁盤到發送隊列,至少也要通過兩次copy(磁盤到socket buffer和socket buffer到發送隊列)。編程
Zero在個人理解中是指在上述過程當中不存在多餘copy的技術。緩存
怎麼會存在多餘的copy呢?多餘的copy有什麼壞處嗎?直接按照上述的過程進行不是就可以作到沒有多餘copy了嗎?
多餘的copy應該是軟硬件歷史的緣由,這在後面的zero-copy的發展過程當中能夠看到。多餘的copy會佔用CPU時間片和內存帶寬,形成系統性能的下降。Zero-copy就是想作到上述的過程,即最直觀的過程。須要說明的是,並非全部的數據的傳輸都適用zero-copy的,並且很大程度上不適用zero-copy的計算要比這種數據傳輸的應用要多得多,咱們必須意識到這中佔大多數應用的存在,畢竟使用計算機作的事情並不只限於存儲和傳輸。我的感受zero-copy是一種具體問題具體分析和精細化功能實現以換取性能的過程。網絡
咱們來看看歷史上對上述一張圖片的響應的實現方式的變化:socket
實現1:contex switch 4次,copy 4次編程語言
read(file, tmp_buf, len); write(socket, tmp_buf, len);
上面是系統調用,下面是內存copy。這種方式沒有什麼可說的,是沒有技巧性的中規中矩作法。函數
tmp_buf = mmap(file, len); write(socket, tmp_buf, len);
使用mmap在kernel space和user space之間構造了一塊共享內存空間,至關於user space和kernel space都是訪問統一塊物理內存(可是在user space和kernel space中分別被映射到各自的虛擬地址空間中)。這種方式由於這塊共享內存會產生問題:當另外一個進程操做同一個文件的時候,文件更改,kernel buffer中的內容至關於失效,此時,SIGBUS總線錯誤會終止以前的進程併產生core錯誤。解決的方法能夠是設置信號的處理函數,可是這種方式可能會屏蔽掉真正發生錯誤的狀況;或者是對操做的文件進行相似於加鎖的操做(讀寫鎖等)。或者若是真的想減小copy次數,只能讓操做系統內核和硬件進行支持了,這就是後面的實現。性能
sendfile(socket, file, len); //since kernel 2.1
新的系統調用被添加到linux kernel中以支持文件間的copy操做,主要是不用在沒必要要的時候bother user space。這種方式的好處是減小了contex switch的次數,可是實際上對於實現2中存在的另外一個進程修改使用的同一個文件的狀況並不能很好地解決。
sendfile(socket, file, len); //since kernel 2.4
在這種方式中,socket適應實現3的方式,而且得到了硬件的相關的支持,在實現3中的第2步的copy在這種方式中只是copy了必要的描述信息:文件描述符和長度。相比於要copy的數據來講,這大大減小了copy須要的時間。