c++tcp接收文件缓存多大合适_网易面经:深剖TCP协议的流量控制和拥塞控制,你懂了吗?...
1.自我介紹+項目
2.RPC框架和普通http有什么區別和優勢? 基于Tcp封裝還是http封裝的
3.rpc是長連接嗎?如果要傳輸一個特別大的文件 底層還是基于流嗎? Nio是一個什么IO模型?
4.github了的watch star fork
5.異常和error的區別,oom是error還是異常?什么東西分配在堆上和棧上?
6.只對堆進行gc 這句話對不對 調用system.gc()馬上就執行gc嗎
7.缺頁中斷 分頁地址轉換 內存抖動
8.linux的fork指令對數據的拷貝是馬上就拷貝的嗎?
9.linux看網絡狀況用什么 看日志用什么?
10.擁塞控制 以及里面的算法?流量控制的協議
11.Ping命令做了什么?基于那一個層?ping是哪一個層的?
12.Mysql和Redis最大的區別? MyISAM和InnoDB的區別?
13.Redis 的實現。為什么這么高性能 ?set kv鍵值對進去的時候,kv鍵值的長度是不一樣的 你覺得底層的數據結構是一樣的嗎? 持久化的策略 長久下來aof文件會很大 怎么辦?InnoDB行鎖的分類 (其實就是排他鎖和共享鎖)
14.Select from update 是什么效果?事務你平常是怎么處理的?
15.兩個隊列實現一個棧、圓里均勻地生成點(極坐標系)
16.ps命令的底層實現?
17.類加載器
2. RPC框架和普通http有什么區別和優勢? 基于Tcp封裝還是http封裝的
RPC是一種遠程過程調用的協議,使用這種協議向另一臺計算機上的程序請求服務,不需要了解底層網絡技術的協議。
在 RPC 中,發出請求的程序是客戶程序,而提供服務的程序是服務器。
HTTP是一種超文本傳輸協議。是WWW瀏覽器和WWW服務器之間的應用層通訊協議。
RPC協議與HTTP協議的區別
1、RPC是一種API,HTTP是一種無狀態的網絡協議。RPC可以基于HTTP協議實現,也可以直接在TCP協議上實現。
2、RPC主要是用在大型網站里面,因為大型網站里面系統繁多,業務線復雜,而且效率優勢非常重要的一塊,這個時候RPC的優勢就比較明顯了。
HTTP主要是用在中小型企業里面,業務線沒那么繁多的情況下。
3、HTTP開發方便簡單、直接。開發一個完善的RPC框架難度比較大。
4、HTTP發明的初衷是為了傳送超文本的資源,協議設計的比較復雜,參數傳遞的方式效率也不高。開源的RPC框架針對遠程調用協議上的效率會比HTTP快很多。
5、HTTP需要事先通知,修改Nginx/HAProxy配置。RPC能做到自動通知,不影響上游。
6、HTTP大部分是通過Json來實現的,字節大小和序列化耗時都比Thrift要更消耗性能。RPC,可以基于Thrift實現高效的二進制傳輸。
3. github了的watch star fork
Watch
Watch翻譯過來可以稱之為觀察。
默認每一個用戶都是處于Not watching的狀態,當你選擇Watching,表示你以后會關注這個項目的所有動態,以后只要這個項目發生變動,如被別人提交了pull request、被別人發起了issue等等情況,你都會在自己的個人通知中心,收到一條通知消息,如果你設置了個人郵箱,那么你的郵箱也可能收到相應的郵件。
如果你不想接受這些通知,那么點擊 Not Watching 即可。
Star
Star 翻譯過來應該是星星,這里解釋為`關注`或者`點贊`更合適,當你點擊 Star,表示你喜歡這個項目或者通俗點,可以把他理解成朋友圈的點贊吧,表示對這個項目的支持。
不過相比朋友圈的點贊,github 里面會有一個列表,專門收集了你所有 start 過的項目,點擊 github 個人頭像,可以看到 your star的條目,點擊就可以查看你 star 過的所有項目了。
不過,在你的 star 列表很容易出現這樣的問題。就是你可能 star 成百上千個項目怎么辦。這時,如果 github 可以提供一個分類功能該多好,就像微博網頁版的收藏,你在收藏的時候可以設置 tag這樣設置的好處是,以后再次查找項目時,可以根據歸類查找。
Fork
當選擇 fork,相當于你自己有了一份原項目的拷貝,當然這個拷貝只是針對當時的項目文件,如果后續原項目文件發生改變,你必須通過其他的方式去同步。
一般來說,我們不需要使用 fork 這個功能,至少我一般不會用,除非有一些項目,可能存在 bug 或者可以繼續優化的地方,你想幫助原項目作者去完善這個項目,那么你可以 fork 一份項目下來,然后自己對這個項目進行修改完善,當你覺得項目沒問題了,你就可以嘗試發起 pull request給原項目作者了,然后就靜靜等待他的 merge。
當前很多人錯誤的在使用 fork。很多人把 fork 當成了收藏一樣的功能,包括一開始使用 github 的我,每次看到一個好的項目就先 fork,因為這樣,就可以我的 repository(倉庫)列表下查看 fork 的項目了。其實你完全可以使用 star 來達到這個目的。
7. 缺頁中斷 分頁地址轉換 內存抖動
一、什么是缺頁中斷?
進程線性地址空間里的頁面不必常駐內存,在執行一條指令時,如果發現他要訪問的頁沒有在內存中(即存在位為0),那么停止該指令的執行,并產生一個頁不存在的異常,對應的故障處理程序可通過從外存加載該頁的方法來排除故障,之后,原先引起的異常的指令就可以繼續執行,而不再產生異常。
二、頁面調度算法
將新頁面調入內存時,如果內存中所有的物理頁都已經分配出去,就按照某種策略來廢棄整個頁面,將其所占據的物理頁釋放出來;
三、查看進程發生缺頁中斷的次數
ps -o majflt,minflt -C program查看
majflt和minflt表示一個進程自啟動以來所發生的缺頁中斷的次數;
四、產生缺頁中斷的幾種情況
1、當內存管理單元(MMU)中確實沒有創建虛擬物理頁映射關系,并且在該虛擬地址之后再沒有當前進程的線性區(vma)的時候,可以肯定這是一個編碼錯誤,這將殺掉該進程;
2、當MMU中確實沒有創建虛擬頁物理頁映射關系,并且在該虛擬地址之后存在當前進程的線性區vma的時候,這很可能是缺頁中斷,并且可能是棧溢出導致的缺頁中斷;
3、當使用malloc/mmap等希望訪問物理空間的庫函數/系統調用后,由于linux并未真正給新創建的vma映射物理頁,此時若先進行寫操作,將和2產生缺頁中斷的情況一樣;若先進行讀操作雖然也會產生缺頁異常,將被映射給默認的零頁,等再進行寫操作時,仍會產生缺頁中斷,這次必須分配1物理頁了,進入寫時復制的流程;
4、當使用fork等系統調用創建子進程時,子進程不論有無自己的vma,它的vma都有對于物理頁的映射,但它們共同映射的這些物理頁屬性為只讀,即linux并未給子進程真正分配物理頁,當父子進程任何一方要寫相應物理頁時,導致缺頁中斷的寫時復制;
什么是內存抖動(顛簸)
本質是頻繁的頁調度行為頻繁的頁調度,進程不斷產生缺頁中斷置換一個頁,又不斷再次需要這個頁
運行程序太多;頁面替換策略不好。通過殺掉一些無關的進程(終止進程)或者增加物理內存解決
10.擁塞控制 以及里面的算法?流量控制的協議
TCP如何實現一個靠譜的協議
為了保證順序性,每個包都有一個ID。在建立連接的時候,商定起始的ID。然后按照ID一個一個發送。
收到包的一端需要對包做應答,一旦應答一個ID,就表明之前的ID都收到了,這個模式稱為累計確認或者累計應答。
為了記錄發送和接收的包,TCP在發送端和接收端都緩存記錄。
發送端緩存結構
發送端的緩存按照包的ID一個個排列,分成4個部分:
(一)發送并且已經確認的
(二)發送了并且尚未確認的
(三)沒有發送,但是已經確認要發送在等待的
(四)沒有發送,并且暫時不會發送的
第三和第四部分區分開是為了流量控制,流量控制的依據是什么?TCP里接收端會給發送端報告一個窗口大小,叫Advertised Window。發送端需要保證上面第二和第三部分的長度加起來等于Advertised Window。
tcp發送端緩存結構
接收端緩存結構
接收端的緩存分成三個部分:
(一)接受并且確認過的
(二)還沒接收,但是馬上就能接收的,要等空格填滿
(三)還沒接收,也沒法接收的,也就是超過工作量(max buffer)的部分
tcp接收端緩存結構
MaxRcvBuffer:最大緩存的量
LastByteRead之后是已經接收了,但是還沒被應用層消耗
NextByteExpected之后是等待接收的
Advertised Window其實就是等待接收未確認部分的大小。其中這部分中有可能是有空擋的,比如7到14有,但6是空的。那NextByteExpected就只能待在這個位置了。
TCP的確認和重發機制
發送端在發出一個包后,會設置一個定時器,超過一定時間沒收到ACK,就會把這個包重發。應該如何評估這個時間的大小呢?這個時間不能太短,必須大于正常的一次往返的時間(RTT)。也不能太長,太長了發送的速度就太慢了。
所以最關鍵的是估算一個平均RTT,所以TCP需要不斷地采樣RTT,還要根據RTT的波動范圍,做加權平均算出一個值來。由于重傳時間是不斷變化的,稱為自適應重傳算法。
快速重傳算法
以上算法的問題是超時時間會越加越長,所以有一個快速重傳的機制。當接收方收到一個序號大于下一個期望的報文段時,就是說接收緩存有了空格,那還是發送原來的ACK,比如我在等6,這時候收到7,我ACK 5。然后又收到8和9,我還都是ack 5。這樣發送端接連收到3個ACK,發送端收到后,會馬上重發6,不再等超時。這里還是有一個問題,發送端這時候可能已經發到20了,收到的3個ACK只能知道6沒收到,7,8,9有沒有收到不知道,只能把6之后的全部重發一遍。
Selctive Acknowledgment(SACK)
還有一種重傳的方式稱為SACK,這種方式是在TCP頭里加一個SACK的東西,將緩存地圖放進去,比如還是7丟了,可以地圖是ACK6,SACK8,SACK9。發送方收到后,立馬能看出是7丟了。Linux下面可以通過tcp_ack參數打開這個功能。
sack示意圖
這里還是有一個問題,就是SACK不是最終保證,就是說接收端在發送SACK后是可以把數據再丟了的。雖然這么做不鼓勵,但是不排除極端情況,比如內存不夠了。所以發送端不能想當然的再也不發SACK的那些包了,還是要看這些包有沒有正式的ACK才能最終確認。
Duplicate SACK(D-SACK)
D-SACK的作用是接收端告訴發送端包發重了。比如ACK 6丟了,那么發送端會重發,接收端發現收到兩個6,現在我都要ACK 8了,則回復ACK 8,SACK 6。
引入D-SACK,有這么幾個好處:
1.讓發送方知道,是發的包丟了還是對端回的ACK丟了
2.是不是自己的timeout設置小了,導致重傳了
3.網絡上出現先發后到的情況
4.網絡上有可能把我的包復制了
5.基于以上的認知,可以更好的做流控。Linux下使用參數tcp_dsack開啟這個功能。
RTT算法策略
以上提到重傳的Timeout很重要,這個超時時間在不同的網絡的情況下,根本沒有辦法設置一個死的值。只能動態地設置。 為了動態地設置,TCP引入了RTT——Round Trip Time,也就是一個數據包從發出去到回來的時間。這樣發送端就大約知道需要多少的時間,從而可以方便地設置Timeout——RTO(Retransmission TimeOut),以讓我們的重傳機制更高效。
經典算法
首先采樣最近幾次的RTT,然后做平滑計算,算法叫加權移動平均。公式如下:(α取值在0.8到0.9之間)
SRTT = ( α * SRTT ) + ((1- α) * RTT)
然后計算RTO,公式如下
RTO = min [ UBOUND, max [ LBOUND, (β * SRTT) ] ]
其中:
UBOUND是最大的timeout時間,上限值
LBOUND是最小的timeout時間,下限值
β 值一般在1.3到2.0之間。
Karn / Partridge 算法
經典算法的問題就是RTT在有重傳的時候怎么算?是用第一次發的時間算做開始時間還是重傳的時間作為開始時間。比如下面這樣:
RTT計算
所以為了避免這個坑,搞了一個Karn / Partridge 算法,這個算法的最大特點是——忽略重傳,不把重傳的RTT做采樣。這樣有一個問題就是突然有一段時間重傳很多,如果都不算的話RTT就一直是原來的值,顯然這個值已經不合適了。所以,算法想了一個辦法,只要一發生重傳,RTO翻倍。
Jacobson / Karels 算法
以上兩種算法的問題就是RTT容易被平均掉,不能很好的應對突發情況。新的算法的公式如下:
SRTT = SRTT + α (RTT – SRTT) —— 計算平滑RTT
DevRTT = (1-β)DevRTT + β(|RTT-SRTT|) ——計算平滑RTT和真實的差距(加權移動平均)
RTO= μ * SRTT + ? *DevRTT —— 神一樣的公式
(其中:在Linux下,α = 0.125,β = 0.25, μ = 1,? = 4 ——這就是算法中的“調得一手好參數”,nobody knows why, it just works…) 這個算法就是今天的TCP協議中用的算法。
RTO終于被算出來了,接下來就是兩個最重要的窗口了。
流量控制
上面講到TCP在包的ACK中會攜帶一個窗口大小,發送方就可以做一個滑動窗口了(簡稱rwnd)。每收到一個ACK就把窗口往右移動一個,也就是從未發送待發送中取一個發出去,然后從不可發送中取一個出來放到待發送里面。
如果接收方下次來的ack中帶的窗口大小變小,則說明接收方處理不過來了,那發送方就不能將窗口右移了,而是要將窗口變小。最后如果窗口變成0,發送端窗口就變成0,不會再發了。這個時候發送方會一直發送窗口探測數據包ZWP(Zero Window Probe),看是否有機會調整窗口的大小。一般會探測3次,每次間隔30-60s,當然不同的實現可能配置不一樣。
SockStress攻擊
有等待就有攻擊,利用ZWP的攻擊方式就是,客戶端連上服務端后就把窗口設置成0,然后服務端就等,然后ZWP。等的多了自然資源就耗盡了,這種攻擊叫做SockStress。
Silly Window Syndrome (糊涂窗口綜合征)
在接收端將窗口調整成0后,如果這個時候應用消耗了一個包,那窗口會變成1,如果這時候發送端立馬發送一個包會發生什么?TCP+IP的頭加起來有40字節,如果為了幾個字節直接發送的話主要的工作都消耗在發頭上了,這是一個問題。
還有就是網絡里有個MTU的概念,就是一次發送的包大小,以太網是1500字節,出去TCP+IP的40字節,本來可以用1460字節而只發幾個字節,那就是浪費帶寬。這個1460就是俗稱的MSS(Max Segment Size)。以上的表現就被稱為糊涂窗口綜合征。
發送端和接收端是怎么來處理這種病的呢?
如果是接收端造成窗口是0,一旦緩沖區開始有地方了,接收端不會立馬發送一個窗口大小1給發送端,而是會等窗口達到一定大小,比如緩沖區一半為空或者空間大于MSS了,才會更新窗口大小,在此之前還是一直回答0。
如果是發送端造成包比較小,那就是發送端負責攢數據,他有兩個主要的條件:1)要等到 Window Size>=MSS 或是 Data Size >=MSS,2)收到之前發送數據的ack回包,他才會發數據。這個就是著名的 Nagle’s algorithm 。
Nagle算法默認是打開的,所以,對于一些需要小包場景的程序——比如像telnet或ssh這樣的交互性比較強的程序,你需要關閉這個算法。你可以在Socket設置TCP_NODELAY選項來關閉這個算法。還有一個類似的參數TCP_CORK,其實是更激進的Nagle算法,完全禁止小包發送,而Nagle算法沒有禁止小包發送,只是禁止了大量的小包發送。所以要分清楚這兩個參數。
以上就是TCP的流量控制策略。
擁塞控制
流量控制通過滑動窗口來實現,但是rwnd窗口只考慮了發送端和接收端的問題,沒考慮網絡的問題。有可能接收端很快,但是網絡擁塞了,所以加了一個擁塞窗口(cwnd)。擁塞窗口的意思就是一次性可以連續提交多少個包到網絡中。最終的形態是LastByteSent-LastByteAcked<=min(cwnd,rwnd),由兩個窗口共同控制發送速度。
TCP的擁塞控制主要避免兩種現象,包丟失和包重傳。網絡的帶寬是固定的,當發送端發送速度超過帶寬后,中間設備處理不完多出來的包就會被丟棄,這就是包丟失。如果我們在中間設備上加上緩存,處理不過來的包就會被加到緩存隊列中,不會丟失,但是會增加時延。如果時延到達一定的程度,就會超時重傳,這就是包重傳。
擁塞控制主要是四個算法:1)慢啟動,2)擁塞避免,3)擁塞發生,4)快速恢復
慢啟動
擁塞窗口(cwnd)的大小應該怎么設置呢?一個TCP連接,開始的時候cwnd設置成一個報文段(MSS),一次只能發送一個;當收到ACK后則cwnd++;如果ACK正常收到,每當過了一個RTT則翻倍,以指數增長。如果網速很快的話增長速度還是很可觀的。
慢啟動
[注] TCP的實現中cwnd并不都是從1個MSS開始的,Linux 3.0后依據google的論文《An Argument for Increasing TCP’s Initial Congestion Window》,初始化cwnd從10個MSS開始。Linux 3.0之前,cwnd是跟MSS的值來變的,如果MSS< 1095,則cwnd = 4;如果MSS>2190,則cwnd=2;其它情況下,則是3。
擁塞避免
cwnd一直漲下去不是辦法,要設置一個限制。當漲到一次發送超過ssthresh(65535個字節),就會減速,改成每次加1/ cwnd,比如之前一次發送cwnd是10個MSS,現在每次收到一個確認cwnd加1/10個MSS,每過一個RTT加1個。這樣一直加下去知道擁塞出現,比如包丟失。
擁塞發生時
前面雖然超過ssthresh時會減速,但是還是在漲,早晚會產生擁塞的。這時候有兩種處理方式,1)一旦超時重傳出現,則把ssthresh改成cwnd/2,cwnd窗口改成1,重新從頭開始慢啟動。2)還有一種情況就是收到接收端SACK要求重傳,這種TCP認為不嚴重,ssthresh改成cwnd/2,cwnd降為cwnd/2+3進入快速恢復算法。
快速恢復
接著上一段擁塞發生的第二種情況,快速恢復算法的邏輯如下:
cwnd = sshthresh + 3 * MSS (3的意思是確認有3個數據包被收到了)
重傳Duplicated ACKs指定的數據包
如果再收到 duplicated Acks,那么cwnd = cwnd +1
如果收到了新的Ack,那么,cwnd = sshthresh ,然后就進入了擁塞避免的算法了。
其他的恢復算法有FACK,TCP Vegas等。
存在問題
擁塞控制用以上的方法控制窗口的大小有兩個問題:
1)丟包不代表通道滿了,也有可能是網絡本來就有問題,所以這個時候收縮時不對的
2)等到發生丟包再收縮,其實已經晚了,應該在剛好用滿時就不再加了
基于以上兩個問題,又出現了TCP BBR擁塞算法。
最近有點懶,今天就不手撕代碼。希望往期文檔的猿猿們,私信給我。
總結
以上是生活随笔為你收集整理的c++tcp接收文件缓存多大合适_网易面经:深剖TCP协议的流量控制和拥塞控制,你懂了吗?...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 蒸木瓜的功效与作用、禁忌和食用方法
- 下一篇: 模拟器显示空白图片_Kawaks街机模拟