Linux內(nèi)核網(wǎng)絡(luò)-擁塞控制系列(一)
談起網(wǎng)絡(luò)擁塞控制,大家可能很熟悉八股文中的"加法增大“、”乘法減小“、”慢開始“、“擁塞避免”、“快重傳”、“快恢復(fù)”等概念。沒錯(cuò),這是一種經(jīng)典網(wǎng)絡(luò)擁塞控制算法的基礎(chǔ)理論,但在實(shí)際的實(shí)現(xiàn)時(shí)不同的擁塞控制算法,有很大差別。本文從Linux內(nèi)核源碼中學(xué)習(xí)網(wǎng)絡(luò)擁塞控制算法的具體實(shí)現(xiàn)框架。從當(dāng)前網(wǎng)絡(luò)擁塞控制算法的發(fā)展歷程上看,網(wǎng)絡(luò)擁塞控制算法的類型主要有以下四種:
基于丟包的擁塞控制算法,這類算法將丟包視為發(fā)生了網(wǎng)絡(luò)擁塞。采取緩慢的探測(cè)方式,逐漸增大擁塞窗口,當(dāng)出現(xiàn)丟包時(shí),將擁塞窗口減少,代表的算法有Tahoe、Reno、NewReno、BIC、Cubic等。
基于延時(shí)的擁塞控制算法,這類算法將延時(shí)增大視為發(fā)生了網(wǎng)絡(luò)擁塞,延時(shí)增大時(shí)減少擁塞窗口,延時(shí)減少時(shí)增大擁塞窗口,代表的算法有Vegas、Westwood等。
基于鏈路容量的擁塞控制算法,代表算法是BBR,其采用了另類的方式,不再使用丟包、延時(shí)等信號(hào)去衡量擁塞是否發(fā)生,而是直接對(duì)網(wǎng)絡(luò)建模來避免以及應(yīng)對(duì)真實(shí)的網(wǎng)絡(luò)擁塞。
基于學(xué)習(xí)的擁塞控制算法,這類算法也沒有特定的擁塞信號(hào),一般是基于訓(xùn)練數(shù)據(jù)、評(píng)價(jià)函數(shù),通過機(jī)器學(xué)習(xí)生成網(wǎng)絡(luò)擁塞控制策略模型,代表算法有Remy、PCC、Aurora、DRL-CC、Orca等。
由于每類擁塞控制算法的核心理念有很大差別,關(guān)于每種算法的實(shí)現(xiàn)與原理在后續(xù)的文章中進(jìn)行呈現(xiàn)。本次文章先對(duì)Linux內(nèi)核中網(wǎng)絡(luò)擁塞控制實(shí)現(xiàn)細(xì)節(jié)、大致框架,進(jìn)行分析和大概學(xué)習(xí)。在進(jìn)行正式的分析前先簡單梳理一下常識(shí)與概念:
什么是網(wǎng)絡(luò)擁塞:網(wǎng)絡(luò)擁塞是指在網(wǎng)絡(luò)中傳輸?shù)臄?shù)據(jù)量超過網(wǎng)絡(luò)鏈路或節(jié)點(diǎn)的處理能力,導(dǎo)致網(wǎng)絡(luò)延遲增加、丟包率升高和帶寬利用率下降的現(xiàn)象。
窗口(Window):如下圖的TCP協(xié)議頭中占據(jù)16位,用于接收端告訴發(fā)送端還有多少緩沖區(qū)可以接收數(shù)據(jù)。

滑動(dòng)窗口、發(fā)送窗口:下圖所示黑色方框代表發(fā)送窗口?;瑒?dòng)窗口只是一種形象的稱呼,即發(fā)送窗口一直移動(dòng)從而達(dá)到發(fā)送新的數(shù)據(jù)的目的,如下圖當(dāng)接收到接收端發(fā)來的ACK數(shù)據(jù)包后發(fā)送窗口向右移動(dòng)。圖中灰色的方框代表已經(jīng)發(fā)送且確認(rèn)的數(shù)據(jù),紅色代表已發(fā)送且剛剛確認(rèn)的數(shù)據(jù),正是因?yàn)閯倓偞_認(rèn)了5byte的數(shù)據(jù),才驅(qū)動(dòng)發(fā)送窗口可以向右移動(dòng)5個(gè)單位,使得序號(hào)52~56的數(shù)據(jù)(綠色方框,代表允許發(fā)送的待發(fā)送數(shù)據(jù))可以發(fā)送,當(dāng)37~51區(qū)間的數(shù)據(jù)(藍(lán)色方框,代表發(fā)送但未確認(rèn)的數(shù)據(jù)包)能夠被確認(rèn)時(shí),發(fā)送窗口才能向右滑動(dòng)。發(fā)送窗口前方的數(shù)據(jù)(黃色方框,不允許發(fā)送的待發(fā)送數(shù)據(jù))只能等待發(fā)送窗窗口區(qū)間內(nèi)才能發(fā)送。TCP的滑動(dòng)窗口是動(dòng)態(tài)的,我們可以想象成小學(xué)常見的一個(gè)數(shù)學(xué)題,一個(gè)水池,體積V,每小時(shí)進(jìn)水量V1,出水量V2。當(dāng)水池滿了就不允許再注入了,如果有個(gè)液壓系統(tǒng)控制水池大小,那么就可以控制水的注入速率和量。這樣的水池就類似TCP的窗口。應(yīng)用根據(jù)自身的處理能力變化,通過本端TCP接收窗口大小控制來對(duì)對(duì)對(duì)端的發(fā)送窗口流量限制。

