一文講解LinuxTCP數(shù)據(jù)接收-快路徑與慢路徑
一、快路徑與慢路徑簡(jiǎn)介
在Linux內(nèi)核的TCP/IP協(xié)議棧實(shí)現(xiàn)中,TCP數(shù)據(jù)接收分為快路徑處理與慢路徑進(jìn)行處理,快路徑用于處理預(yù)期的、理想情形的輸入數(shù)段,TCP連接中最常見的情形應(yīng)該被盡可能地檢測(cè)并最優(yōu)化處理,達(dá)到快速處理的目的。慢路徑用于處理那些非預(yù)期、非理想情況下的數(shù)據(jù)段,如亂序數(shù)據(jù)段、socket內(nèi)存管理和緊急數(shù)據(jù)等。
快路徑與慢路徑在Linux內(nèi)核中的處理流程:tcp握手完成后,收到數(shù)據(jù)包后,調(diào)用路徑為:
tcp_v4_rcv
->tcp_v4_do_rcv
->tcp_rcv_established,
在tcp_rcv_establisheed函數(shù)中處理TCP_ESTABLISHED狀態(tài)的包 ,并根據(jù)pred_flags預(yù)測(cè)字段來(lái)選擇著采用快路徑或慢路徑。
二、首部預(yù)測(cè)字段-pred_flags
預(yù)測(cè)字段存儲(chǔ)在struct tcp_sock中,pred_flag為0表示關(guān)閉首部預(yù)測(cè)使用慢速路徑,非0表示開啟快速路徑的前提,如果開啟會(huì)對(duì)該變量進(jìn)行設(shè)定。
可以看到pred_flags與網(wǎng)絡(luò)傳輸時(shí)一致采用大端存儲(chǔ),其大小為32位。該32位的pred_flags和TCP首部的第3個(gè)32位字(第0個(gè)32位:16位源端口號(hào),16位目的端口號(hào);第1個(gè)32位:32位序列號(hào);第2個(gè)32位:32位確認(rèn)號(hào);第3個(gè)32位:首部字段、標(biāo)志、窗口大小等,即首部預(yù)測(cè)字段需要的字段)對(duì)應(yīng),**如下圖1為TCP首部字段分布與第3個(gè)32位的細(xì)節(jié),**pred_flag變量的賦值通過(guò)調(diào)用tcp_fast_path_on函數(shù)或tcp_fast_path_on函數(shù)(include\net\tcp.h)中進(jìn)行設(shè)定,其中tcp_fast_path_on間接調(diào)用__tcp_fast_path_on,只不過(guò)是在調(diào)用__tcp_fast_path_on針對(duì)攜帶窗口擴(kuò)大因子的TCP傳輸進(jìn)行還原發(fā)送窗口的大小。
**pred_flags由三個(gè)部分組成:【首部長(zhǎng)度、ACK標(biāo)記、發(fā)送窗口大小】也就是與TCP首部字段(13-16字節(jié))中相應(yīng)的字段,******如圖2 pred_flags圖示,****內(nèi)核中的解釋如下:
進(jìn)行快速路徑判斷的時(shí)候只需要將預(yù)測(cè)字段與TCP首部中對(duì)應(yīng)的部分進(jìn)行對(duì)比即可。
__tcp_fast_path_on函數(shù)中:tp->header_len<<26的解釋如下:
1、關(guān)于header_len:是指TCP首部的字節(jié)數(shù)(TCP首部固定部分為20字節(jié)),在TCP頭部對(duì)應(yīng)的是4位的"首部長(zhǎng)度"字段,TCP的首部字節(jié)數(shù) header_len= 首部長(zhǎng)度*4,要得到”首部長(zhǎng)度“字段,需要將header_len右移兩位,即除以4:header_len>>2
2、”首部長(zhǎng)度“字段是4位,現(xiàn)在要得到的pred_flags應(yīng)該與TCP首部的第4個(gè)32位的位置(第13-16字節(jié))進(jìn)行對(duì)應(yīng),所以4位的首部字段左移28位得到:32位初始化(僅包含首部長(zhǎng)度字段)的pred_flags,即header_len右移兩位后,再左移28位:header_len<<26,即XXXX0000000000000000000000000000
其中XXXX表示首部長(zhǎng)度字段的值。
tp->header_len<<26得到了含首部長(zhǎng)度字段的pred_flags,但pred_flags除了首部長(zhǎng)度字段外還應(yīng)含有ACK標(biāo)記,發(fā)送窗口:
tp->tcp_header_len << 26) | ntohl(TCP_FLAG_ACK) | snd_wnd
其中TCP_FLAG_ACK定義如下:
【文章福利】小編推薦自己的Linux內(nèi)核技術(shù)交流群:【891587639】整理了一些個(gè)人覺(jué)得比較好的學(xué)習(xí)書籍、視頻資料共享在群文件里面,有需要的可以自行添加哦?。。。ê曨l教程、電子書、實(shí)戰(zhàn)項(xiàng)目及代碼)? ? ??


TCP_FLAG_ACK字段為0x00100000,即對(duì)應(yīng)ACK標(biāo)志位為1。


