斟酌這樣1種經(jīng)常使用的情形:你需要將靜態(tài)內(nèi)容(類似圖片、文件)展現(xiàn)給用戶。那末這個(gè)情形就意味著你需要先將靜態(tài)內(nèi)容從磁盤中拷貝出來放到1個(gè)內(nèi)存buf中,然后將這個(gè)buf通過socket傳輸給用戶,進(jìn)而用戶或靜態(tài)內(nèi)容的展現(xiàn)。這看起來再正常不過了,但是實(shí)際上這是很低效的流程,我們把上面的這類情形抽象成下面的進(jìn)程:
read(file, tmp_buf, len);
write(socket, tmp_buf, len);
首先調(diào)用read將靜態(tài)內(nèi)容,這里假定為文件A,讀取到tmp_buf, 然后調(diào)用write將tmp_buf寫入到socket中,如圖:
在這個(gè)進(jìn)程中文件A的經(jīng)歷了4次copy的進(jìn)程:
從上面的進(jìn)程可以看出,數(shù)據(jù)白白從kernel模式到user模式走了1圈,浪費(fèi)了2次copy(第1次,從kernel模式拷貝到user模式;第2次從user模式再拷貝回kernel模式,即上面4次進(jìn)程的第2和3步驟。)。而且上面的進(jìn)程中kernel和user模式的上下文的切換也是4次。
榮幸的是,你可以用1種叫做Zero-Copy的技術(shù)來去掉這些無謂的copy。利用程序用Zero-Copy來要求kernel直接把disk的data傳輸給socket,而不是通過利用程序傳輸。Zero-Copy大大提高了利用程序的性能,并且減少了kernel和user模式上下文的切換。
Zero-Copy技術(shù)省去了將操作系統(tǒng)的read buffer拷貝到程序的buffer,和從程序buffer拷貝到socket buffer的步驟,直接將read buffer拷貝到socket buffer. Java NIO中的FileChannal.transferTo()方法就是這樣的實(shí)現(xiàn),這個(gè)實(shí)現(xiàn)是依賴于操作系統(tǒng)底層的sendFile()實(shí)現(xiàn)的。
public void transferTo(long position, long count, WritableByteChannel target);
他底層的調(diào)用時(shí)系統(tǒng)調(diào)用sendFile()方法:
#include <sys/socket.h>
ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
下圖展現(xiàn)了在transferTo()以后的數(shù)據(jù)流向:
下圖展現(xiàn)了在使用transferTo()以后的上下文切換:
使用了Zero-Copy技術(shù)以后,全部進(jìn)程以下:
但是這是Zero-Copy么,答案是不是定的。
Linux 2.1內(nèi)核開始引入了sendfile函數(shù)(上1節(jié)有提到),用于將文件通過socket傳送。
sendfile(socket, file, len);
該函數(shù)通過1次系統(tǒng)調(diào)用完成了文件的傳送,減少了原來read/write方式的模式切換。另外更是減少了數(shù)據(jù)的copy, sendfile的詳細(xì)進(jìn)程如圖:
通過sendfile傳送文件只需要1次系統(tǒng)調(diào)用,當(dāng)調(diào)用sendfile時(shí):
這個(gè)進(jìn)程就是第2節(jié)(詳述)中的那個(gè)步驟。
sendfiel與read/write模式相比,少了1次copy。但是從上述進(jìn)程中也能夠發(fā)現(xiàn)從kernel buffer中將數(shù)據(jù)copy到socket buffer是沒有必要的。
Linux2.4 內(nèi)核對(duì)sendfile做了改進(jìn),如圖:
改進(jìn)后的處理進(jìn)程以下:
經(jīng)過上述進(jìn)程,數(shù)據(jù)只經(jīng)過了2次copy就從磁盤傳送出去了。
這個(gè)才是真實(shí)的Zero-Copy(這里的零拷貝是針對(duì)kernel來說的,數(shù)據(jù)在kernel模式下是Zero-Copy)。
正是Linux2.4的內(nèi)核做了改進(jìn),Java中的TransferTo()實(shí)現(xiàn)了Zero-Copy,以下圖:
Zero-Copy技術(shù)的使用處景有很多,比如Kafka, 又或是Netty等,可以大大提升程序的性能。