擁塞窗口:上面介紹了發(fā)送窗口的概念,在TCP協(xié)議中有一個(gè)反映網(wǎng)絡(luò)傳輸能力的變量,叫做擁塞窗口(congestion window),記作cwnd。發(fā)送端實(shí)際的發(fā)送窗口大小實(shí)際是為 接收端通告窗口 rwnd 與 擁塞窗口 cwnd 較小的那個(gè)值。
從上面的概念中可以得知,擁塞窗口可以間接反映網(wǎng)絡(luò)的狀況,進(jìn)而去限制發(fā)送窗口的大小。擁塞窗口作為網(wǎng)絡(luò)擁塞控制中核心變量之一,對(duì)網(wǎng)絡(luò)擁塞控制起到關(guān)鍵作用。在Linux內(nèi)核中,關(guān)于網(wǎng)絡(luò)的核心結(jié)構(gòu)體在:Linux內(nèi)核網(wǎng)絡(luò)基礎(chǔ)-TCP相關(guān)的幾個(gè)關(guān)鍵結(jié)構(gòu)體-小記中進(jìn)行了介紹,如下圖是四個(gè)核心結(jié)構(gòu)體,四個(gè)結(jié)構(gòu)的關(guān)系具有面向?qū)ο蟮奶卣?,通過層層繼承,實(shí)現(xiàn)了類的復(fù)用;內(nèi)核中網(wǎng)絡(luò)相關(guān)的很多函數(shù),參數(shù)往往都是struct sock,函數(shù)內(nèi)部依照不同的業(yè)務(wù)邏輯,將struct sock轉(zhuǎn)換為不同的業(yè)務(wù)結(jié)構(gòu)。

struct tcp_sock
從struct inet_connection_sock
結(jié)構(gòu)體的基礎(chǔ)上繼承而來,在struct inet_connection_sock
上增加了一些tcp協(xié)議相關(guān)的字段,如滑動(dòng)窗口協(xié)議,擁塞算法等一些TCP專有的屬性。由于這種繼承關(guān)系,可以互相轉(zhuǎn)換,如下舉例兩種轉(zhuǎn)換方式,第一種是struct sock轉(zhuǎn)換為struct tcp_sock,第二種是struct sock轉(zhuǎn)換成struct inet_connection_sock。具體的關(guān)于結(jié)構(gòu)體詳細(xì)介紹可以查看Linux內(nèi)核網(wǎng)絡(luò)基礎(chǔ)-TCP相關(guān)的幾個(gè)關(guān)鍵結(jié)構(gòu)體-小記。下面將struct tcp_sock展開可以看到與網(wǎng)絡(luò)擁塞控制相關(guān)的字段。
【文章福利】小編推薦自己的Linux內(nèi)核技術(shù)交流群:【749907784】整理了一些個(gè)人覺得比較好的學(xué)習(xí)書籍、視頻資料共享在群文件里面,有需要的可以自行添加哦?。。。ê曨l教程、電子書、實(shí)戰(zhàn)項(xiàng)目及代碼)? ? ?


struct tcp_sock中定義的關(guān)于網(wǎng)絡(luò)擁塞控制相關(guān)的字段如下所示:
下面看一個(gè)特別重要的框架,也可以稱為是擁塞控制引擎,如下結(jié)構(gòu)體所示,tcp_congestion_ops描述了一套擁塞控制算法所需要支持的操作。這個(gè)框架定義了一些鉤子函數(shù),Linux內(nèi)核中不同的擁塞控制算法根據(jù)算法思想實(shí)現(xiàn)以下鉤子函數(shù),然后進(jìn)行注冊(cè)即可完成擁塞控制算法的設(shè)計(jì)。
用戶可以通過自定義以上鉤子函數(shù)實(shí)現(xiàn)定制擁塞控制算法,并進(jìn)行注冊(cè)。以下截取cubic擁塞控制算法對(duì)接口的實(shí)現(xiàn)、注冊(cè)的代碼片段。可以注意到cubic只實(shí)現(xiàn)了擁塞控制引擎tcp_congestion_ops的部分鉤子函數(shù),因?yàn)橛幸恍┿^子函數(shù)是必須實(shí)現(xiàn),有一些是根據(jù)算法選擇實(shí)現(xiàn)的。
在Linux用戶態(tài)可以通過參數(shù)查看當(dāng)前使用的擁塞控制算法、當(dāng)前可支持的擁塞控制算法。如下表所示是兩個(gè)參數(shù)以及含義。

法??梢钥吹疆?dāng)前可支持的擁塞控制算法中包含bbr算法,bbr算法在內(nèi)核版本4.9開始支持的。

如果留意的話,在本文開始時(shí)提到了很多傳統(tǒng)的擁塞控制算法,那么在上面的命令中沒有看到,其實(shí)有眾多擁塞控制算法在Linux中沒有進(jìn)行安裝,如下命令查看Linux系統(tǒng)中所有已實(shí)現(xiàn)的擁塞控制算法模塊:

如果想安裝特定的擁塞控制算法可以通過modprobe命令對(duì)指定的擁塞控制算法進(jìn)行安裝,如下所示安裝了Vegas擁塞控制算法,此時(shí)再查看當(dāng)前系統(tǒng)中可以使用的擁塞控制算法,多了一個(gè)Vegas算法。

除了可以動(dòng)態(tài)查看當(dāng)前Linux系統(tǒng)可用的擁塞控制算法、當(dāng)前使用的擁塞控制算法外還可以動(dòng)態(tài)切換擁塞控制算法。如下所示將默認(rèn)的cubic擁塞控制算法切換為bbr擁塞控制算法。

切換后驗(yàn)證如下,當(dāng)前運(yùn)行的擁塞控制算法由之前的cubic擁塞控制算法切換到了bbr擁塞控制算法。


原文作者:技術(shù)簡說
