可使用classful qdisc的代理來解鎖Linux流量控制的靈活性和控制力。classful qdisc能夠附加過濾器,容許將報文重定向到特定的類和子隊列。html
有幾個常見的術語用來描述直接附加到root
qdisc和終止類的類。附加到root
qdisc的類稱爲根類,通常爲內部類。任何特定qdisc中的終止類稱爲葉類,相似於樹形結構的類。除了把結構比喻成一棵樹外,一般也會使用家庭關係進行比喻。算法
HTB是Linux中CBQ(參閱第7.4章)qdisc的一種更易理解和直觀的替換品。CBQ和HTB能夠控制給定鏈路上的出站帶寬。這兩種方式均可以使用一個物理鏈路來模擬多個較慢的連接,並將不一樣的鏈路發送到不一樣的模擬鏈路上。在這兩種狀況下,必須指定如何將物理鏈路劃分爲模擬鏈路,以及肯定要發送的報文使用哪一個模擬鏈路。編程
HTB使用了令牌和桶的概念,並使用了基於類的系統和過濾器對流量進行復雜和細粒度的控制。經過一個複雜的借用模型,HTB能夠實現各類複雜的流量控制技術。另外一種最簡單的方式是在整流時使用HTB。網絡
經過理解令牌和桶或掌握HTB的功能能夠了解到,HTB僅僅是一個邏輯上的步驟。該qdisc容許用戶定義令牌和桶的特性,並容許用戶任意嵌套這些桶。當與分類方案結合使用時,能夠以很是細粒度的方式控制流量。tcp
例11. HTB的tc用法:優化
Usage: ... qdisc add ... htb [default N] [r2q N] default minor id of class to which unclassified packets are sent {0} r2q DRR quantums are computed as rate in Bps/r2q {10} debug string of 16 numbers each 0-3 {0} ... class add ... htb rate R1 burst B1 [prio P] [slot S] [pslot PS] [ceil R2] [cburst B2] [mtu MTU] [quantum Q] rate rate allocated to this class (class can still borrow) burst max bytes burst which can be accumulated during idle period {computed} ceil definite upper class rate (no borrows) {rate} cburst burst but for ceil {computed} mtu max packet size we create rate map for {1600} prio priority of leaf; lower are served first {0} quantum how much bytes to serve from leaf at once {use r2q} TC HTB version 3.3
不一樣於前面討論的幾乎全部軟件,HTB是一個新的qdisc,現有的發行版可能缺乏使用HTB所須要的全部軟件和能力。內核版本2.4.20及其之後的版本會支持HTB,早期的內核版本須要打補丁。爲了在用戶空間支持HTB,參見HTB。ui
HTB的最多見應用之一是將傳輸的流量調整到特定速率。this
全部的整流都發生在葉類上。內部或根類上不會發送整流,這些類僅會在借用模型中給出如何分配可用的令牌。編碼
HTB的一個基本功能是借用機制。當子類超速率以後會借用父類的令牌。在達到ceil
(此時子類會有數據包排隊,等待傳輸,直到有更多可用的令牌爲止。)以前,子類會持續嘗試借用父類的令牌。因爲使用HTB僅能夠建立兩種主要的類(葉子類和內部類),所以下面的表和圖區分了借用機制的各類可能的狀態和行爲。.net
表2.HTB類型狀態和和潛在的action令牌
type of class | class state | HTB internal state | action taken |
---|---|---|---|
leaf | < rate |
HTB_CAN_SEND |
葉子類會出站隊列中的數據,不能大於突發的報文。 |
leaf | > rate , < ceil |
HTB_MAY_BORROW |
葉子類會嘗試從父類借用令牌(tokens/ctokens)。若是有可用的令牌,則以quantum 增量的方式借出,且葉子類的出站速率不能大於cburst 字節。 |
leaf | > ceil |
HTB_CANT_SEND |
沒有出隊列的報文。會致使報文延遲,並增長延遲以知足所需的速率。 |
inner, root | < rate |
HTB_CAN_SEND |
內部類會給子類借用令牌 |
inner, root | > rate , < ceil |
HTB_MAY_BORROW |
葉子類會嘗試從父類借用令牌(tokens/ctokens),父類會以quantum 遞增的方式將令牌借給競爭的子類。 |
inner, root | > ceil |
HTB_CANT_SEND |
內部類不會嘗試從父類借用令牌,且父類不會將令牌借給子類。 |
下圖展現了借用令牌的流程,被借用的令牌會計入父類。爲了借用模型可以正常工做,每一個類都必須精確計算自身和子類使用的令牌。基於這種原則,子類或葉子類使用的令牌會計入父類中,直到到達root類。
任何想要借用令牌的子類會從其父類中請求一個令牌,若是父類也達到了rate
的限制,它將會向本身的父類借用令牌,以此類推,直到找到一個可用的令牌或達到root類爲止。所以借用的令牌會流向葉子類,而對令牌的統計則會流向root類。
注意:上圖中有好幾個HTB root類,每一個root類均可以模擬一個虛擬迴路。
default
每一個HTB qdisc對象可選的參數,默認的default爲0,這樣未分類的流量會使用硬件速度出隊列,不會通過附加到root qdisc的全部類。
rate
用於設置限制傳輸流量的最小速度。能夠認爲其至關於信息的提交速率,或爲一個給定的葉子類保證的帶寬。
ceil
用於設置限制傳輸流量的最大速度。借用模型應該說明如何使用該參數。能夠認爲其等同於"突發的帶寬"。
burst
rate
的桶大小。HTB在等待更多令牌前能夠入隊列burst
字節的數據。
cburst
ceil
的桶大小。HTB在等待更多ctokens前能夠入隊列cburst
字節的數據。
quantum
這是HTB用於控制借用的關鍵參數。一般HTB會計算一個正確的quantum
,用戶無需指定。修改該值會對競爭下的借用和整流形成巨大的影響(由於它會在流量超過rate(且低於ceil)的子類間對流量進行分割,並傳輸這些子類的報文)。
r2q
提供給用戶使用,用於幫助優化特定類的quantum
。
mtu
prio
在輪詢處理中,能夠優先處理具備最低優先級字段數值的類對應的報文。強制字段。
prio
類在層次結構中的位置。若是一個類直連到一個qdisc而不是另外一個類,則能夠省略minor(見下)。強制性字段。
prio
與qdisc類型,也能夠命名類。major號必須等於其附加到的qdisc的major號。該字段是可選的,但在類包含子類時須要配置該字段。
一個HTB qdisc類樹的root包含三個字段:
parent major:minor | root
該強制參數肯定了HTB實例的位置:接口的root位置仍是位於一個已存在的類中。
handle major:
與其餘的qdisc相似,能夠給HTB分配一個句柄,該句柄應該包含一個major號,後跟一個冒號。可選字段,但若是類將在這個qdisc中生成,則很是有用。
default minor-id
未分類的流量會使用該minor-id發送到類。
如下是從http://www.docum.org/docum.org/和(new) LARTC mailing list (也能夠參見 (old) LARTC mailing list archive)中挑選的使用HTB的通常準則。這些準則能夠方便初學者在最大程度上了解HTB。
HTB的整流僅發生在葉子類上。
因爲HTB不會在除葉子類的類上進行整流,所以葉子類的rate
s 之和不能大於父類的ceil
。理想狀況下子類的rate
s 之和應該與父類的ceil
相匹配,容許父類將剩餘的帶寬(ceil - rate)分配給子類。
在使用HTB時,會屢次重複這個關鍵概念。只有葉子類纔會真正進行整流;報文只會在這些葉子類上延遲。內部類(到root類路徑上的類)定義瞭如何進行借入/借出(參見Section 7.1.3, 「Borrowing」)。
只有在一個類大於rate
但低於ceil
時纔會用到quantum
。
quantum
應該設置爲等於或大於MTU的值。即便在quantum
很小的狀況下,HTB也會至少給每一個服務一次報文入隊列的機會。這種狀況下,HTB將沒法精確計算真實使用的帶寬。
父類以增量爲quantum
的方式將令牌提供給子類,以便得到最大的粒度和最均勻的瞬時帶寬分佈,quantum
應該儘可能小,但不能小於MTU。
tokens和ctokens的不一樣點僅對葉子類有意義,由於非葉子的類僅會借給子類令牌。
對HTB借用的更精確的描述應該是"使用"(並不會歸還)
如前面所述,一個HTB實例可能會包含不少類,每一個類都包含一個qdisc,默認爲tc-pfifo。當入隊列一個報文時,HTB會從root類開始,使用多種方式來決定哪一個類去接收該數據。在沒有特殊配置選項的狀況下,處理會至關簡單。在樹的每一個節點上查找一條指令,而後轉到指令指向的類。若是找到的類是一個葉子類,則將報文入隊列到此處,若是不是一個葉子節點,則從該節點開始重複上述工做。
在訪問的每一個節點上會執行如下操做,直到發送到另外一個節點(子節點)或終止該過程爲止。
這種算法會確保報文老是在某個地方結束。
HFSC classful qdisc會對延遲敏感的流量和吞吐量敏感的流量進行平衡。當處於擁塞或擠壓狀態下時,HFSC排隊規則會在須要時根據服務曲線定義穿插處理對延遲敏感的流量。
PRIO classful qdisc的工做原理很是簡單。當它須要入隊列一個報文時,會檢查第一個類,若是該類包含報文,則將該報文入隊列,不然檢查下一個類,直到最後一個類。PRIO是一個不會延遲報文的調度器,它是一個連續工做的qdisc(儘管包含在類中的qdisc可能不是連續工做的)。
當使用tc qdisc add命令建立PRIO時,會建立固定數目的bands(與pfifo相似)。每一個band就是一個類(雖然不能使用tc class add添加類)。建立qdisc的時候建立的band的數目是固定的。
當入隊列報文時,總會優先檢查band 0。若是band 0中沒有報文,則PRIO會檢查band 1,以此類推。具備最大可靠性的報文會進入band 0,最小延遲的報文會進入band 1,其他進入band 2。
因爲PRIO本地包含minor號0,band 0實際上就是major:1,band 1爲major:2,等等。對於major,能夠用handle參數替換'tc qdisc add'上分配給qdisc的major號。
$ tc qdisc ... dev dev ( parent classid | root) [ handle major: ] prio [bands bands ] [ priomap band band band... ] [ estimator interval time‐constant ]
有三種方式決定一個報文入隊列時的band:
只有此qdisc指定了priomap。
bands:不一樣band的數目,若是該值不是默認值3,則須要更新priomap。
priomap:附加到root qdisc的tc過濾器,能夠將流量直接指向一個類。
一個priomap 指定了該qdisc如何將一個報文映射到一個特定的band。對報文的映射基於其TOS的值。
0 1 2 3 4 5 6 7 +-----+-----+-----+-----+-----+-----+-----+-----+ | PRECEDENCE | ToS | MBZ | RFC 791 +-----+-----+-----+-----+-----+-----+-----+-----+ 0 1 2 3 4 5 6 7 +-----+-----+-----+-----+-----+-----+-----+-----+ | DiffServ Code Point (DSCP) | (unused) | RFC 2474 +-----+-----+-----+-----+-----+-----+-----+-----+
RFC 791 和RFC 2474對(4個比特位的)TOS的定義稍微有所不一樣,後者取代了前者在格式上的定義,但不是全部的軟件,系統和術語都能及時跟上這種變化。所以,報文分析程序一般會使用Type of Service (ToS)而不是DiffServ Code Point (DSCP)。
RFC 791 對IP的TOS首部的解析以下:
Binary | Decimal | Meaning |
---|---|---|
1000 | 8 | Minimize delay (md) |
0100 | 4 | Maximize throughput (mt) |
0010 | 2 | Maximize reliability (mr) |
0001 | 1 | Minimize monetary cost (mmc) |
0000 | 0 | Normal Service |
因爲這4個比特位的右邊還有一個比特位,所以TOS字段的實際值爲TOS比特值的兩倍。運行 tcpdump -v -v能夠顯示整個TOS字段的值,而不只僅4個比特位的值。
下表展現瞭如何將TOS值映射到priomap band:
ToS Field | ToS Bits | Meaning | Linux Priority | Band |
---|---|---|---|---|
0x0 | 0 | Normal Service | 0 Best Effort | 1 |
0x2 | 1 | Minimize Monetary Cost (mmc) | 1 Filler | 2 |
0x4 | 2 | Maximize Reliability (mr) | 0 Best Effort | 1 |
0x6 | 3 | mmc+mr | 0 Best Effort | 1 |
0x8 | 4 | Maximize Throughput (mt) | 2 Bulk | 2 |
0xa | 5 | mmc+mt | 2 Bulk | 2 |
0xc | 6 | mr+mt | 2 Bulk | 2 |
0xe | 7 | mmc+mr+mt | 2 Bulk | 2 |
0x10 | 8 | Minimize Delay (md) | 6 Interactive | 0 |
0x12 | 9 | mmc+md | 6 Interactive | 0 |
0x14 | 10 | mr+md | 6 Interactive | 0 |
0x16 | 11 | mmc+mr+md | 6 Interactive | 0 |
0x18 | 12 | mt+md | 4 Int. Bulk | 1 |
0x1a | 13 | mmc+mt+md | 4 Int. Bulk | 1 |
0x1c | 14 | mr+mt+md | 4 Int. Bulk | 1 |
0x1e | 15 | mmc+mr+mt+md | 4 Int. Bulk | 1 |
第二列包含相關的四個ToS位的值,以及它們的含義。例如,15表示想要最小貨幣成本,最大可靠性,最大吞吐量以及最小延遲。
第四列列出了Linux內核解析TOS比特位的方式,展現了TOS映射到的優先級。
最後一列展現了默認的priomap值。在命令行中,默認的priomap爲:1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1
,其對應的優先級爲4,對應的band爲1。priormap容許更高的優先級(>7),這類優先級並不對應TOS的映射,表示其餘的含義。
沒法對PRIO類進行進一步的配置——它們在附加PRIO qdisc時自動建立。但每一個類能夠包含更多的qdisc。
當低band中包含了大量流時,可能會致使高band飢餓。能夠給這些band附加一個整流器,以確保這些band不會佔用大部分鏈路。
CBQ是一個流量控制系統的類實現。CBQ是一個classful qdisc,它能夠在類層次結構中共享鏈路。它包含整流元素以及優先級功能。經過對入隊列事件的鏈路空閒時間和對底層鏈路帶寬的瞭解來實現整流。
經過計算鏈路空閒時間以及設置當結果偏離了設定的限定值時所採起的動做來實現整流。
當一個10mbit/s鏈接到1mbit/s時,該鏈路上90%的時間都是空閒的。若是不是,則須要對其進行限流,使其90%的時間處於空閒狀態。
從內核的角度很難對流量進行衡量,所以,CBQ會根據設備驅動程序請求數據之間的毫秒數計算空閒時間。結合報文的大小,能夠近似知道鏈路滿或空的程度。
應當謹慎應對這種結果,由於並非每次都能計算出合適的結果。在不太真實的網絡設備(例如以太網上的PPP或TCP / IP上的PPTP)的狀況下,對物理鏈路帶寬的定義可能不正確。
在操做時,有效空閒時間是用指數加權移動平均(EWMA)來進行測量的。這種針對空閒狀態計算出的最近的報文數是之前的報文數的指數倍。EWMA是一種有效的計算方法,能夠解決系統處於活動狀態或非活動狀態的問題。例如,UNIX系統的平均負載就採用了這種計算方式。
從EWMA測得的值中減去計算出的空閒時間,結果稱爲avgidle
(平均空閒時間)。一個完美加載的鏈路的avgidle
應該爲0:接收的報文間隔等於計算的結果。
一個過載的鏈路的avgidle
爲負值,若是負值過大,則CBQ會限流。相反,若是一個空閒的鏈路的avgidle
過大,則在一段時間的靜默後,可能會容許無限的帶寬。爲了防止發生這種狀況,將avgidle
的上限是maxidle
。
從理論上講,若是超出限制,CBQ會嚴格限制計算出的用於放通報文的時間,而後放通一個報文,而後再進行限流。因爲定時器精度的限制,這種方式可能不可行,參見下圖的minburst
參數。
在CBQ qdisc下可能存在不少類。每一個類會包含其餘qdisc,默認爲tc-pfifo。
當入隊列一個報文時,CBQ會從root開始,使用多種方法來肯定使用哪一個類來接收該數據。若是作出斷定,則對接收的類重複此過程,該類可能有進一步的方法來將流量分類到子類(若是有的話)。CBQ可使用以下方法將一個報文分類給任何子類:
每一個類也有一個級別。鏈接到類層次結構底部的葉節點的級別爲0。
分類是一個循環,當達到葉子類時終止。循環中的任一點均可能跳到一個回退算法中。循環包含以下步驟:
回退算法位於循環以外,遵循以下規則:
當任意一種算法終止時,報文被加入到所選擇的類中。所以,報文能夠不在葉節點入隊列,而在層次結構的中間入隊列。
當入隊列發送到網絡驅動的報文時,CBQ決定哪一個類能夠發送報文。CBQ使用一個基於權重的輪詢處理,每一個類中的報文都有機會按照順序發送出去。WRR會從具備最高優先級的類中處理報文,直到這些類中沒有任何數據,而後處理低優先級的類。
因爲每一個類都不容許以長度發送數據,所以只能在每輪中取出可配置數量的數據。
若是一個類即將超出限制,且不受限制,它將嘗試從沒有隔離的兄弟節點中借用avgidle,從下向上重複這個過程。若是一個類沒法借用到足夠的avgidle來發送報文,則這個類會限流,而且不要求報文等待足夠的時間來使avgidle增長到零以上。
CBQ的root qdisc有以下參數:
parent
root
| major:minor
:
強制參數,肯定CBQ實例的位置,即接口的root類仍是一個現有的類中。
handle
major:
與其餘類的qdisc相似,CBQ能夠分配一個句柄。應該包含一個使用冒號分割的主號。可選參數。
avpkt
bytes
爲了計算,平均報文大小必須事先可知。默認至少爲MTU的2/3。強制參數。
bandwidth
rate
底層可用的帶寬,用於肯定空閒時間,CBQ必須知道帶寬:A)目標帶寬;B)底層物理接口或C)父qdisc。這是一個相當重要的參數,後面會詳細介紹。強制參數。
cell
size
cell大小肯定了報文傳輸時間計算的粒度,必須是2的整數次冪,默認爲8。
mpu
bytes
0大小的報文可能也會佔用傳輸的時間。這個值是報文傳輸時間計算的下限,小於該值的報文仍然被認爲是這個大小。默認值爲0。
ewma
log
CBQ使用指數加權移動平均值(EWMA)來計算空閒狀態,該方法能夠以平滑測量的方式輕鬆應對短期的突發。log
值決定了平滑發送的數量。更低的值意味着更高的靈敏度。必須爲0~31之間的數值,默認爲5。
一個CBQ qdisc不會自行整流。它須要知道有關底層鏈路的某些參數。實際的整流是在類中完成的。
類有不少參數類配置其操做:
parent
major:minor
層級結構中類的位置。若是直接附加到一個qdisc,而不是一個類,則能夠忽略minor。強制參數。
classid
major:minor
與qdisc相似,class是可命名的。majob號必須等於其屬於的qdisc的major號。可選參數,但若是一個類包含子類時必選。
weight
weightvalue
當入隊列到底層時,將以循環方式使用類處理流量。一般具備更高權重的qdisc的類在一輪循環中能夠發送更多的流量,所以該類也能夠入隊列更多的流量。因爲一個類下的全部權重都被歸一化了,所以只有比率纔是重要的。默認使用配置的速率,除非該類的優先級是最大的,在這種狀況下,它的優先級設置爲1。
allot
bytes
allot
指定了在每一個輪詢中一個qdisc能夠入隊列的字節數據。可使用上面描述的權重對該參數進行加權。
priority
priovalue
在輪詢處理中,具備最低優先級字段值的類會優先處理報文。強制字段。
rate
bitrate
這個類(包括子類)可以傳輸的最大聚合速率。bitrate 使用tc的方式指定速率(如1544kbit)。強制字段。
bandwidth
bitrate
該參數與建立CBQ qdisc時指定的帶寬不一樣。CBQ類的bandwidth
參數僅用於肯定maxidle和offtime,而該參數僅在指定maxburst或minburst時才計算。所以,該參數僅在指定maxburst或minburst時使用。
maxburst
packetcount
該報文數用於計算maxidle,這樣在avgidle達到maxidle時,且在avgidle 將到0前,容許平均報文的突發。增長該值能夠容許更大的突發。用戶沒法直接設置maxilde,只能經過該參數設置。
minburst
packetcount
如前面所述,CBQ須要設置限制來防止發生超額。理想的解決方案是精確計算出空閒時間,而後傳遞1個報文。然而,Unix內核一般很難調度小於10ms的事件,所以最好能在一段比較長的時間內限流,而後一次性傳遞minburst的報文,而後使minburst 時間更長。等待的時間稱爲offtime。從長遠角度看,更大的minburst 值會得到更精確的整流效果,但在毫秒時間尺度上會產生更大的突發。
minidle
microseconds
minidle:若是avgidle小於0,則說明此時已經超額,須要等到avgidle可以發送一個報文爲止。爲了防止底層鏈路長時間中斷致使的突發,若是avgidle值太低,則會重置爲minidle。minidle 使用負值表示,所以10表示avgidle的上限爲-10us。
bounded
| borrow
指定了一個借用策略。即要麼類會從兄弟節點借用帶寬,要麼將本身視爲受限的。 兩者是互斥的。
isolated
| sharing
指一個共享策略。這個類要麼對它的兄弟類使用共享策略,要麼認爲本身是孤立的。兩者是互斥的。
split major:minor and defmap bitmap[/bitmap]: 若是查詢附加到類的過濾器以後沒有給出結論,CBQ能夠根據報文的優先級進行分類。有16個優先級,從0到15。defmap指定了類指望接收的優先級,優先級使用位圖進行表示。最低有效位對應的優先級爲零。split參數告訴CBQ必須在哪一個類上作出決定,這個類應該是要添加的類的(祖)父類。
如, 'tc class add ... classid 10:1 cbq .. split 10:0 defmap c0' 配置類10:0發送優先級爲6和7到10:1的報文。
可替代的寫法爲: 'tc class add ... classid 10:2 cbq ... split 10:0 defmap 3f',將發送全部的優先級爲0, 1, 2, 3, 4 和5 的報文到10:1。
estimator interval timeconstant: CBQ能夠測量每一個類使用的帶寬,以及哪些tc過濾器能夠用來對報文進行分類。爲了肯定帶寬,它使用了一個很是簡單的評估器,每隔一微秒測量經過了多少流量。這也是一個EWMA,它的時間常數能夠指定,一樣是以微秒爲單位。時間常數對應於測量的遲緩性,或者相反,對應平均值對短脈衝的靈敏度。更高的值表示更低的靈敏度。
該qdisc不包含在標誌的內核中。
WRR qdisc使用加權輪詢方案在其類之間分配帶寬。它與CBQ qdisc相似,包含能夠插入任意qdisc的類。具備足夠需求的全部類都將得到與類相關的權重成比例的帶寬。能夠經過tc程序手動設置權重。可是對於傳輸大量數據的類,也可使它們自動減小。
該qdisc有一個內置的分類器,能夠未來自或發送到不一樣機器的數據包分配給不一樣的類(使用MAC或IP以及源或目的地址)。當Linux做爲橋時會使用MAC地址。這些類會根據所看到的報文自動分配給機器。
該qdisc在許多無關的人共享Internet鏈接的站點上時很是有用。WRR發行版的關鍵部分是一組爲此類站點設置相關行爲的腳本。