三、首部預(yù)測(cè)字段的設(shè)定
首部預(yù)測(cè)字段的設(shè)定分為兩種過(guò)程 :
1、直接調(diào)用__tcp_fast_path_on函數(shù)進(jìn)行設(shè)定首部預(yù)測(cè)字段相應(yīng)的值
2、直接調(diào)用tcp_fast_path_on函數(shù)進(jìn)行設(shè)定首部預(yù)測(cè)字段相應(yīng)的值
2、先進(jìn)行條件檢驗(yàn),檢驗(yàn)通過(guò)后調(diào)用__tcp_fast_path_on函數(shù)進(jìn)行設(shè)定首部預(yù)測(cè)字段相應(yīng)的值直接調(diào)用__tcp_fast_path_on 的時(shí)機(jī):
客戶端connect系統(tǒng)調(diào)用即將結(jié)束的時(shí)
在tcp_finish_connect中沒(méi)有開啟窗口擴(kuò)大因子的時(shí),調(diào)用__tcp_fast_path_on來(lái)設(shè)置快速路徑條件,這時(shí)候客戶端進(jìn)入TCP_ESTABLISHED狀態(tài),服務(wù)端還在等待客戶端最后一次ACK才能發(fā)送數(shù)據(jù),因此不會(huì)收到服務(wù)端的數(shù)據(jù),也就不用考慮快速路徑。
直接調(diào)用tcp_fast_path_on的時(shí)機(jī):
服務(wù)器在收到SYN請(qǐng)求后的處理過(guò)程中,如下tcp_rcv_state_process函數(shù)中
進(jìn)行檢驗(yàn)后調(diào)用__tcp_fast_path_on函數(shù):
檢驗(yàn)函數(shù)檢查條件是否滿足,滿足后才能設(shè)置預(yù)測(cè)標(biāo)記,條件:** **
條件1:亂序隊(duì)列是否為空(沒(méi)有亂序數(shù)據(jù)時(shí)設(shè)定)
條件2:接收窗口是否還有剩余空間(接收窗口不為0時(shí)設(shè)定)
條件3:接收內(nèi)存是否受限(接收緩存未耗盡時(shí)設(shè)定)
條件4:是否有緊急數(shù)據(jù)需要傳輸(無(wú)緊急數(shù)據(jù)時(shí)設(shè)定)
檢驗(yàn)函數(shù)是tcp_fast_path_check,邏輯主要是進(jìn)行4個(gè)條件判斷,決定是否設(shè)定首部預(yù)測(cè)字段,如下所示:
先通過(guò)tcp_fast_check函數(shù)檢測(cè)后調(diào)用t cp_fast_path_on的時(shí)機(jī):
1、讀完緊急數(shù)據(jù)后:緊急數(shù)據(jù)是由慢路徑處理的,在慢速路徑上收完緊急數(shù)據(jù)后檢查是否可用開啟快速路徑模式
2、當(dāng)發(fā)送發(fā)收到ACK并調(diào)用tcp_ack_update_window更新窗口時(shí),通告窗口發(fā)生了變化,則必須更新預(yù)測(cè)標(biāo)記,以免后續(xù)的輸入報(bào)文因?yàn)榇翱诓环M(jìn)入慢速路徑:
3、當(dāng)調(diào)用tcp_data_queue將數(shù)據(jù)放入接收隊(duì)列時(shí),這時(shí)可用的接收緩存大小發(fā)生變化,即將輸入數(shù)據(jù)放入到接收隊(duì)列后,更新了字節(jié)的內(nèi)存占用量,tcp_fast_path_check會(huì)檢查這俄格緩存的變化是否允許開啟快速路徑模式。只有當(dāng)前包是非亂序包,且接收窗口非0的時(shí)候,才能調(diào)用tcp_fast_path_check嘗試開啟快速路徑
四、進(jìn)入快速路徑與慢速路徑處理
設(shè)置預(yù)測(cè)標(biāo)記后,使用它是在處理已連接TCP數(shù)據(jù)段的唯一入口函數(shù):tcp_rcv_estblished
是否能夠執(zhí)行快速路徑,pred_flags匹配只是前提條件,還有一些其他的判斷條件,在內(nèi)核中有相關(guān)的定義(net\ipv4\tcp_input.c):
在tcp_rcv_established函數(shù)中,關(guān)于快速路徑檢查與執(zhí)行部分如下:
其中:
tcp_flag_word(th)獲取的是TCP首部的第13-16字節(jié),也就是第3個(gè)32位(第0個(gè)32位:16位源端口號(hào),16位目的端口號(hào);第1個(gè)32位:32位序列號(hào);第2個(gè)32位:32位確認(rèn)號(hào);第3個(gè)32位:首部字段、標(biāo)志、窗口大小等,需要的首部預(yù)測(cè)字段)
得到TCP首部的第3個(gè)32位后,還不是首部預(yù)測(cè)字段,還要繼續(xù)屏蔽掉PSH字段,即 tcp_flag_word(th) & TCP_HP_BITS
慢速路徑處理:
