服務(wù)器將硬盤上的文件坎穿,傳給用戶屡江,需要幾次拷貝巢钓?
解決這個問題,需要明白具體的工作流程是什么睬辐?
read(file, buf, len); // 把文件讀到緩沖區(qū)buf中
write(socket, buf, len); // 把buf中的內(nèi)容發(fā)送給用戶
關(guān)于哪個步驟需要拷貝:
- 把磁盤中文件拷貝到kernel buf
- 把kernel buf拷貝到user buf
- 把user buf拷貝到socket 中的kernel buf
- 把socket buffer 拷貝到 網(wǎng)卡設(shè)備的buffer
其實1,2是read挠阁,3,4是write
所以說,一共拷貝4次溯饵,而且kernel mod和user mod的切換也是4次侵俗。
Linux 2.1內(nèi)核開始引入了sendfile函數(shù)。省去了將操作系統(tǒng)的read buffer拷貝到程序的buffer丰刊,以及從程序buffer拷貝到socket buffer的步驟
而是直接將kernel buf 拷貝 到 socket buf隘谣。 實際上是把2,3兩部步驟整合了。
拷貝次數(shù)
上下文切換
這里把上下文的切換次數(shù)從4次減少到2次啄巧,同時也把數(shù)據(jù)copy的次數(shù)從4次降低到了3次寻歧。
Java NIO中的FileChannal.transferTo()方法就是這樣的實現(xiàn),這個實現(xiàn)是依賴于操作系統(tǒng)底層的sendFile()實現(xiàn)的
但是秩仆,這還不是Zero-Copy码泛。
- 將文件拷貝到kernel buffer中
- 向socket buf中寫入當(dāng)前要拷貝數(shù)據(jù)的位置和偏移量
-
根據(jù)socket buf中的位置和偏移量,直接將kernel buf拷貝到網(wǎng)卡buffer中逗概。
這樣只需要拷貝兩次弟晚。這里的零拷貝是針對kernel來講的,數(shù)據(jù)在kernel模式下是Zero-Copy逾苫。
在kafka和netty中會使用到Zero-Copy卿城,大大提升了性能。