騰訊一面:32 位 4GB 系統,訪問 2GB 資料,虛擬記憶體會發生什麼?
來源:小林coding
今天有朋友給我發了他 8 月份面騰訊的面經,被問到的問題還挺多的。
作業系統和網路面試整個面試 60%,剩下40%是 Java+專案的內容(讀者的技術棧是 Java 方向)。
這次,我主要是擷取作業系統和網路相關的問題給大家解析一波。
作業系統
單核可以多執行緒嗎?
可以的。
單核建立了多執行緒,CPU 會從一個程式快速切換至另一個程式,其間每個程式各執行幾十或幾百個毫秒,雖然單核的 CPU 在某一個瞬間,只能執行一個程式。但在 1 秒鐘期間,它可能會執行多個程式,這樣就產生並行的錯覺,實際上這是併發。
虛擬地址怎麼找到對應的內容的?
作業系統記憶體管理方式主要兩種,不同的管理方式,定址的實現是不同的:
記憶體分段:將程式的虛擬地址空間劃分為多個不同大小的段,每個段對應一個邏輯單位,如程式碼段、資料段、堆段和棧段。每個段的大小可以根據需要進行調整,使得不同段可以按需分配和釋放記憶體。虛擬記憶體分段的優點是可以更好地管理不同型別的資料,但是由於段的大小不一致,容易產生外部碎片。 記憶體分頁:將程式的虛擬地址空間劃分為固定大小的頁,同時將實體記憶體也劃分為相同大小的頁框。透過頁表將虛擬地址對映到實體地址,並且可以按需載入和釋放頁。虛擬記憶體分頁的優點是可以更好地利用實體記憶體空間,但是可能會產生內部碎片。
分段的定址方式
分段機制下的虛擬地址由兩部分組成,段選擇因子和段內偏移量。
段選擇因子和段內偏移量:
段選擇子就儲存在段暫存器裡面。段選擇子裡面最重要的是段號,用作段表的索引。段表裡面儲存的是這個段的基地址、段的界限和特權等級等。 虛擬地址中的段內偏移量應該位於 0 和段界限之間,如果段內偏移量是合法的,就將段基地址加上段內偏移量得到實體記憶體地址。
在上面,知道了虛擬地址是透過段表與實體地址進行對映的,分段機制會把程式的虛擬地址分成 4 個段,每個段在段表中有一個項,在這一項找到段的基地址,再加上偏移量,於是就能找到實體記憶體中的地址,如下圖:
如果要訪問段 3 中偏移量 500 的虛擬地址,我們可以計算出實體地址為,段 3 基地址 7000 + 偏移量 500 = 7500。
分段的辦法很好,解決了程式本身不需要關心具體的實體記憶體地址的問題,但它也有一些不足之處:
第一個就是記憶體碎片的問題。 第二個就是記憶體交換的效率低的問題。
分頁的定址方式
虛擬地址與實體地址之間透過頁表來對映,如下圖:
頁表是儲存在記憶體裡的,記憶體管理單元 (MMU)就做將虛擬記憶體地址轉換成實體地址的工作。
而當程式訪問的虛擬地址在頁表中查不到時,系統會產生一個缺頁異常,進入系統核心空間分配實體記憶體、更新程式頁表,最後再返回使用者空間,恢復程式的執行。
在分頁機制下,虛擬地址分為兩部分,頁號和頁內偏移。頁號作為頁表的索引,頁表包含物理頁每頁所在實體記憶體的基地址,這個基地址與頁內偏移的組合就形成了實體記憶體地址,見下圖。
總結一下,對於一個記憶體地址轉換,其實就是這樣三個步驟:
把虛擬記憶體地址,切分成頁號和偏移量; 根據頁號,從頁表裡面,查詢對應的物理頁號; 直接拿物理頁號,加上前面的偏移量,就得到了實體記憶體地址。
下面舉個例子,虛擬記憶體中的頁透過頁表對映為了實體記憶體中的頁,如下圖:
32位 4G 執行2G的東西,虛擬記憶體會有什麼變化呢?
應用程式透過 malloc 函式申請記憶體的時候,實際上申請的是虛擬記憶體,此時並不會分配實體記憶體。
當應用程式讀寫了這塊虛擬記憶體,CPU 就會去訪問這個虛擬記憶體, 這時會發現這個虛擬記憶體沒有對映到實體記憶體, CPU 就會產生缺頁中斷,程式會從使用者態切換到核心態,並將缺頁中斷交給核心的 Page Fault Handler (缺頁中斷函式)處理。
缺頁中斷處理函式會看是否有空閒的實體記憶體:
如果有,就直接分配實體記憶體,並建立虛擬記憶體與實體記憶體之間的對映關係。 如果沒有空閒的實體記憶體,那麼核心就會開始進行回收記憶體的工作,比如會進行 swap 機制。
什麼是 Swap 機制?
當系統的實體記憶體不夠用的時候,就需要將實體記憶體中的一部分空間釋放出來,以供當前執行的程式使用。那些被釋放的空間可能來自一些很長時間沒有什麼操作的程式,這些被釋放的空間會被臨時儲存到磁碟,等到那些程式要執行時,再從磁碟中恢復儲存的資料到記憶體中。
另外,當記憶體使用存在壓力的時候,會開始觸發記憶體回收行為,會把這些不常訪問的記憶體先寫到磁碟中,然後釋放這些記憶體,給其他更需要的程式使用。再次訪問這些記憶體時,重新從磁碟讀入記憶體就可以了。
這種,將記憶體資料換出磁碟,又從磁碟中恢復資料到記憶體的過程,就是 Swap 機制負責的。
Swap 就是把一塊磁碟空間或者本地檔案,當成記憶體來使用,它包含換出和換入兩個過程:
換出(Swap Out) ,是把程式暫時不用的記憶體資料儲存到磁碟中,並釋放這些資料佔用的記憶體; 換入(Swap In),是在程式再次訪問這些記憶體的時候,把它們從磁碟讀到記憶體中來;
Swap 換入換出的過程如下圖:
使用 Swap 機制優點是,應用程式實際可以使用的記憶體空間將遠遠超過系統的實體記憶體。由於硬碟空間的價格遠比記憶體要低,因此這種方式無疑是經濟實惠的。當然,頻繁地讀寫硬碟,會顯著降低作業系統的執行速率,這也是 Swap 的弊端。
核心態和使用者態的區別是什麼?
核心態和使用者態是作業系統中的兩種不同的執行模式。
核心態是作業系統執行在特權級別最高的模式下的狀態,它具有對系統資源的完全控制權。在核心態下,作業系統可以執行特權指令,訪問所有的記憶體和裝置,以及執行關鍵的系統操作。核心態下執行的程式碼通常是作業系統核心或驅動程式。
使用者態是應用程式執行的一種模式,它執行在較低的特權級別下。在使用者態下,應用程式只能訪問有限的系統資源,不能直接執行特權指令或訪問核心級別的資料。使用者態下執行的程式碼通常是應用程式或使用者程式。
核心態和使用者態的區別在於許可權和資源訪問的限制。核心態具有更高的許可權和更廣泛的資源訪問能力,而使用者態受到限制,只能訪問有限的資源。作業系統透過將關鍵的操作和資源保護在核心態下來確保系統的安全性和穩定性。使用者程式透過系統呼叫的方式向作業系統請求服務或資源,並在使用者態下執行,以提供更高的隔離性和安全性。
網路協議
http常見響應碼有哪些?
HTTP 狀態碼分為 5 大類:1XX:表示訊息狀態碼;2XX:表示成功狀態碼;3XX:表示重定向狀態碼;4XX:表示客戶端錯誤狀態碼;5XX:表示服務端錯誤狀態碼。
其中常見的具體狀態碼有:200:請求成功;301:永久重定向;302:臨時重定向;404:無法找到此頁面;405:請求的方法型別不支援;500:伺服器內部出錯。
http各個版本的特性?
HTTP/1.1 相比 HTTP/1.0 效能上的改進:
使用長連線的方式改善了 HTTP/1.0 短連線造成的效能開銷。 支援管道(pipeline)網路傳輸,只要第一個請求發出去了,不必等其回來,就可以發第二個請求出去,可以減少整體的響應時間。
但 HTTP/1.1 還是有效能瓶頸:
請求 / 響應頭部(Header)未經壓縮就傳送,首部資訊越多延遲越大。只能壓縮 Body
的部分;傳送冗長的首部。每次互相傳送相同的首部造成的浪費較多; 伺服器是按請求的順序響應的,如果伺服器響應慢,會招致客戶端一直請求不到資料,也就是隊頭阻塞; 沒有請求優先順序控制; 請求只能從客戶端開始,伺服器只能被動響應。
HTTP/2 相比 HTTP/1.1 效能上的改進:
頭部壓縮 二進位制格式 併發傳輸 伺服器主動推送資源
1. 頭部壓縮
HTTP/2 會壓縮頭(Header)如果你同時發出多個請求,他們的頭是一樣的或是相似的,那麼,協議會幫你消除重複的部分。
這就是所謂的 HPACK
演算法:在客戶端和伺服器同時維護一張頭資訊表,所有欄位都會存入這個表,生成一個索引號,以後就不傳送同樣欄位了,只傳送索引號,這樣就提高速度了。
2. 二進位制格式
HTTP/2 不再像 HTTP/1.1 裡的純文字形式的報文,而是全面採用了二進位制格式,頭資訊和資料體都是二進位制,並且統稱為幀(frame):頭資訊幀(Headers Frame)和資料幀(Data Frame)。
這樣雖然對人不友好,但是對計算機非常友好,因為計算機只懂二進位制,那麼收到報文後,無需再將明文的報文轉成二進位制,而是直接解析二進位制報文,這增加了資料傳輸的效率。
3. 併發傳輸
我們都知道 HTTP/1.1 的實現是基於請求-響應模型的。同一個連線中,HTTP 完成一個事務(請求與響應),才能處理下一個事務,也就是說在發出請求等待響應的過程中,是沒辦法做其他事情的,如果響應遲遲不來,那麼後續的請求是無法傳送的,也造成了隊頭阻塞的問題。
而 HTTP/2 就很牛逼了,引出了 Stream 概念,多個 Stream 複用在一條 TCP 連線。
從上圖可以看到,1 個 TCP 連線包含多個 Stream,Stream 裡可以包含 1 個或多個 Message,Message 對應 HTTP/1 中的請求或響應,由 HTTP 頭部和包體構成。Message 裡包含一條或者多個 Frame,Frame 是 HTTP/2 最小單位,以二進位制壓縮格式存放 HTTP/1 中的內容(頭部和包體)。
針對不同的 HTTP 請求用獨一無二的 Stream ID 來區分,接收端可以透過 Stream ID 有序組裝成 HTTP 訊息,不同 Stream 的幀是可以亂序傳送的,因此可以併發不同的 Stream ,也就是 HTTP/2 可以並行交錯地傳送請求和響應。
比如下圖,服務端並行交錯地傳送了兩個響應:Stream 1 和 Stream 3,這兩個 Stream 都是跑在一個 TCP 連線上,客戶端收到後,會根據相同的 Stream ID 有序組裝成 HTTP 訊息。
4、伺服器推送
HTTP/2 還在一定程度上改善了傳統的「請求 - 應答」工作模式,服務端不再是被動地響應,可以主動向客戶端傳送訊息。
客戶端和伺服器雙方都可以建立 Stream, Stream ID 也是有區別的,客戶端建立的 Stream 必須是奇數號,而伺服器建立的 Stream 必須是偶數號。
比如下圖,Stream 1 是客戶端向服務端請求的資源,屬於客戶端建立的 Stream,所以該 Stream 的 ID 是奇數(數字 1);Stream 2 和 4 都是服務端主動向客戶端推送的資源,屬於服務端建立的 Stream,所以這兩個 Stream 的 ID 是偶數(數字 2 和 4)。
再比如,客戶端透過 HTTP/1.1 請求從伺服器那獲取到了 HTML 檔案,而 HTML 可能還需要依賴 CSS 來渲染頁面,這時客戶端還要再發起獲取 CSS 檔案的請求,需要兩次訊息往返,如下圖左邊部分:
如上圖右邊部分,在 HTTP/2 中,客戶端在訪問 HTML 時,伺服器可以直接主動推送 CSS 檔案,減少了訊息傳遞的次數。
tcp擁塞控制介紹一下
在網路出現擁堵時,如果繼續傳送大量資料包,可能會導致資料包時延、丟失等,這時 TCP 就會重傳資料,但是一重傳就會導致網路的負擔更重,於是會導致更大的延遲以及更多的丟包,這個情況就會進入惡性迴圈被不斷地放大....
所以,TCP 不能忽略網路上發生的事,它被設計成一個無私的協議,當網路傳送擁塞時,TCP 會自我犧牲,降低傳送的資料量。
於是,就有了擁塞控制,控制的目的就是避免「傳送方」的資料填滿整個網路。
為了在「傳送方」調節所要傳送資料的量,定義了一個叫做「擁塞視窗」的概念。
擁塞控制主要是四個演算法:
慢啟動 擁塞避免 擁塞發生 快速恢復
慢啟動
TCP 在剛建立連線完成後,首先是有個慢啟動的過程,這個慢啟動的意思就是一點一點的提高傳送資料包的數量,如果一上來就發大量的資料,這不是給網路添堵嗎?
慢啟動的演算法記住一個規則就行:當傳送方每收到一個 ACK,擁塞視窗 cwnd 的大小就會加 1。
這裡假定擁塞視窗 cwnd
和傳送視窗 swnd
相等,下面舉個例子:
連線建立完成後,一開始初始化 cwnd = 1
,表示可以傳一個MSS
大小的資料。當收到一個 ACK 確認應答後,cwnd 增加 1,於是一次能夠傳送 2 個 當收到 2 個的 ACK 確認應答後, cwnd 增加 2,於是就可以比之前多發2 個,所以這一次能夠傳送 4 個 當這 4 個的 ACK 確認到來的時候,每個確認 cwnd 增加 1, 4 個確認 cwnd 增加 4,於是就可以比之前多發 4 個,所以這一次能夠傳送 8 個。
慢啟動演算法的變化過程如下圖:
可以看出慢啟動演算法,發包的個數是指數性的增長。
那慢啟動漲到什麼時候是個頭呢?
有一個叫慢啟動門限 ssthresh
(slow start threshold)狀態變數。
當 cwnd
<ssthresh
時,使用慢啟動演算法。當 cwnd
>=ssthresh
時,就會使用「擁塞避免演算法」。
擁塞避免
當擁塞視窗 cwnd
「超過」慢啟動門限 ssthresh
就會進入擁塞避免演算法。
一般來說 ssthresh
的大小是 65535
位元組。
那麼進入擁塞避免演算法後,它的規則是:每當收到一個 ACK 時,cwnd 增加 1/cwnd。
接上前面的慢啟動的栗子,現假定 ssthresh
為 8
:
當 8 個 ACK 應答確認到來時,每個確認增加 1/8,8 個 ACK 確認 cwnd 一共增加 1,於是這一次能夠傳送 9 個 MSS
大小的資料,變成了線性增長。
擁塞避免演算法的變化過程如下圖:
所以,我們可以發現,擁塞避免演算法就是將原本慢啟動演算法的指數增長變成了線性增長,還是增長階段,但是增長速度緩慢了一些。
就這麼一直增長著後,網路就會慢慢進入了擁塞的狀況了,於是就會出現丟包現象,這時就需要對丟失的資料包進行重傳。
當觸發了重傳機制,也就進入了「擁塞發生演算法」。
擁塞發生
當網路出現擁塞,也就是會發生資料包重傳,重傳機制主要有兩種:
超時重傳 快速重傳
當發生了「超時重傳」,則就會使用擁塞發生演算法。
這個時候,ssthresh 和 cwnd 的值會發生變化:
ssthresh
設為cwnd/2
,cwnd
重置為1
(是恢復為 cwnd 初始化值,我這裡假定 cwnd 初始化值 1)
擁塞發生演算法的變化如下圖:
接著,就重新開始慢啟動,慢啟動是會突然減少資料流的。這真是一旦「超時重傳」,馬上回到解放前。但是這種方式太激進了,反應也很強烈,會造成網路卡頓。
還有更好的方式,前面我們講過「快速重傳演算法」。當接收方發現丟了一箇中間包的時候,傳送三次前一個包的 ACK,於是傳送端就會快速地重傳,不必等待超時再重傳。
TCP 認為這種情況不嚴重,因為大部分沒丟,只丟了一小部分,則 ssthresh
和 cwnd
變化如下:
cwnd = cwnd/2
,也就是設定為原來的一半;ssthresh = cwnd
;進入快速恢復演算法
快速恢復
快速重傳和快速恢復演算法一般同時使用,快速恢復演算法是認為,你還能收到 3 個重複 ACK 說明網路也不那麼糟糕,所以沒有必要像 RTO
超時那麼強烈。
正如前面所說,進入快速恢復之前,cwnd
和 ssthresh
已被更新了:
cwnd = cwnd/2
,也就是設定為原來的一半;ssthresh = cwnd
;
然後,進入快速恢復演算法如下:
擁塞視窗 cwnd = ssthresh + 3
( 3 的意思是確認有 3 個資料包被收到了);重傳丟失的資料包; 如果再收到重複的 ACK,那麼 cwnd 增加 1; 如果收到新資料的 ACK 後,把 cwnd 設定為第一步中的 ssthresh 的值,原因是該 ACK 確認了新的資料,說明從 duplicated ACK 時的資料都已收到,該恢復過程已經結束,可以回到恢復之前的狀態了,也即再次進入擁塞避免狀態;
快速恢復演算法的變化過程如下圖:
也就是沒有像「超時重傳」一夜回到解放前,而是還在比較高的值,後續呈線性增長。
哪些會影響視窗大小?
TCP視窗大小受到多個因素的影響,包括以下幾個方面:
接收方視窗大小:接收方的視窗大小決定了傳送方可以傳送的資料量。如果接收方視窗較小,傳送方需要等待確認後才能繼續傳送資料,從而限制了傳送速率。 頻寬和延遲:網路的頻寬和延遲會對TCP視窗大小產生影響。較高的頻寬和較低的延遲通常可以支援較大的視窗大小,從而實現更高的資料傳輸速率。 擁塞控制:TCP的擁塞控制機制會根據網路擁塞程度調整視窗大小。當網路出現擁塞時,TCP會減小視窗大小以降低傳送速率,從而避免擁塞的進一步惡化。 路由器和網路裝置:路由器和其他網路裝置的緩衝區大小也會對TCP視窗大小產生影響。如果緩衝區較小,可能導致資料包丟失或延遲增加,從而限制了視窗大小。 作業系統和應用程式:作業系統和應用程式也可以對TCP視窗大小進行配置和調整。透過調整作業系統的引數或應用程式的設定,可以影響TCP視窗大小的預設值和動態調整的行為。
因此,TCP視窗大小受到接收方視窗大小、頻寬和延遲、擁塞控制、網路裝置、作業系統和應用程式等多個因素的綜合影響。
來自 “ ITPUB部落格 ” ,連結:https://blog.itpub.net/70024924/viewspace-3004876/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 騰訊一面:記憶體滿了,會發生什麼?記憶體
- 虛擬記憶體有什麼用 虛擬記憶體不足怎麼解決記憶體
- win10怎麼加虛擬記憶體_win10系統增加虛擬記憶體教程Win10記憶體
- 32位win10系統支援多大記憶體_32位win10系統能識別多大記憶體Win10記憶體
- 為什麼 Linux 需要虛擬記憶體Linux記憶體
- 虛擬記憶體系統——瞭解記憶體的工作原理記憶體
- win10怎麼清除虛擬記憶體 win10系統虛擬記憶體清理的辦法Win10記憶體
- 記憶體耗盡後Redis會發生什麼記憶體Redis
- win10 8g記憶體還需要虛擬記憶體嗎 win10系統8g記憶體怎麼分配虛擬記憶體Win10記憶體
- Java虛擬機器01——Java記憶體資料區域和記憶體溢位異常Java虛擬機記憶體溢位
- 計算機作業系統——虛擬記憶體與實體記憶體計算機作業系統記憶體
- win11系統設定虛擬記憶體記憶體
- windows10系統如何增加虛擬記憶體Windows記憶體
- 32位xp系統識別4G以上記憶體記憶體
- Win10系統C盤虛擬記憶體怎麼設定?Win10專業版系統C盤虛擬記憶體設定方法Win10記憶體
- win10 32位系統4g記憶體夠不夠用_4g記憶體可以裝win10 32位系統嗎Win10記憶體
- win10怎麼調整虛擬記憶體_win10怎麼調虛擬記憶體Win10記憶體
- Linux 虛擬記憶體Linux記憶體
- 聊聊虛擬記憶體記憶體
- win10虛擬記憶體怎麼設定 筆記本win10虛擬記憶體怎麼看Win10記憶體筆記
- 在 4GB 實體記憶體的機器上,申請 8G 記憶體會怎麼樣?記憶體
- win10怎麼更改虛擬記憶體_win10更改虛擬記憶體大小方法Win10記憶體
- win10虛擬記憶體如何優化_win10怎麼優化虛擬記憶體Win10記憶體優化
- win10 虛擬記憶體怎麼設定好 win10 虛擬記憶體設定方法Win10記憶體
- 怎麼設定虛擬記憶體win10 win10虛擬記憶體如何設定記憶體Win10
- win10虛擬記憶體怎麼設定 win10虛擬記憶體設定方法Win10記憶體
- 對jvm虛擬機器 記憶體溢位的思考JVM虛擬機記憶體溢位
- 【Java基礎】實體記憶體&虛擬記憶體Java記憶體
- 5.虛擬記憶體記憶體
- 在Linux中,什麼是虛擬記憶體?它是如何工作的?Linux記憶體
- 【知識分享】伺服器虛擬記憶體有什麼用伺服器記憶體
- 深入理解JVM虛擬機器-JVM記憶體區域與記憶體溢位JVM虛擬機記憶體溢位
- win10怎麼刪除虛擬記憶體 刪除win10虛擬記憶體的方法Win10記憶體
- 讀懂作業系統之虛擬記憶體頁表(五)作業系統記憶體
- win10 8g記憶體虛擬記憶體設定多少合適_win10 8g記憶體怎麼分配虛擬記憶體Win10記憶體
- 深入理解Java虛擬機器之物件的記憶體佈局、訪問定位Java虛擬機物件記憶體
- win10虛擬記憶體怎麼設定最好 win10虛擬記憶體設定多少好Win10記憶體
- win10虛擬記憶體如何最佳化_win10怎麼最佳化虛擬記憶體Win10記憶體