大家好,我是小林。
今天有讀者給我發(fā)了他 8 月份面騰訊的面經(jīng),被問到的問題還挺多的。
操作系統(tǒng)和網(wǎng)絡(luò)面試整個(gè)面試 60%,剩下40%是 Java+項(xiàng)目的內(nèi)容(讀者的技術(shù)棧是 Java 方向)。
這次,我主要是截取操作系統(tǒng)和網(wǎng)絡(luò)相關(guān)的問題給大家解析一波。
騰訊面試問題
操作系統(tǒng)
單核可以多線程嗎?
可以的。
單核創(chuàng)建了多線程,CPU 會(huì)從一個(gè)進(jìn)程快速切換至另一個(gè)進(jìn)程,其間每個(gè)進(jìn)程各運(yùn)行幾十或幾百個(gè)毫秒,雖然單核的 CPU 在某一個(gè)瞬間,只能運(yùn)行一個(gè)進(jìn)程。但在 1 秒鐘期間,它可能會(huì)運(yùn)行多個(gè)進(jìn)程,這樣就產(chǎn)生并行的錯(cuò)覺,實(shí)際上這是并發(fā)。
并發(fā)與并行
虛擬地址怎么找到對(duì)應(yīng)的內(nèi)容的?
操作系統(tǒng)內(nèi)存管理方式主要兩種,不同的管理方式,尋址的實(shí)現(xiàn)是不同的:
- 內(nèi)存分段:將進(jìn)程的虛擬地址空間劃分為多個(gè)不同大小的段,每個(gè)段對(duì)應(yīng)一個(gè)邏輯單位,如代碼段、數(shù)據(jù)段、堆段和棧段。每個(gè)段的大小可以根據(jù)需要進(jìn)行調(diào)整,使得不同段可以按需分配和釋放內(nèi)存。虛擬內(nèi)存分段的優(yōu)點(diǎn)是可以更好地管理不同類型的數(shù)據(jù),但是由于段的大小不一致,容易產(chǎn)生外部碎片。內(nèi)存分頁(yè):將進(jìn)程的虛擬地址空間劃分為固定大小的頁(yè),同時(shí)將物理內(nèi)存也劃分為相同大小的頁(yè)框。通過頁(yè)表將虛擬地址映射到物理地址,并且可以按需加載和釋放頁(yè)。虛擬內(nèi)存分頁(yè)的優(yōu)點(diǎn)是可以更好地利用物理內(nèi)存空間,但是可能會(huì)產(chǎn)生內(nèi)部碎片。
分段的尋址方式
分段機(jī)制下的虛擬地址由兩部分組成,段選擇因子和段內(nèi)偏移量。
img
段選擇因子和段內(nèi)偏移量:
段選擇子
-
- 就保存在段寄存器里面。段選擇子里面最重要的是
段號(hào)
-
- ,用作段表的索引。
段表
-
- 里面保存的是這個(gè)
段的基地址、段的界限和特權(quán)等級(jí)
-
- 等。虛擬地址中的
段內(nèi)偏移量
- 應(yīng)該位于 0 和段界限之間,如果段內(nèi)偏移量是合法的,就將段基地址加上段內(nèi)偏移量得到物理內(nèi)存地址。
在上面,知道了虛擬地址是通過段表與物理地址進(jìn)行映射的,分段機(jī)制會(huì)把程序的虛擬地址分成 4 個(gè)段,每個(gè)段在段表中有一個(gè)項(xiàng),在這一項(xiàng)找到段的基地址,再加上偏移量,于是就能找到物理內(nèi)存中的地址,如下圖:
img
如果要訪問段 3 中偏移量 500 的虛擬地址,我們可以計(jì)算出物理地址為,段 3 基地址 7000 + 偏移量 500 = 7500。
分段的辦法很好,解決了程序本身不需要關(guān)心具體的物理內(nèi)存地址的問題,但它也有一些不足之處:
-
- 第一個(gè)就是
內(nèi)存碎片
-
- 的問題。第二個(gè)就是
內(nèi)存交換的效率低
- 的問題。
分頁(yè)的尋址方式
虛擬地址與物理地址之間通過頁(yè)表來(lái)映射,如下圖:
img
頁(yè)表是存儲(chǔ)在內(nèi)存里的,內(nèi)存管理單元 (MMU)就做將虛擬內(nèi)存地址轉(zhuǎn)換成物理地址的工作。
而當(dāng)進(jìn)程訪問的虛擬地址在頁(yè)表中查不到時(shí),系統(tǒng)會(huì)產(chǎn)生一個(gè)缺頁(yè)異常,進(jìn)入系統(tǒng)內(nèi)核空間分配物理內(nèi)存、更新進(jìn)程頁(yè)表,最后再返回用戶空間,恢復(fù)進(jìn)程的運(yùn)行。
在分頁(yè)機(jī)制下,虛擬地址分為兩部分,頁(yè)號(hào)和頁(yè)內(nèi)偏移。頁(yè)號(hào)作為頁(yè)表的索引,頁(yè)表包含物理頁(yè)每頁(yè)所在物理內(nèi)存的基地址,這個(gè)基地址與頁(yè)內(nèi)偏移的組合就形成了物理內(nèi)存地址,見下圖。
img
總結(jié)一下,對(duì)于一個(gè)內(nèi)存地址轉(zhuǎn)換,其實(shí)就是這樣三個(gè)步驟:
- 把虛擬內(nèi)存地址,切分成頁(yè)號(hào)和偏移量;根據(jù)頁(yè)號(hào),從頁(yè)表里面,查詢對(duì)應(yīng)的物理頁(yè)號(hào);直接拿物理頁(yè)號(hào),加上前面的偏移量,就得到了物理內(nèi)存地址。
下面舉個(gè)例子,虛擬內(nèi)存中的頁(yè)通過頁(yè)表映射為了物理內(nèi)存中的頁(yè),如下圖:
img
32位 4G 執(zhí)行2G的東西,虛擬內(nèi)存會(huì)有什么變化呢?
應(yīng)用程序通過 malloc 函數(shù)申請(qǐng)內(nèi)存的時(shí)候,實(shí)際上申請(qǐng)的是虛擬內(nèi)存,此時(shí)并不會(huì)分配物理內(nèi)存。
當(dāng)應(yīng)用程序讀寫了這塊虛擬內(nèi)存,CPU 就會(huì)去訪問這個(gè)虛擬內(nèi)存, 這時(shí)會(huì)發(fā)現(xiàn)這個(gè)虛擬內(nèi)存沒有映射到物理內(nèi)存, CPU 就會(huì)產(chǎn)生缺頁(yè)中斷,進(jìn)程會(huì)從用戶態(tài)切換到內(nèi)核態(tài),并將缺頁(yè)中斷交給內(nèi)核的 Page Fault Handler (缺頁(yè)中斷函數(shù))處理。
缺頁(yè)中斷處理函數(shù)會(huì)看是否有空閑的物理內(nèi)存:
- 如果有,就直接分配物理內(nèi)存,并建立虛擬內(nèi)存與物理內(nèi)存之間的映射關(guān)系。如果沒有空閑的物理內(nèi)存,那么內(nèi)核就會(huì)開始進(jìn)行回收內(nèi)存的工作,比如會(huì)進(jìn)行 swap 機(jī)制。
什么是 Swap 機(jī)制?
當(dāng)系統(tǒng)的物理內(nèi)存不夠用的時(shí)候,就需要將物理內(nèi)存中的一部分空間釋放出來(lái),以供當(dāng)前運(yùn)行的程序使用。那些被釋放的空間可能來(lái)自一些很長(zhǎng)時(shí)間沒有什么操作的程序,這些被釋放的空間會(huì)被臨時(shí)保存到磁盤,等到那些程序要運(yùn)行時(shí),再?gòu)拇疟P中恢復(fù)保存的數(shù)據(jù)到內(nèi)存中。
另外,當(dāng)內(nèi)存使用存在壓力的時(shí)候,會(huì)開始觸發(fā)內(nèi)存回收行為,會(huì)把這些不常訪問的內(nèi)存先寫到磁盤中,然后釋放這些內(nèi)存,給其他更需要的進(jìn)程使用。再次訪問這些內(nèi)存時(shí),重新從磁盤讀入內(nèi)存就可以了。
這種,將內(nèi)存數(shù)據(jù)換出磁盤,又從磁盤中恢復(fù)數(shù)據(jù)到內(nèi)存的過程,就是 Swap 機(jī)制負(fù)責(zé)的。
Swap 就是把一塊磁盤空間或者本地文件,當(dāng)成內(nèi)存來(lái)使用,它包含換出和換入兩個(gè)過程:
換出(Swap Out)
-
- ,是把進(jìn)程暫時(shí)不用的內(nèi)存數(shù)據(jù)存儲(chǔ)到磁盤中,并釋放這些數(shù)據(jù)占用的內(nèi)存;
換入(Swap In)
- ,是在進(jìn)程再次訪問這些內(nèi)存的時(shí)候,把它們從磁盤讀到內(nèi)存中來(lái);
Swap 換入換出的過程如下圖:
img
使用 Swap 機(jī)制優(yōu)點(diǎn)是,應(yīng)用程序?qū)嶋H可以使用的內(nèi)存空間將遠(yuǎn)遠(yuǎn)超過系統(tǒng)的物理內(nèi)存。由于硬盤空間的價(jià)格遠(yuǎn)比內(nèi)存要低,因此這種方式無(wú)疑是經(jīng)濟(jì)實(shí)惠的。當(dāng)然,頻繁地讀寫硬盤,會(huì)顯著降低操作系統(tǒng)的運(yùn)行速率,這也是 Swap 的弊端。
內(nèi)核態(tài)和用戶態(tài)的區(qū)別是什么?
內(nèi)核態(tài)和用戶態(tài)是操作系統(tǒng)中的兩種不同的執(zhí)行模式。
內(nèi)核態(tài)是操作系統(tǒng)運(yùn)行在特權(quán)級(jí)別最高的模式下的狀態(tài),它具有對(duì)系統(tǒng)資源的完全控制權(quán)。在內(nèi)核態(tài)下,操作系統(tǒng)可以執(zhí)行特權(quán)指令,訪問所有的內(nèi)存和設(shè)備,以及執(zhí)行關(guān)鍵的系統(tǒng)操作。內(nèi)核態(tài)下運(yùn)行的代碼通常是操作系統(tǒng)內(nèi)核或驅(qū)動(dòng)程序。
用戶態(tài)是應(yīng)用程序運(yùn)行的一種模式,它運(yùn)行在較低的特權(quán)級(jí)別下。在用戶態(tài)下,應(yīng)用程序只能訪問有限的系統(tǒng)資源,不能直接執(zhí)行特權(quán)指令或訪問內(nèi)核級(jí)別的數(shù)據(jù)。用戶態(tài)下運(yùn)行的代碼通常是應(yīng)用程序或用戶進(jìn)程。
內(nèi)核態(tài)和用戶態(tài)的區(qū)別在于權(quán)限和資源訪問的限制。內(nèi)核態(tài)具有更高的權(quán)限和更廣泛的資源訪問能力,而用戶態(tài)受到限制,只能訪問有限的資源。操作系統(tǒng)通過將關(guān)鍵的操作和資源保護(hù)在內(nèi)核態(tài)下來(lái)確保系統(tǒng)的安全性和穩(wěn)定性。用戶程序通過系統(tǒng)調(diào)用的方式向操作系統(tǒng)請(qǐng)求服務(wù)或資源,并在用戶態(tài)下執(zhí)行,以提供更高的隔離性和安全性。
網(wǎng)絡(luò)協(xié)議
http常見響應(yīng)碼有哪些?
HTTP 狀態(tài)碼分為 5 大類:1XX:表示消息狀態(tài)碼;2XX:表示成功狀態(tài)碼;3XX:表示重定向狀態(tài)碼;4XX:表示客戶端錯(cuò)誤狀態(tài)碼;5XX:表示服務(wù)端錯(cuò)誤狀態(tài)碼。
五大類 HTTP 狀態(tài)碼
其中常見的具體狀態(tài)碼有:200:請(qǐng)求成功;301:永久重定向;302:臨時(shí)重定向;404:無(wú)法找到此頁(yè)面;405:請(qǐng)求的方法類型不支持;500:服務(wù)器內(nèi)部出錯(cuò)。
http各個(gè)版本的特性?
HTTP/1.1 相比 HTTP/1.0 性能上的改進(jìn):
- 使用長(zhǎng)連接的方式改善了 HTTP/1.0 短連接造成的性能開銷。支持管道(pipeline)網(wǎng)絡(luò)傳輸,只要第一個(gè)請(qǐng)求發(fā)出去了,不必等其回來(lái),就可以發(fā)第二個(gè)請(qǐng)求出去,可以減少整體的響應(yīng)時(shí)間。
但 HTTP/1.1 還是有性能瓶頸:
-
- 請(qǐng)求 / 響應(yīng)頭部(Header)未經(jīng)壓縮就發(fā)送,首部信息越多延遲越大。只能壓縮
Body
- 的部分;發(fā)送冗長(zhǎng)的首部。每次互相發(fā)送相同的首部造成的浪費(fèi)較多;服務(wù)器是按請(qǐng)求的順序響應(yīng)的,如果服務(wù)器響應(yīng)慢,會(huì)招致客戶端一直請(qǐng)求不到數(shù)據(jù),也就是隊(duì)頭阻塞;沒有請(qǐng)求優(yōu)先級(jí)控制;請(qǐng)求只能從客戶端開始,服務(wù)器只能被動(dòng)響應(yīng)。
HTT/1 ~ HTTP/2
HTTP/2 相比 HTTP/1.1 性能上的改進(jìn):
- 頭部壓縮二進(jìn)制格式并發(fā)傳輸服務(wù)器主動(dòng)推送資源
1. 頭部壓縮
HTTP/2 會(huì)壓縮頭(Header)如果你同時(shí)發(fā)出多個(gè)請(qǐng)求,他們的頭是一樣的或是相似的,那么,協(xié)議會(huì)幫你消除重復(fù)的部分。
這就是所謂的 HPACK
算法:在客戶端和服務(wù)器同時(shí)維護(hù)一張頭信息表,所有字段都會(huì)存入這個(gè)表,生成一個(gè)索引號(hào),以后就不發(fā)送同樣字段了,只發(fā)送索引號(hào),這樣就提高速度了。
2. 二進(jìn)制格式
HTTP/2 不再像 HTTP/1.1 里的純文本形式的報(bào)文,而是全面采用了二進(jìn)制格式,頭信息和數(shù)據(jù)體都是二進(jìn)制,并且統(tǒng)稱為幀(frame):頭信息幀(Headers Frame)和數(shù)據(jù)幀(Data Frame)。
HTTP/1 與 HTTP/2
這樣雖然對(duì)人不友好,但是對(duì)計(jì)算機(jī)非常友好,因?yàn)橛?jì)算機(jī)只懂二進(jìn)制,那么收到報(bào)文后,無(wú)需再將明文的報(bào)文轉(zhuǎn)成二進(jìn)制,而是直接解析二進(jìn)制報(bào)文,這增加了數(shù)據(jù)傳輸的效率。
3. 并發(fā)傳輸
我們都知道 HTTP/1.1 的實(shí)現(xiàn)是基于請(qǐng)求-響應(yīng)模型的。同一個(gè)連接中,HTTP 完成一個(gè)事務(wù)(請(qǐng)求與響應(yīng)),才能處理下一個(gè)事務(wù),也就是說(shuō)在發(fā)出請(qǐng)求等待響應(yīng)的過程中,是沒辦法做其他事情的,如果響應(yīng)遲遲不來(lái),那么后續(xù)的請(qǐng)求是無(wú)法發(fā)送的,也造成了隊(duì)頭阻塞的問題。
而 HTTP/2 就很牛逼了,引出了 Stream 概念,多個(gè) Stream 復(fù)用在一條 TCP 連接。
img
從上圖可以看到,1 個(gè) TCP 連接包含多個(gè) Stream,Stream 里可以包含 1 個(gè)或多個(gè) Message,Message 對(duì)應(yīng) HTTP/1 中的請(qǐng)求或響應(yīng),由 HTTP 頭部和包體構(gòu)成。Message 里包含一條或者多個(gè) Frame,F(xiàn)rame 是 HTTP/2 最小單位,以二進(jìn)制壓縮格式存放 HTTP/1 中的內(nèi)容(頭部和包體)。
針對(duì)不同的 HTTP 請(qǐng)求用獨(dú)一無(wú)二的 Stream ID 來(lái)區(qū)分,接收端可以通過 Stream ID 有序組裝成 HTTP 消息,不同 Stream 的幀是可以亂序發(fā)送的,因此可以并發(fā)不同的 Stream ,也就是 HTTP/2 可以并行交錯(cuò)地發(fā)送請(qǐng)求和響應(yīng)。
比如下圖,服務(wù)端并行交錯(cuò)地發(fā)送了兩個(gè)響應(yīng):Stream 1 和 Stream 3,這兩個(gè) Stream 都是跑在一個(gè) TCP 連接上,客戶端收到后,會(huì)根據(jù)相同的 Stream ID 有序組裝成 HTTP 消息。
img
4、服務(wù)器推送
HTTP/2 還在一定程度上改善了傳統(tǒng)的「請(qǐng)求 - 應(yīng)答」工作模式,服務(wù)端不再是被動(dòng)地響應(yīng),可以主動(dòng)向客戶端發(fā)送消息。
客戶端和服務(wù)器雙方都可以建立 Stream, Stream ID 也是有區(qū)別的,客戶端建立的 Stream 必須是奇數(shù)號(hào),而服務(wù)器建立的 Stream 必須是偶數(shù)號(hào)。
比如下圖,Stream 1 是客戶端向服務(wù)端請(qǐng)求的資源,屬于客戶端建立的 Stream,所以該 Stream 的 ID 是奇數(shù)(數(shù)字 1);Stream 2 和 4 都是服務(wù)端主動(dòng)向客戶端推送的資源,屬于服務(wù)端建立的 Stream,所以這兩個(gè) Stream 的 ID 是偶數(shù)(數(shù)字 2 和 4)。
img
再比如,客戶端通過 HTTP/1.1 請(qǐng)求從服務(wù)器那獲取到了 HTML 文件,而 HTML 可能還需要依賴 CSS 來(lái)渲染頁(yè)面,這時(shí)客戶端還要再發(fā)起獲取 CSS 文件的請(qǐng)求,需要兩次消息往返,如下圖左邊部分:
img
如上圖右邊部分,在 HTTP/2 中,客戶端在訪問 HTML 時(shí),服務(wù)器可以直接主動(dòng)推送 CSS 文件,減少了消息傳遞的次數(shù)。
tcp擁塞控制介紹一下
在網(wǎng)絡(luò)出現(xiàn)擁堵時(shí),如果繼續(xù)發(fā)送大量數(shù)據(jù)包,可能會(huì)導(dǎo)致數(shù)據(jù)包時(shí)延、丟失等,這時(shí) TCP 就會(huì)重傳數(shù)據(jù),但是一重傳就會(huì)導(dǎo)致網(wǎng)絡(luò)的負(fù)擔(dān)更重,于是會(huì)導(dǎo)致更大的延遲以及更多的丟包,這個(gè)情況就會(huì)進(jìn)入惡性循環(huán)被不斷地放大....
所以,TCP 不能忽略網(wǎng)絡(luò)上發(fā)生的事,它被設(shè)計(jì)成一個(gè)無(wú)私的協(xié)議,當(dāng)網(wǎng)絡(luò)發(fā)送擁塞時(shí),TCP 會(huì)自我犧牲,降低發(fā)送的數(shù)據(jù)量。
于是,就有了擁塞控制,控制的目的就是避免「發(fā)送方」的數(shù)據(jù)填滿整個(gè)網(wǎng)絡(luò)。
為了在「發(fā)送方」調(diào)節(jié)所要發(fā)送數(shù)據(jù)的量,定義了一個(gè)叫做「擁塞窗口」的概念。
擁塞控制主要是四個(gè)算法:
- 慢啟動(dòng)擁塞避免擁塞發(fā)生快速恢復(fù)
慢啟動(dòng)
TCP 在剛建立連接完成后,首先是有個(gè)慢啟動(dòng)的過程,這個(gè)慢啟動(dòng)的意思就是一點(diǎn)一點(diǎn)的提高發(fā)送數(shù)據(jù)包的數(shù)量,如果一上來(lái)就發(fā)大量的數(shù)據(jù),這不是給網(wǎng)絡(luò)添堵嗎?
慢啟動(dòng)的算法記住一個(gè)規(guī)則就行:當(dāng)發(fā)送方每收到一個(gè) ACK,擁塞窗口 cwnd 的大小就會(huì)加 1。
這里假定擁塞窗口 cwnd
和發(fā)送窗口 swnd
相等,下面舉個(gè)栗子:
-
- 連接建立完成后,一開始初始化
cwnd = 1
-
- ,表示可以傳一個(gè)
MSS
- 大小的數(shù)據(jù)。當(dāng)收到一個(gè) ACK 確認(rèn)應(yīng)答后,cwnd 增加 1,于是一次能夠發(fā)送 2 個(gè)當(dāng)收到 2 個(gè)的 ACK 確認(rèn)應(yīng)答后, cwnd 增加 2,于是就可以比之前多發(fā)2 個(gè),所以這一次能夠發(fā)送 4 個(gè)當(dāng)這 4 個(gè)的 ACK 確認(rèn)到來(lái)的時(shí)候,每個(gè)確認(rèn) cwnd 增加 1, 4 個(gè)確認(rèn) cwnd 增加 4,于是就可以比之前多發(fā) 4 個(gè),所以這一次能夠發(fā)送 8 個(gè)。
慢啟動(dòng)算法的變化過程如下圖:
慢啟動(dòng)算法
可以看出慢啟動(dòng)算法,發(fā)包的個(gè)數(shù)是指數(shù)性的增長(zhǎng)。
那慢啟動(dòng)漲到什么時(shí)候是個(gè)頭呢?
有一個(gè)叫慢啟動(dòng)門限 ssthresh
(slow start threshold)狀態(tài)變量。
-
- 當(dāng)
cwnd
-
- <
ssthresh
-
- 時(shí),使用慢啟動(dòng)算法。當(dāng)
cwnd
-
- >=
ssthresh
- 時(shí),就會(huì)使用「擁塞避免算法」。
擁塞避免
當(dāng)擁塞窗口 cwnd
「超過」慢啟動(dòng)門限 ssthresh
就會(huì)進(jìn)入擁塞避免算法。
一般來(lái)說(shuō) ssthresh
的大小是 65535
字節(jié)。
那么進(jìn)入擁塞避免算法后,它的規(guī)則是:每當(dāng)收到一個(gè) ACK 時(shí),cwnd 增加 1/cwnd。
接上前面的慢啟動(dòng)的栗子,現(xiàn)假定 ssthresh
為 8
:
-
- 當(dāng) 8 個(gè) ACK 應(yīng)答確認(rèn)到來(lái)時(shí),每個(gè)確認(rèn)增加 1/8,8 個(gè) ACK 確認(rèn) cwnd 一共增加 1,于是這一次能夠發(fā)送 9 個(gè)
MSS
-
- 大小的數(shù)據(jù),變成了
線性增長(zhǎng)。
擁塞避免算法的變化過程如下圖:
擁塞避免
所以,我們可以發(fā)現(xiàn),擁塞避免算法就是將原本慢啟動(dòng)算法的指數(shù)增長(zhǎng)變成了線性增長(zhǎng),還是增長(zhǎng)階段,但是增長(zhǎng)速度緩慢了一些。
就這么一直增長(zhǎng)著后,網(wǎng)絡(luò)就會(huì)慢慢進(jìn)入了擁塞的狀況了,于是就會(huì)出現(xiàn)丟包現(xiàn)象,這時(shí)就需要對(duì)丟失的數(shù)據(jù)包進(jìn)行重傳。
當(dāng)觸發(fā)了重傳機(jī)制,也就進(jìn)入了「擁塞發(fā)生算法」。
擁塞發(fā)生
當(dāng)網(wǎng)絡(luò)出現(xiàn)擁塞,也就是會(huì)發(fā)生數(shù)據(jù)包重傳,重傳機(jī)制主要有兩種:
- 超時(shí)重傳快速重傳
當(dāng)發(fā)生了「超時(shí)重傳」,則就會(huì)使用擁塞發(fā)生算法。
這個(gè)時(shí)候,ssthresh 和 cwnd 的值會(huì)發(fā)生變化:
ssthresh
-
- 設(shè)為
cwnd/2
-
- ,
cwnd
-
- 重置為
1
- (是恢復(fù)為 cwnd 初始化值,我這里假定 cwnd 初始化值 1)
擁塞發(fā)生算法的變化如下圖:
擁塞發(fā)送 —— 超時(shí)重傳
接著,就重新開始慢啟動(dòng),慢啟動(dòng)是會(huì)突然減少數(shù)據(jù)流的。這真是一旦「超時(shí)重傳」,馬上回到解放前。但是這種方式太激進(jìn)了,反應(yīng)也很強(qiáng)烈,會(huì)造成網(wǎng)絡(luò)卡頓。
還有更好的方式,前面我們講過「快速重傳算法」。當(dāng)接收方發(fā)現(xiàn)丟了一個(gè)中間包的時(shí)候,發(fā)送三次前一個(gè)包的 ACK,于是發(fā)送端就會(huì)快速地重傳,不必等待超時(shí)再重傳。
TCP 認(rèn)為這種情況不嚴(yán)重,因?yàn)榇蟛糠譀]丟,只丟了一小部分,則 ssthresh
和 cwnd
變化如下:
cwnd = cwnd/2
-
- ,也就是設(shè)置為原來(lái)的一半;
ssthresh = cwnd
- ;進(jìn)入快速恢復(fù)算法
快速恢復(fù)
快速重傳和快速恢復(fù)算法一般同時(shí)使用,快速恢復(fù)算法是認(rèn)為,你還能收到 3 個(gè)重復(fù) ACK 說(shuō)明網(wǎng)絡(luò)也不那么糟糕,所以沒有必要像 RTO
超時(shí)那么強(qiáng)烈。
正如前面所說(shuō),進(jìn)入快速恢復(fù)之前,cwnd
和 ssthresh
已被更新了:
cwnd = cwnd/2
-
- ,也就是設(shè)置為原來(lái)的一半;
ssthresh = cwnd
- ;
然后,進(jìn)入快速恢復(fù)算法如下:
-
- 擁塞窗口
cwnd = ssthresh + 3
- ( 3 的意思是確認(rèn)有 3 個(gè)數(shù)據(jù)包被收到了);重傳丟失的數(shù)據(jù)包;如果再收到重復(fù)的 ACK,那么 cwnd 增加 1;如果收到新數(shù)據(jù)的 ACK 后,把 cwnd 設(shè)置為第一步中的 ssthresh 的值,原因是該 ACK 確認(rèn)了新的數(shù)據(jù),說(shuō)明從 duplicated ACK 時(shí)的數(shù)據(jù)都已收到,該恢復(fù)過程已經(jīng)結(jié)束,可以回到恢復(fù)之前的狀態(tài)了,也即再次進(jìn)入擁塞避免狀態(tài);
快速恢復(fù)算法的變化過程如下圖:
快速重傳和快速恢復(fù)
也就是沒有像「超時(shí)重傳」一夜回到解放前,而是還在比較高的值,后續(xù)呈線性增長(zhǎng)。
哪些會(huì)影響窗口大???
TCP窗口大小受到多個(gè)因素的影響,包括以下幾個(gè)方面:
- 接收方窗口大?。航邮辗降拇翱诖笮Q定了發(fā)送方可以發(fā)送的數(shù)據(jù)量。如果接收方窗口較小,發(fā)送方需要等待確認(rèn)后才能繼續(xù)發(fā)送數(shù)據(jù),從而限制了發(fā)送速率。帶寬和延遲:網(wǎng)絡(luò)的帶寬和延遲會(huì)對(duì)TCP窗口大小產(chǎn)生影響。較高的帶寬和較低的延遲通??梢灾С州^大的窗口大小,從而實(shí)現(xiàn)更高的數(shù)據(jù)傳輸速率。擁塞控制:TCP的擁塞控制機(jī)制會(huì)根據(jù)網(wǎng)絡(luò)擁塞程度調(diào)整窗口大小。當(dāng)網(wǎng)絡(luò)出現(xiàn)擁塞時(shí),TCP會(huì)減小窗口大小以降低發(fā)送速率,從而避免擁塞的進(jìn)一步惡化。路由器和網(wǎng)絡(luò)設(shè)備:路由器和其他網(wǎng)絡(luò)設(shè)備的緩沖區(qū)大小也會(huì)對(duì)TCP窗口大小產(chǎn)生影響。如果緩沖區(qū)較小,可能導(dǎo)致數(shù)據(jù)包丟失或延遲增加,從而限制了窗口大小。操作系統(tǒng)和應(yīng)用程序:操作系統(tǒng)和應(yīng)用程序也可以對(duì)TCP窗口大小進(jìn)行配置和調(diào)整。通過調(diào)整操作系統(tǒng)的參數(shù)或應(yīng)用程序的設(shè)置,可以影響TCP窗口大小的默認(rèn)值和動(dòng)態(tài)調(diào)整的行為。
因此,TCP窗口大小受到接收方窗口大小、帶寬和延遲、擁塞控制、網(wǎng)絡(luò)設(shè)備、操作系統(tǒng)和應(yīng)用程序等多個(gè)因素的綜合影響。