簡介
在學(xué)習(xí)kafka如何實(shí)現(xiàn)高吞吐量的時(shí)候遇到一個(gè)技術(shù)zero copy荆烈,追隨kafka文檔中的鏈接深入了解了一下zero copy,在這里mark一下裸燎,原文鏈接https://www.ibm.com/developerworks/linux/library/j-zerocopy/
通常的實(shí)現(xiàn)方式
假設(shè)這樣一個(gè)場景:從文件中讀取數(shù)據(jù)然后通過網(wǎng)絡(luò)將數(shù)據(jù)發(fā)送出去顾瞻,實(shí)現(xiàn)的核心部分一般是這樣的
File.read(fileDesc, buf, len);
Socket.send(socket, buf, len);
盡管實(shí)現(xiàn)非常簡單,需要4次用戶態(tài)和內(nèi)核態(tài)的上線文切換德绿,數(shù)據(jù)需要拷貝4次荷荤,整個(gè)流程如下圖
- read()從用戶態(tài)切換到內(nèi)核態(tài),調(diào)用sys_read()從文件中讀取數(shù)據(jù)移稳,數(shù)據(jù)從disk拷貝到內(nèi)核地址空間
- 數(shù)據(jù)從內(nèi)核地址空間拷貝到用戶地址空間蕴纳,read函數(shù)返回
- send函數(shù)從用戶態(tài)切換到內(nèi)核態(tài),將數(shù)據(jù)從用戶地址空間拷貝到內(nèi)核地址空間
- 數(shù)據(jù)從內(nèi)核地址空間拷貝到協(xié)議引擎个粱,send函數(shù)返回
使用內(nèi)核對(duì)數(shù)據(jù)緩存相比于用戶直接讀取數(shù)據(jù)好像是低效的古毛,在某些情況下內(nèi)核態(tài)的數(shù)據(jù)緩存確實(shí)會(huì)提高效率,但是當(dāng)數(shù)據(jù)大小超過內(nèi)核緩存的大小時(shí)都许,這個(gè)操作是低效的稻薇。
zero copy
其實(shí)對(duì)于這種場景下,數(shù)據(jù)沒有必要從內(nèi)核態(tài)拷貝到用戶態(tài)胶征,可以直接從read buffer拷貝到socket buffer塞椎,transferTo()函數(shù)就是實(shí)現(xiàn)這樣的操作,但是transferTo函數(shù)需要操作系統(tǒng)的支持睛低,在linux下sendfile實(shí)現(xiàn)了這樣的操作案狠,剛才的實(shí)現(xiàn)可以改成下面的:
transferTo(position, count, writableChannel);
這樣數(shù)據(jù)就不需要從內(nèi)核空間拷貝到用戶空間了
如果內(nèi)核支持gather operations的話還可以進(jìn)一步優(yōu)化蹬敲,在用戶實(shí)現(xiàn)層面沒有發(fā)生變化,優(yōu)化都是在內(nèi)核實(shí)現(xiàn)中的莺戒,首先數(shù)據(jù)從disk拷貝到內(nèi)核buffer伴嗡,然后并不需要把數(shù)據(jù)再次拷貝到socket buffer,只需要把數(shù)據(jù)位置和長度告訴socket buffer即可从铲,優(yōu)化之后的數(shù)據(jù)流如下: