1. 程式人生 > >Linux TCP擁塞控制中undo操作

Linux TCP擁塞控制中undo操作

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow

也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!

                Linux的TCP實現複雜且繁瑣,建議不要直接去看程式碼,而是花點時間把TCP規範先擼一遍。本文主要描述一下TCP實現中undo操作,然後順便再吐一下槽(千萬不要覺得吐槽有什麼不好,很多好東西都是從吐槽開始的,從造紙,蒸汽機,到法蘭西第一共和國,再到Linux...)。

1.TCP對網路擁塞是基於預測的

TCP屬於網路的一部分,這無可厚非,但當一個人說自己精通網路的時候,他更可能的意思是自己精通網路節點的行為而不是端到端的行為。比如,一個Cisco的工程師精通各種路由協議,可以設計出複雜的OSPF網路,BGP網路,優化各個節點的pps,在某處啟用spanning tree,各種演算法的靈活運用,不管在研發的標準還是在運維的標準,都足以讓你煙花繚亂,崇拜至極,好像網路中的一切盡掌握在其手中,玩轉地不亦樂乎,我曾經接觸過這樣的人,也認識不少這樣的朋友,曾經站在研發的角度與其對峙,也曾站在他們的立場上置碼農的傷痛於不顧...這些人保證,只要資料包經過他們掌管的裝置,在效率的意義上,他就可以保證其儘可能線速通過,在安全的意義上,任何邪惡的蛛絲馬跡都無法逃避出他們的法眼...

然而,一旦涉及TCP,他們就無能了。我一直都覺得,一個TCP連線的行為是“與全世界相關的”,沒有任何一個管理員可以控制全世界所有的計算機的行為,就算是神仙也不行。TCP只定義了端到端的行為,就好像假設兩個通訊的程序是相鄰的一樣,TCP首先就是這樣假設的,這就是“端到端滑動視窗”的初衷,即傳送多少資料僅僅和接收端可以接收多少有關。當發現端到端之間並不是直接相鄰而是一個變換莫測具有蝴蝶效應的網路時,擁塞控制演算法是被加進去的。

TCP在設計之初,並沒有任何的顯式擁塞提醒機制,也沒有否認應答機制,因此對於傳送端而言,預測網路情況的唯二工具就是ACK和RTT,並且,RTT是測量出來的,這就意味著它會受到接收端延遲傳送ACK的影響,雖然TCP規範要求避免stretch ACK,但是如果ACK丟了,什麼也不好說...因此對於TCP網路而言,擁塞是預測出來的,下面是現實中的類似預測的例子:
同居的室友去買菜,然後走了10個小時都沒有回來,你會怎麼想,你可能早就會打電話確認,可是如果什麼都沒有,你只能假設室友出事了,於是報警;
同居的室友和四個互不相識的鄰居前後一起去同一個市場買菜,四人回來很久了,先走的室友還沒有回來,此時你會怎麼想。
不管上面任何一種情況,都可能僅僅意味著室友玩了一個惡作劇而已...

2.當預測錯誤就是可以undo的

如果我們預測了網路的擁塞,可是後來有跡象表明事實並不是我們想象的那樣,此時就可以undo那些為了應對網路擁塞而做出的動作,問題是哪些可以undo,哪些不可以undo。

3.哪些可以undo

一旦我們預測了網路的擁塞,我們需要做的有兩件事:
a).減少擁塞視窗
b).重發可能丟失的資料包(人去買菜沒有再回來,如果僅僅是為了菜的話,你可以再派一個人去買菜)。
減少擁塞視窗是為了不為既已擁塞的網路添堵,這無疑是一種君子行為,不適合中國式司機,當然也不適合中國的TCP研發者們,雖然也會減窗,但初衷不是不添堵,而是不減窗發了資料也沒用,淨增加流量...當發現預測錯誤,網路根本就沒有擁塞的時候,TCP最好的行為就是補償自己了,在一個“人人為我,我為人人”的世界,沒人會因為你的君子行為補償你,只有自己可以。因此可以將擁塞視窗升高至降窗之前的值,這也算是一種自我補償了。

4.哪些不可以undo

在預判斷為擁塞的時候,會重發可能丟失的資料包,這部分是不可以undo的,沒有任何機制可以將發出去的資料再收回來!唯一能做的,就是下次預測的時候再準確些吧。

5.什麼時候可以undo

我們知道,TCP可以通過reordering個重複ACK以及超時來預判斷資料包已經丟失,但是並不意味著發生了重複ACK或者超時一定是資料包丟失了,也可能是資料包發生了重排序,延遲亂序到達了接收端,因此重複ACK或者超時僅僅是資料包丟失的必要非充分條件。

有一種情況下,我們可以下結論說我們的預判斷是錯誤的,那就是我們重發的資料包均被對端證實收到了兩遍,這就是說一遍是原始的傳送,一遍是重發。通過DSACK或者時間戳機制可以可以很好的發現這一點。一旦發現所有的重發包均收到了DSACK,我們就可以undo了,把可以undo的全都undo掉。    

6.undo到哪裡

如果已經因為錯誤的預判斷進入了recovery狀態,那麼undo到哪裡呢?

如果在收到重發資料包的所有DSACK時,傳送視窗已經足夠乾淨了,那麼就可以直接到Open狀態了,但是如果在UNA之後依然有資料被標記為SACK或者LOST,這就表明依然有亂序的可能,那麼就進入Disorder狀態好了。我們知道,在recovery狀態,視窗是下降的,然而在Disorder狀態,視窗並不一定是僵住(僵住是為了在確認有足夠的重複ACK前,無法預判斷是亂序還是丟包,所以要先僵住視窗)的,如果我們在undo的時候,reordering的值已經變大,那麼就說明網路更有可能是亂序了,此時可以繼續增加擁塞視窗的大小。

下圖是一個總體的圖,大意所在:


7.Linux核心協議棧的持續優化

如果你看2.6.8的核心和4.4的核心,會發現在TCP擁塞處理上有很大的不同,把2.6.32作為標準版的話,你會發現3.10版本增加了prr降窗演算法,然後在緊隨的3.11,引入了“reordering大於預設值在Disorder狀態下可不必僵住而增加視窗”的演算法,然後再看4.3/4.4,你會發現在擁塞狀態機中,Mid Switch這個東西事實上已經完全打破了NewReno的假設,這就是NewReno提前退出。並且看似Linux在持續優化的過程中,已經變得不再君子,突發不再被限制,搶道插隊時有發生...

8.一個刷重傳佇列的細節

這一節與本文大意無關,只是不想另外寫一篇了。
在RFC規範中,擁塞狀態下,應該首先發LOST資料包,然後發新資料包,最後“前向傳送”cover(即High seq)之前既沒有標記LOST,也沒有SACK的資料包,RFC這麼規定是有根據的,背後的數學推理根本就不會在RFC中給出,RFC只給結論!請不要質疑這種方案的合理性,請不要!
但是如果你看tcp_xmit_retransmit_queue的時候,很難發現這個邏輯:
    tcp_for_write_queue_from(skb, sk) {        __u8 sacked = TCP_SKB_CB(skb)->sacked;        if (skb == tcp_send_head(sk))            break;        if (hole == NULL)            tp->retransmit_skb_hint = skb;        // 視窗限制檢查        if (tcp_packets_in_flight(tp) >= tp->snd_cwnd)            return;        if (fwd_rexmitting) {begin_fwd:            // 最高前向傳送到high sacked            if (!before(TCP_SKB_CB(skb)->seq, tcp_highest_sack_seq(tp)))                break;        } else if (!before(TCP_SKB_CB(skb)->seq, tp->retransmit_high)) {            tp->retransmit_high = last_lost;            // 關鍵在這裡!tcp_can_forward_retransmit會判斷,如果有新資料就緒,就會break!            // 進入tcp_can_forward_retransmit之後,會有tcp_may_send_now判斷,其判斷是否有            // 新資料就緒。Linux並沒有在一個路徑按RFC規範的傳送優先順序進行傳送,在有新資料            // 就緒的時候,只是簡單的退出,讓傳送路徑自行處理!            if (!tcp_can_forward_retransmit(sk))                break;            if (hole != NULL) {                skb = hole;                hole = NULL;            }            // 只有在tcp_can_forward_retransmit不會break的時候,才會“前向傳送”            fwd_rexmitting = 1;            goto begin_fwd;        } else if (!(sacked & TCPCB_LOST)) {            if (hole == NULL && !(sacked & (TCPCB_SACKED_RETRANS|TCPCB_SACKED_ACKED)))                hole = skb;            continue;        } else {            // 首先發送LOST資料            last_lost = TCP_SKB_CB(skb)->end_seq;        }        // 不會發送已經被SACK,或者已經被重傳的資料包!        if (sacked & (TCPCB_SACKED_ACKED|TCPCB_SACKED_RETRANS))            continue;        tcp_retransmit_skb(sk, skb);    }

9.再次針對OpenSSL/OpenVPN/Linux TCP的吐槽

雖然目前不再從事OpenSSL/OpenVPN的工作,但是我依然在持續關注,依然無法容忍這種讓人看不懂,即便看懂了也不爽的程式碼,如果有人能教我看懂了OpenSSL的ASN.1處理的程式碼,我願意發 100塊錢的紅包!OpenVPN也一樣!對於TCP的程式碼,請看下面的片段:
/* People celebrate: "We love our President!" */static int tcp_try_undo_recovery(struct sock *sk){    struct tcp_sock *tp = tcp_sk(sk);....
哪位大神可以解釋一下“People celebrate: "We love our President!"”的意義!我敢說,如果我在自己的程式碼中寫這樣的註釋,一定會被認為是抿了兩口二鍋頭後的傑作,然後被批,被人認為是不正常。曾經我在設計conntrack cache的時候,在程式碼中寫了一句“旋轉升降座椅一定會爆炸”,然後我刪了,我覺得這不符合我們這個國家中規中矩的文化,於是我刪了。在我們的文化裡,不會有“talk is cheap show me the code”,因此也就不會有哈格里夫斯,不會有瓦特,不會有喬布斯,不會有Linus...只會有誰呢?反正就是一堆人說了算,抑揚頓挫,從小被灌輸,然後長大灌輸別人。爆炸!           

給我老師的人工智慧教程打call!http://blog.csdn.net/jiangjunshow

這裡寫圖片描述 你好! 這是你第一次使用 **Markdown編輯器** 所展示的歡迎頁。如果你想學習如何使用Markdown編輯器, 可以仔細閱讀這篇文章,瞭解一下Markdown的基本語法知識。

新的改變

我們對Markdown編輯器進行了一些功能拓展與語法支援,除了標準的Markdown編輯器功能,我們增加了如下幾點新功能,幫助你用它寫部落格:

  1. 全新的介面設計 ,將會帶來全新的寫作體驗;
  2. 在創作中心設定你喜愛的程式碼高亮樣式,Markdown 將程式碼片顯示選擇的高亮樣式 進行展示;
  3. 增加了 圖片拖拽 功能,你可以將本地的圖片直接拖拽到編輯區域直接展示;
  4. 全新的 KaTeX數學公式 語法;
  5. 增加了支援甘特圖的mermaid語法1 功能;
  6. 增加了 多螢幕編輯 Markdown文章功能;
  7. 增加了 焦點寫作模式、預覽模式、簡潔寫作模式、左右區域同步滾輪設定 等功能,功能按鈕位於編輯區域與預覽區域中間;
  8. 增加了 檢查列表 功能。

功能快捷鍵

撤銷:Ctrl/Command + Z
重做:Ctrl/Command + Y
加粗:Ctrl/Command + B
斜體:Ctrl/Command + I
標題:Ctrl/Command + Shift + H
無序列表:Ctrl/Command + Shift + U
有序列表:Ctrl/Command + Shift + O
檢查列表:Ctrl/Command + Shift + C
插入程式碼:Ctrl/Command + Shift + K
插入連結:Ctrl/Command + Shift + L
插入圖片:Ctrl/Command + Shift + G

合理的建立標題,有助於目錄的生成

直接輸入1次#,並按下space後,將生成1級標題。
輸入2次#,並按下space後,將生成2級標題。
以此類推,我們支援6級標題。有助於使用TOC語法後生成一個完美的目錄。

如何改變文字的樣式

強調文字 強調文字

加粗文字 加粗文字

標記文字

刪除文字

引用文字

H2O is是液體。

210 運算結果是 1024.

插入連結與圖片

連結: link.

圖片: Alt

帶尺寸的圖片: Alt

當然,我們為了讓使用者更加便捷,我們增加了圖片拖拽功能。

如何插入一段漂亮的程式碼片

部落格設定頁面,選擇一款你喜歡的程式碼片高亮樣式,下面展示同樣高亮的 程式碼片.

// An highlighted block var foo = 'bar'; 

生成一個適合你的列表

  • 專案
    • 專案
      • 專案
  1. 專案1
  2. 專案2
  3. 專案3
  • 計劃任務
  • 完成任務

建立一個表格

一個簡單的表格是這麼建立的:

專案 Value
電腦 $1600
手機 $12
導管 $1

設定內容居中、居左、居右

使用:---------:居中
使用:----------居左
使用----------:居右

第一列 第二列 第三列
第一列文字居中 第二列文字居右 第三列文字居左

SmartyPants

SmartyPants將ASCII標點字元轉換為“智慧”印刷標點HTML實體。例如:

TYPE ASCII HTML
Single backticks 'Isn't this fun?' ‘Isn’t this fun?’
Quotes "Isn't this fun?" “Isn’t this fun?”
Dashes -- is en-dash, --- is em-dash – is en-dash, — is em-dash

建立一個自定義列表

Markdown
Text-to- HTML conversion tool
Authors
John
Luke

如何建立一個註腳

一個具有註腳的文字。2

註釋也是必不可少的

Markdown將文字轉換為 HTML

KaTeX數學公式

您可以使用渲染LaTeX數學表示式 KaTeX:

Gamma公式展示 Γ ( n ) = ( n 1 ) ! n N \Gamma(n) = (n-1)!\quad\forall n\in\mathbb N 是通過尤拉積分

Γ ( z ) = 0 t z 1 e t d t   . \Gamma(z) = \int_0^\infty t^{z-1}e^{-t}dt\,.

你可以找到更多關於的資訊 LaTeX 數學表示式here.

新的甘特圖功能,豐富你的文章

gantt
        dateFormat  YYYY-MM-DD
        title Adding GANTT diagram functionality to mermaid
        section 現有任務
        已完成               :done,    des1, 2014-01-06,2014-01-08
        進行中               :active,  des2, 2014-01-09, 3d
        計劃一               :         des3, after des2, 5d
        計劃二               :         des4, after des3, 5d
  • 關於 甘特圖 語法,參考 這兒,

UML 圖表

可以使用UML圖表進行渲染。 Mermaid. 例如下面產生的一個序列圖::

這將產生一個流程圖。:

  • 關於 Mermaid 語法,參考 這兒,

FLowchart流程圖

我們依舊會支援flowchart的流程圖:

  • 關於 Flowchart流程圖 語法,參考 這兒.

匯出與匯入

匯出

如果你想嘗試使用此編輯器, 你可以在此篇文章任意編輯。當你完成了一篇文章的寫作, 在上方工具欄找到 文章匯出 ,生成一個.md檔案或者.html檔案進行本地儲存。

匯入

如果你想載入一篇你寫過的.md檔案或者.html檔案,在上方工具欄可以選擇匯入功能進行對應副檔名的檔案匯入,
繼續你的創作。


  1. mermaid語法說明 ↩︎

  2. 註腳的解釋 ↩︎