精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

Linux操作系統(tǒng)面試——虛擬內(nèi)存45問

系統(tǒng) Linux
OOM Killer 在選擇要?dú)⒌舻倪M(jìn)程時(shí),會(huì)為每個(gè)進(jìn)程計(jì)算一個(gè) oom_score 值 。oom_score 的計(jì)算涉及多個(gè)因素,包括進(jìn)程占用的物理內(nèi)存頁數(shù)、交換區(qū)頁數(shù)以及頁表(Page Table)數(shù)量等 。

你是否好奇,為何在老舊設(shè)備上運(yùn)行大型程序時(shí),系統(tǒng)不會(huì)瞬間崩潰,而是勉力維持?為何多任務(wù)并行時(shí),各個(gè)進(jìn)程看似都能擁有充足內(nèi)存空間?這背后,Linux 虛擬內(nèi)存技術(shù)功不可沒。它讓每個(gè)進(jìn)程都仿佛擁有一塊獨(dú)立且足夠大的內(nèi)存空間,不必?fù)?dān)憂實(shí)際物理內(nèi)存的局促與復(fù)雜布局。想象一下,你有一間堆滿書籍的小書房,空間有限,但又想存放更多知識(shí)。虛擬內(nèi)存就像一個(gè)智能書架系統(tǒng),它能巧妙規(guī)劃,將常用書籍?dāng)[在觸手可及之處,不常用的暫時(shí)存放到地下室,需要時(shí)再快速調(diào)取。

Linux 虛擬內(nèi)存正是如此,通過頁表這一 “翻譯官”,將進(jìn)程使用的虛擬地址精準(zhǔn)轉(zhuǎn)換為實(shí)際物理地址,實(shí)現(xiàn)內(nèi)存地址的靈活轉(zhuǎn)換與管理。當(dāng)進(jìn)程訪問的數(shù)據(jù)不在物理內(nèi)存中,系統(tǒng)便會(huì)觸發(fā)缺頁異常,如同從地下室取書,從磁盤交換區(qū)或文件系統(tǒng)中加載相應(yīng)數(shù)據(jù)到內(nèi)存,更新頁表后,進(jìn)程便能順利訪問。今天,就讓我們一同深入 Linux 虛擬內(nèi)存的神秘世界,從原理、機(jī)制到實(shí)踐應(yīng)用,全方位解析這一解決物理內(nèi)存限制的關(guān)鍵技術(shù),探尋它如何在有限資源下,為程序運(yùn)行構(gòu)建出無限可能的舞臺(tái)。

Part1.虛擬內(nèi)存是什么?

1.1虛擬內(nèi)存概述

虛擬內(nèi)存,簡單來說,是一種內(nèi)存管理技術(shù),它讓操作系統(tǒng)能將硬盤空間當(dāng)作額外的內(nèi)存來使用。你可以把它想象成一個(gè) “內(nèi)存擴(kuò)充器”,當(dāng)計(jì)算機(jī)的物理內(nèi)存(也就是我們常說的內(nèi)存條提供的內(nèi)存)不夠用時(shí),虛擬內(nèi)存就會(huì)把暫時(shí)用不到的數(shù)據(jù)從物理內(nèi)存轉(zhuǎn)移到硬盤上的特定區(qū)域,這個(gè)區(qū)域就像是內(nèi)存的 “倉庫”,我們稱之為交換空間(swap space) 。當(dāng)程序需要這些數(shù)據(jù)時(shí),再從交換空間把數(shù)據(jù)調(diào)回到物理內(nèi)存。

圖片圖片

打個(gè)比方,物理內(nèi)存就像是你辦公桌上的桌面,空間有限,只能放一些當(dāng)下馬上要處理的文件。而虛擬內(nèi)存則像是辦公室里的文件柜,當(dāng)桌面上堆滿了文件,你就可以把一些暫時(shí)不需要的文件放到文件柜里。當(dāng)你需要這些文件時(shí),再從文件柜中取出來放回桌面。這樣,即使你的桌面空間有限,也能處理更多的文件,就像計(jì)算機(jī)即使物理內(nèi)存有限,也能運(yùn)行更多的程序。

對(duì)于進(jìn)程來說,虛擬內(nèi)存提供了獨(dú)立的地址空間,每個(gè)進(jìn)程都認(rèn)為自己獨(dú)占了系統(tǒng)的所有內(nèi)存資源,這樣可以避免不同進(jìn)程之間的內(nèi)存沖突,提高了系統(tǒng)的穩(wěn)定性和安全性。就好比每個(gè)租客都覺得自己租下了一整套房子,有獨(dú)立的空間放置自己的物品,不用擔(dān)心和其他租客的物品混在一起。

1.2為什么需要使用虛擬內(nèi)存

進(jìn)程需要使用的代碼和數(shù)據(jù)都放在內(nèi)存中,比放在外存中要快很多。問題是內(nèi)存空間太小了,不能滿足進(jìn)程的需求,而且現(xiàn)在都是多進(jìn)程,情況更加糟糕。所以提出了虛擬內(nèi)存,使得每個(gè)進(jìn)程用于3G的獨(dú)立用戶內(nèi)存空間和共享的1G內(nèi)核內(nèi)存空間。(每個(gè)進(jìn)程都有自己的頁表,才使得3G用戶空間的獨(dú)立)這樣進(jìn)程運(yùn)行的速度必然很快了。而且虛擬內(nèi)存機(jī)制還解決了內(nèi)存碎片和內(nèi)存不連續(xù)的問題。為什么可以在有限的物理內(nèi)存上達(dá)到這樣的效果呢?

例如:對(duì)于程序計(jì)數(shù)器位數(shù)為32位的處理器來說,他的地址發(fā)生器所能發(fā)出的地址數(shù)目為2^32=4G個(gè),于是這個(gè)處理器所能訪問的最大內(nèi)存空間就是4G。在計(jì)算機(jī)技術(shù)中,這個(gè)值就叫做處理器的尋址空間或?qū)ぶ纺芰Α?/span>

照理說,為了充分利用處理器的尋址空間,就應(yīng)按照處理器的最大尋址來為其分配系統(tǒng)的內(nèi)存。如果處理器具有32位程序計(jì)數(shù)器,那么就應(yīng)該按照下圖的方式,為其配備4G的內(nèi)存:

圖片圖片

這樣,處理器所發(fā)出的每一個(gè)地址都會(huì)有一個(gè)真實(shí)的物理存儲(chǔ)單元與之對(duì)應(yīng);同時(shí),每一個(gè)物理存儲(chǔ)單元都有唯一的地址與之對(duì)應(yīng)。這顯然是一種最理想的情況。

但遺憾的是,實(shí)際上計(jì)算機(jī)所配置內(nèi)存的實(shí)際空間常常小于處理器的尋址范圍,這是就會(huì)因處理器的一部分尋址空間沒有對(duì)應(yīng)的物理存儲(chǔ)單元,從而導(dǎo)致處理器尋址能力的浪費(fèi)。例如:如下圖的系統(tǒng)中,具有32位尋址能力的處理器只配置了256M的內(nèi)存儲(chǔ)器,這就會(huì)造成大量的浪費(fèi):

圖片圖片

另外,還有一些處理器因外部地址線的根數(shù)小于處理器程序計(jì)數(shù)器的位數(shù),而使地址總線的根數(shù)不滿足處理器的尋址范圍,從而處理器的其余尋址能力也就被浪費(fèi)了。例如:Intel8086處理器的程序計(jì)數(shù)器位32位,而處理器芯片的外部地址總線只有20根,所以它所能配置的最大內(nèi)存為1MB:

圖片圖片

在實(shí)際的應(yīng)用中,如果需要運(yùn)行的應(yīng)用程序比較小,所需內(nèi)存容量小于計(jì)算機(jī)實(shí)際所配置的內(nèi)存空間,自然不會(huì)出什么問題。但是,目前很多的應(yīng)用程序都比較大,計(jì)算機(jī)實(shí)際所配置的內(nèi)存空間無法滿足。

實(shí)踐和研究都證明:一個(gè)應(yīng)用程序總是逐段被運(yùn)行的,而且在一段時(shí)間內(nèi)會(huì)穩(wěn)定運(yùn)行在某一段程序里。

這也就出現(xiàn)了一個(gè)方法:如下圖所示,把要運(yùn)行的那一段程序自輔存復(fù)制到內(nèi)存中來運(yùn)行,而其他暫時(shí)不運(yùn)行的程序段就讓它仍然留在輔存。

圖片圖片

當(dāng)需要執(zhí)行另一端尚未在內(nèi)存的程序段(如程序段2),如下圖所示,就可以把內(nèi)存中程序段1的副本復(fù)制回輔存,在內(nèi)存騰出必要的空間后,再把輔存中的程序段2復(fù)制到內(nèi)存空間來執(zhí)行即可:

圖片圖片

在計(jì)算機(jī)技術(shù)中,把內(nèi)存中的程序段復(fù)制回輔存的做法叫做“換出”,而把輔存中程序段映射到內(nèi)存的做法叫做“換入”。經(jīng)過不斷有目的的換入和換出,處理器就可以運(yùn)行一個(gè)大于實(shí)際物理內(nèi)存的應(yīng)用程序了。或者說,處理器似乎是擁有了一個(gè)大于實(shí)際物理內(nèi)存的內(nèi)存空間。于是,這個(gè)存儲(chǔ)空間叫做虛擬內(nèi)存空間,而把真正的內(nèi)存叫做實(shí)際物理內(nèi)存,或簡稱為物理內(nèi)存。

那么對(duì)于一臺(tái)真實(shí)的計(jì)算機(jī)來說,它的虛擬內(nèi)存空間又有多大呢?計(jì)算機(jī)虛擬內(nèi)存空間的大小是由程序計(jì)數(shù)器的尋址能力來決定的。例如:在程序計(jì)數(shù)器的位數(shù)為32的處理器中,它的虛擬內(nèi)存空間就為4GB。

可見,如果一個(gè)系統(tǒng)采用了虛擬內(nèi)存技術(shù),那么它就存在著兩個(gè)內(nèi)存空間:虛擬內(nèi)存空間和物理內(nèi)存空間。虛擬內(nèi)存空間中的地址叫做“虛擬地址”;而實(shí)際物理內(nèi)存空間中的地址叫做“實(shí)際物理地址”或“物理地址”。處理器運(yùn)算器和應(yīng)用程序設(shè)計(jì)人員看到的只是虛擬內(nèi)存空間和虛擬地址,而處理器片外的地址總線看到的只是物理地址空間和物理地址。

由于存在兩個(gè)內(nèi)存地址,因此一個(gè)應(yīng)用程序從編寫到被執(zhí)行,需要進(jìn)行兩次映射。第一次是映射到虛擬內(nèi)存空間,第二次時(shí)映射到物理內(nèi)存空間。在計(jì)算機(jī)系統(tǒng)中,第兩次映射的工作是由硬件和軟件共同來完成的。承擔(dān)這個(gè)任務(wù)的硬件部分叫做存儲(chǔ)管理單元MMU,軟件部分就是操作系統(tǒng)的內(nèi)存管理模塊了。

在映射工作中,為了記錄程序段占用物理內(nèi)存的情況,操作系統(tǒng)的內(nèi)存管理模塊需要建立一個(gè)表格,該表格以虛擬地址為索引,記錄了程序段所占用的物理內(nèi)存的物理地址。這個(gè)虛擬地址/物理地址記錄表便是存儲(chǔ)管理單元MMU把虛擬地址轉(zhuǎn)化為實(shí)際物理地址的依據(jù),記錄表與存儲(chǔ)管理單元MMU的作用如下圖所示:

圖片圖片

綜上所述,虛擬內(nèi)存技術(shù)的實(shí)現(xiàn),是建立在應(yīng)用程序可以分成段,并且具有“在任何時(shí)候正在使用的信息總是所有存儲(chǔ)信息的一小部分”的局部特性基礎(chǔ)上的。它是通過用輔存空間模擬RAM來實(shí)現(xiàn)的一種使機(jī)器的作業(yè)地址空間大于實(shí)際內(nèi)存的技術(shù)。

從處理器運(yùn)算裝置和程序設(shè)計(jì)人員的角度來看,它面對(duì)的是一個(gè)用MMU、映射記錄表和物理內(nèi)存封裝起來的一個(gè)虛擬內(nèi)存空間,這個(gè)存儲(chǔ)空間的大小取決于處理器程序計(jì)數(shù)器的尋址空間。

可見,程序映射表是實(shí)現(xiàn)虛擬內(nèi)存的技術(shù)關(guān)鍵,它可給系統(tǒng)帶來如下特點(diǎn):

  • 系統(tǒng)中每一個(gè)程序各自都有一個(gè)大小與處理器尋址空間相等的虛擬內(nèi)存空間;
  • 在一個(gè)具體時(shí)刻,處理器只能使用其中一個(gè)程序的映射記錄表,因此它只看到多個(gè)程序虛存空間中的一個(gè),這樣就保證了各個(gè)程序的虛存空間時(shí)互不相擾、各自獨(dú)立的;
  • 使用程序映射表可方便地實(shí)現(xiàn)物理內(nèi)存的共享。

Part2.虛擬內(nèi)存與物理內(nèi)存

2.1兩者區(qū)別

物理內(nèi)存是實(shí)實(shí)在在插在計(jì)算機(jī)主板內(nèi)存槽上的內(nèi)存條所提供的內(nèi)存,是計(jì)算機(jī)硬件的一部分,由半導(dǎo)體芯片組成 ,CPU 可以直接進(jìn)行尋址,用于存放正在運(yùn)行的程序和臨時(shí)的數(shù)據(jù)。一旦電腦關(guān)閉或重啟,物理內(nèi)存中的內(nèi)容就會(huì)丟失。它的容量是固定的,取決于你安裝的內(nèi)存條數(shù)量和容量大小。例如,你的電腦安裝了兩根 8GB 的內(nèi)存條,那么物理內(nèi)存就是 16GB。

而虛擬內(nèi)存是一種內(nèi)存管理技術(shù),并不是真實(shí)的物理硬件。它通過將硬盤空間的一部分模擬為內(nèi)存來擴(kuò)展物理內(nèi)存的容量,是操作系統(tǒng)為了擴(kuò)大可用內(nèi)存而創(chuàng)造的一種 “模擬” 擴(kuò)展 。當(dāng)物理內(nèi)存不足時(shí),操作系統(tǒng)會(huì)自動(dòng)利用虛擬內(nèi)存來存儲(chǔ)暫時(shí)不需要立即訪問的數(shù)據(jù),把部分長期不用的數(shù)據(jù)從物理內(nèi)存移動(dòng)到硬盤上的虛擬內(nèi)存區(qū)域(也就是交換空間) 。虛擬內(nèi)存對(duì)于那些需要大量數(shù)據(jù)但仍受限于物理內(nèi)存的應(yīng)用特別有用,但其讀取速度遠(yuǎn)低于物理內(nèi)存,因?yàn)橛脖P的讀寫速度要比內(nèi)存慢得多。

2.2相互關(guān)系

在系統(tǒng)運(yùn)行時(shí),虛擬內(nèi)存和物理內(nèi)存緊密協(xié)作。當(dāng)一個(gè)程序啟動(dòng)時(shí),操作系統(tǒng)會(huì)為其分配虛擬內(nèi)存空間,程序在運(yùn)行過程中訪問的都是虛擬地址。當(dāng)程序需要訪問某個(gè)數(shù)據(jù)時(shí),首先會(huì)通過虛擬地址去查找。CPU 會(huì)將虛擬地址發(fā)送給內(nèi)存管理單元(MMU) ,MMU 通過查詢頁表(一種記錄虛擬地址和物理地址映射關(guān)系的數(shù)據(jù)結(jié)構(gòu)),將虛擬地址轉(zhuǎn)換為對(duì)應(yīng)的物理地址。如果所需的數(shù)據(jù)就在物理內(nèi)存中,那么 CPU 就可以直接從物理內(nèi)存中讀取數(shù)據(jù),這就好比你在辦公桌上能直接找到需要的文件,速度很快。

但如果數(shù)據(jù)不在物理內(nèi)存中,而是在虛擬內(nèi)存(硬盤的交換空間)里,就會(huì)發(fā)生缺頁異常。這時(shí),操作系統(tǒng)會(huì)從物理內(nèi)存中選擇一個(gè)暫時(shí)不用的頁面(如果物理內(nèi)存已滿的話),將其數(shù)據(jù)寫回到硬盤的交換空間,然后把程序需要的數(shù)據(jù)從交換空間讀取到物理內(nèi)存中,并更新頁表中的映射關(guān)系。之后,CPU 就可以通過新的物理地址訪問數(shù)據(jù)了。這個(gè)過程就像是你在辦公桌上找不到文件,需要去文件柜(虛擬內(nèi)存)里找,找到后把文件拿出來放在桌面上(物理內(nèi)存),方便下次使用 。

可以說,虛擬內(nèi)存是物理內(nèi)存的補(bǔ)充和延伸,它們共同為程序的運(yùn)行提供內(nèi)存支持,使得計(jì)算機(jī)系統(tǒng)能夠更高效地運(yùn)行多個(gè)程序,處理各種復(fù)雜的任務(wù)。

Part3.虛擬內(nèi)存技術(shù)

3.1分頁機(jī)制

分頁是虛擬內(nèi)存管理中的一種重要機(jī)制,它將內(nèi)存空間劃分為固定大小的塊,這些塊就被稱為頁(page) 。在 Linux 系統(tǒng)中,常見的頁大小是 4KB(2^12 字節(jié)),不過在某些架構(gòu)下,也支持如 64KB 或 2MB 的大頁(Huge Pages) 。之所以采用固定大小的頁,是為了簡化內(nèi)存管理和提高內(nèi)存分配的效率。

圖片圖片

頁表(Page Table)是分頁機(jī)制的核心數(shù)據(jù)結(jié)構(gòu),它就像是一本 “地址字典”,記錄了虛擬頁與物理頁之間的映射關(guān)系 。每個(gè)進(jìn)程都擁有自己獨(dú)立的頁表,當(dāng)進(jìn)程訪問內(nèi)存時(shí),CPU 會(huì)將虛擬地址發(fā)送給內(nèi)存管理單元(MMU) ,MMU 通過查詢頁表,把虛擬地址轉(zhuǎn)換為對(duì)應(yīng)的物理地址。例如,在 x86_64 架構(gòu)中,Linux 使用四級(jí)頁表結(jié)構(gòu),分別為頁全局目錄(PGD)、頁上級(jí)目錄(PUD)、頁中間目錄(PMD)和頁表項(xiàng)(PTE) 。當(dāng) CPU 接收到一個(gè)虛擬地址時(shí),首先會(huì)根據(jù)虛擬地址的最高幾位在 PGD 中找到對(duì)應(yīng)的 PUD;然后依據(jù)虛擬地址的次高幾位在 PUD 中找到對(duì)應(yīng)的 PMD;接著根據(jù)虛擬地址的再次高幾位在 PMD 中找到對(duì)應(yīng)的 PTE;最后,PTE 中記錄了該虛擬頁對(duì)應(yīng)的物理頁框地址,從而實(shí)現(xiàn)了虛擬地址到物理地址的轉(zhuǎn)換。

以一個(gè)簡單的例子來說明,假設(shè)我們有一個(gè)進(jìn)程需要訪問虛擬地址 0x12345678。在四級(jí)頁表結(jié)構(gòu)下,MMU 會(huì)先提取虛擬地址的高幾位(比如高 9 位,具體位數(shù)根據(jù)架構(gòu)和頁表設(shè)計(jì)而定),通過這幾位索引 PGD,找到對(duì)應(yīng)的 PUD;再從虛擬地址中提取接下來的幾位(同樣根據(jù)設(shè)計(jì)而定),在 PUD 中找到對(duì)應(yīng)的 PMD;然后繼續(xù)提取相應(yīng)位在 PMD 中找到 PTE;最終,PTE 中保存了物理頁框地址,再結(jié)合虛擬地址中剩下的偏移部分,就可以得到實(shí)際的物理地址,進(jìn)而訪問到所需的數(shù)據(jù) 。

3.2分段機(jī)制

分段機(jī)制是另一種內(nèi)存管理方式,它將程序的地址空間劃分為多個(gè)邏輯段,比如代碼段(存放程序的指令)、數(shù)據(jù)段(存放程序的全局變量和靜態(tài)變量等)、堆棧段(存放函數(shù)調(diào)用時(shí)的局部變量、返回地址等) 。每個(gè)段都有自己的起始地址和長度,通過段寄存器來標(biāo)識(shí)和訪問。

圖片圖片

與分頁機(jī)制不同,分頁是將內(nèi)存劃分為固定大小的頁,主要目的是為了實(shí)現(xiàn)虛擬內(nèi)存和內(nèi)存管理的高效性;而分段是基于程序的邏輯結(jié)構(gòu)進(jìn)行劃分,更注重程序的模塊化和保護(hù) 。例如,代碼段可以設(shè)置為只讀,防止程序運(yùn)行時(shí)被意外修改;數(shù)據(jù)段可以根據(jù)需要設(shè)置讀寫權(quán)限,以保證數(shù)據(jù)的安全性。

分頁機(jī)制消除了外部碎片,因?yàn)閮?nèi)存空間是預(yù)先劃分好的,頁與頁之間是緊密排列的。但分頁機(jī)制可能產(chǎn)生內(nèi)部碎片,即當(dāng)分配的頁面大小大于實(shí)際需要的內(nèi)存大小時(shí),剩余的空間將被浪費(fèi)。

現(xiàn)代操作系統(tǒng)一般都采用段頁式存儲(chǔ)的方式來實(shí)現(xiàn)虛擬內(nèi)存和物理內(nèi)存的映射。段頁式存儲(chǔ),顧名思義是一種結(jié)合了段式存儲(chǔ)管理和頁式存儲(chǔ)管理優(yōu)點(diǎn)的內(nèi)存管理技術(shù)。在段頁式存儲(chǔ)中,程序的邏輯地址空間被劃分為若干個(gè)段,每個(gè)段再被劃分為若干個(gè)固定大小的頁。同時(shí),物理內(nèi)存也被劃分為與頁面大小相同的物理塊。

圖片圖片

  • 當(dāng)程序需要訪問某個(gè)邏輯地址時(shí),首先根據(jù)段號(hào)找到對(duì)應(yīng)的段表項(xiàng)。
  • 從段表項(xiàng)中獲取該段的頁表起始地址,并根據(jù)段內(nèi)頁號(hào)找到對(duì)應(yīng)的頁表項(xiàng)。
  • 從頁表項(xiàng)中獲取該頁對(duì)應(yīng)的物理塊號(hào)。
  • 最后,將物理塊號(hào)與頁內(nèi)偏移量組合,得到物理地址,從而完成地址映射。

在 Linux 內(nèi)核中,雖然 x86 架構(gòu)支持分段機(jī)制,但 Linux 對(duì)分段機(jī)制的使用進(jìn)行了簡化和弱化 。在 32 位的 x86 架構(gòu)中,Linux 通常只使用了兩個(gè)段:一個(gè)是用戶數(shù)據(jù)段,用于存放用戶進(jìn)程的數(shù)據(jù);另一個(gè)是用戶代碼段,用于存放用戶進(jìn)程的代碼。對(duì)于內(nèi)核空間,也類似地使用兩個(gè)段。這種簡化的分段方式,使得 Linux 的內(nèi)存管理更加簡單高效,同時(shí)也借助分頁機(jī)制來實(shí)現(xiàn)更強(qiáng)大的內(nèi)存管理功能 。

虛擬內(nèi)存區(qū)域(VMA)是 Linux 內(nèi)核中用于管理進(jìn)程虛擬內(nèi)存的一種數(shù)據(jù)結(jié)構(gòu) 。每個(gè)進(jìn)程的虛擬地址空間被劃分為多個(gè) VMA,每個(gè) VMA 對(duì)應(yīng)一個(gè)連續(xù)的虛擬地址范圍,并且具有相同的訪問權(quán)限和屬性 。例如,一個(gè)進(jìn)程的代碼段、數(shù)據(jù)段、堆棧段等都可以分別對(duì)應(yīng)一個(gè) VMA。分段機(jī)制與 VMA 的關(guān)系在于,VMA 可以看作是對(duì)分段概念的一種擴(kuò)展和細(xì)化,它更靈活地管理進(jìn)程的虛擬內(nèi)存,并且與分頁機(jī)制相結(jié)合,共同實(shí)現(xiàn)了 Linux 高效的內(nèi)存管理 。

3.3內(nèi)存對(duì)齊

內(nèi)存對(duì)齊是指數(shù)據(jù)在內(nèi)存中存儲(chǔ)時(shí),按照一定的規(guī)則排列,使得數(shù)據(jù)的起始地址是特定值的倍數(shù) 。這個(gè)特定值通常是數(shù)據(jù)類型大小的倍數(shù),比如在 32 位系統(tǒng)中,int 類型通常占 4 字節(jié),那么 int 類型數(shù)據(jù)的起始地址通常會(huì)被對(duì)齊到 4 的倍數(shù);在 64 位系統(tǒng)中,指針類型通常占 8 字節(jié),指針數(shù)據(jù)的起始地址會(huì)被對(duì)齊到 8 的倍數(shù) 。

基本變量類型所占大小如下圖,不同的系統(tǒng)的區(qū)別在于long和point類型的大小:

圖片圖片

內(nèi)存對(duì)齊的重要性主要體現(xiàn)在以下幾個(gè)方面:一是提高訪問速度,現(xiàn)代處理器在訪問內(nèi)存時(shí),通常是以一定的塊大小(如 4 字節(jié)、8 字節(jié)等)進(jìn)行讀取的。如果數(shù)據(jù)是對(duì)齊的,處理器可以一次讀取到完整的數(shù)據(jù),而不需要進(jìn)行額外的處理。相反,如果數(shù)據(jù)未對(duì)齊,可能需要多次讀取內(nèi)存,并進(jìn)行數(shù)據(jù)拼接,這會(huì)大大降低訪問速度 。二是硬件要求,某些處理器架構(gòu)對(duì)數(shù)據(jù)的對(duì)齊有嚴(yán)格要求,如果數(shù)據(jù)未對(duì)齊,可能會(huì)導(dǎo)致硬件異常或性能下降 。三是優(yōu)化存儲(chǔ)空間,合理的內(nèi)存對(duì)齊可以減少內(nèi)存碎片的產(chǎn)生,提高內(nèi)存的利用率 。例如,在結(jié)構(gòu)體中,如果各個(gè)成員按照其自身的對(duì)齊要求進(jìn)行排列,可以減少結(jié)構(gòu)體整體的大小,節(jié)省內(nèi)存空間 。

Part4.頁面置換算法

當(dāng)物理內(nèi)存已滿,而又需要加載新的頁面時(shí),操作系統(tǒng)就需要決定將哪個(gè)頁面從內(nèi)存中置換出去,這就用到了頁面置換算法 。頁面置換算法的目標(biāo)是盡可能減少缺頁中斷的次數(shù),提高系統(tǒng)性能。

圖片圖片

4.1 LRU(最近最少使用)算法

LRU 算法的核心思想是:如果一個(gè)頁面在最近一段時(shí)間內(nèi)沒有被訪問,那么在未來它被訪問的概率也較低 ,所以當(dāng)內(nèi)存已滿需要置換頁面時(shí),就選擇最近最少使用的頁面淘汰出去 。

假設(shè)內(nèi)存中最多能容納 3 個(gè)頁面,頁面訪問序列為 1, 2, 3, 4, 2, 1, 5, 6, 2, 1, 2, 3, 7, 6, 3, 2, 1, 2, 3, 6 。

  1. 最初,內(nèi)存為空,依次訪問頁面 1, 2, 3,此時(shí)內(nèi)存中頁面為 1, 2, 3。
  2. 當(dāng)訪問頁面 4 時(shí),內(nèi)存已滿,需要置換頁面。根據(jù) LRU 算法,最近最少使用的頁面是 1(因?yàn)?1 最早進(jìn)入內(nèi)存且之后未被訪問),所以將 1 置換出去,內(nèi)存中頁面變?yōu)?4, 2, 3 。
  3. 接著訪問頁面 2,2 在內(nèi)存中,不需要置換,更新 2 的訪問時(shí)間,使其成為最近使用的頁面,內(nèi)存中頁面順序變?yōu)?2, 4, 3 。
  4. 訪問頁面 1 時(shí),內(nèi)存中沒有 1,需要置換頁面。此時(shí)最近最少使用的是 3,將 3 置換出去,把 1 放入內(nèi)存,內(nèi)存中頁面變?yōu)?2, 4, 1 。
  5. 以此類推,隨著頁面的不斷訪問,LRU 算法會(huì)根據(jù)頁面的使用情況動(dòng)態(tài)地置換頁面,以保證內(nèi)存中始終是最近最常使用的頁面 。

在實(shí)際實(shí)現(xiàn) LRU 算法時(shí),常用的數(shù)據(jù)結(jié)構(gòu)是雙向鏈表和哈希表 。雙向鏈表用于維護(hù)頁面的訪問順序,鏈表頭部是最近使用的頁面,鏈表尾部是最近最少使用的頁面 。哈希表用于快速查找某個(gè)頁面是否在內(nèi)存中以及獲取其在雙向鏈表中的位置 。當(dāng)訪問一個(gè)頁面時(shí),如果頁面在內(nèi)存中,通過哈希表找到其在鏈表中的位置,將其移動(dòng)到鏈表頭部;如果頁面不在內(nèi)存中,先從鏈表尾部刪除最近最少使用的頁面(同時(shí)更新哈希表),再將新頁面插入到鏈表頭部并更新哈希表 。

4.2其他常見算法

FIFO(先進(jìn)先出)算法:這種算法非常直觀,它按照頁面進(jìn)入內(nèi)存的先后順序進(jìn)行置換 。即最早進(jìn)入內(nèi)存的頁面最先被置換出去 。還是以上面的頁面訪問序列為例,最初內(nèi)存為空,依次放入1, 2, 3 。當(dāng)訪問 4 時(shí),由于內(nèi)存已滿,根據(jù) FIFO 算法,最早進(jìn)入的 1 被置換出去,內(nèi)存變?yōu)?2, 3, 4 。FIFO 算法的優(yōu)點(diǎn)是實(shí)現(xiàn)簡單,但它沒有考慮頁面的使用頻率,可能會(huì)把一些仍然被頻繁訪問的頁面置換出去,導(dǎo)致缺頁率較高 。例如,如果有一個(gè)程序需要頻繁訪問最早進(jìn)入內(nèi)存的某個(gè)頁面,F(xiàn)IFO 算法就會(huì)不斷地將其置換出去又換進(jìn)來,增加了系統(tǒng)開銷 。

LFU(最不經(jīng)常使用)算法:LFU 算法根據(jù)頁面的訪問頻率來決定置換哪個(gè)頁面 。它為每個(gè)頁面設(shè)置一個(gè)計(jì)數(shù)器,每當(dāng)頁面被訪問時(shí),計(jì)數(shù)器加 1 。當(dāng)內(nèi)存已滿需要置換頁面時(shí),選擇計(jì)數(shù)器值最小(即訪問頻率最低)的頁面淘汰 。假設(shè)內(nèi)存中已有頁面 1, 2, 3,它們的訪問次數(shù)分別為 3, 2, 1 。當(dāng)需要置換頁面時(shí),LFU 算法會(huì)選擇訪問次數(shù)為 1 的頁面 3 進(jìn)行置換 。LFU 算法能較好地反映頁面的實(shí)際使用情況,但它需要維護(hù)每個(gè)頁面的訪問次數(shù),實(shí)現(xiàn)相對(duì)復(fù)雜一些 。

Part5.虛擬內(nèi)存與進(jìn)程

5.1進(jìn)程的虛擬地址空間布局

圖片圖片

在 Linux 系統(tǒng)中,每個(gè)進(jìn)程都擁有自己獨(dú)立的虛擬地址空間,就像每個(gè)租客都有自己獨(dú)立的房間,互不干擾 。以 32 位系統(tǒng)為例,這個(gè)虛擬地址空間的大小為 4GB(2^32 字節(jié)),它被劃分為不同的區(qū)域,每個(gè)區(qū)域都有特定的用途和特點(diǎn) :

  1. 代碼段(Text Segment):這是程序的只讀部分,存放著程序的機(jī)器指令(也就是我們編寫的代碼被編譯后的二進(jìn)制形式)和只讀數(shù)據(jù),如字符串常量 。它的特點(diǎn)是只讀,這就像是一本被鎖起來的書,只能讀取內(nèi)容,不能修改,這樣可以防止程序在運(yùn)行時(shí)意外修改自身的代碼,保證了程序執(zhí)行的穩(wěn)定性和安全性 。每個(gè)進(jìn)程只有一個(gè)代碼段,并且在內(nèi)存中是共享的,例如多個(gè)進(jìn)程同時(shí)運(yùn)行同一個(gè)可執(zhí)行文件,它們共享的就是同一段代碼段 。
  2. 數(shù)據(jù)段(Data Segment):用于存放已初始化的全局變量和靜態(tài)變量 。這些變量在程序編譯時(shí)就已經(jīng)確定了初始值,并且在程序運(yùn)行期間一直存在 。數(shù)據(jù)段屬于靜態(tài)內(nèi)存分配,一旦程序加載到內(nèi)存中,數(shù)據(jù)段的大小就基本固定下來了 。比如在C語言中定義的int global_variable = 10;,這個(gè)global_variable就存放在數(shù)據(jù)段中 。
  3. BSS 段(Block Started by Symbol Segment):主要存放未初始化的全局變量和靜態(tài)變量 。與數(shù)據(jù)段不同,BSS 段在可執(zhí)行文件中并不占用實(shí)際的磁盤空間,只是記錄了這些變量所需的空間大小 。在程序開始執(zhí)行前,系統(tǒng)會(huì)自動(dòng)將 BSS 段中的變量初始化為 0 。例如,在 C 語言中定義的int uninitialized_global_variable;,它就位于 BSS 段 。BSS 段屬于靜態(tài)內(nèi)存分配,它的存在可以節(jié)省可執(zhí)行文件的大小,因?yàn)椴恍枰獮槲闯跏蓟淖兞吭诖疟P上存儲(chǔ)初始值 。
  4. 堆(Heap Segment):是進(jìn)程運(yùn)行時(shí)動(dòng)態(tài)分配內(nèi)存的區(qū)域,通過malloc、calloc、realloc等函數(shù)進(jìn)行內(nèi)存分配,使用free函數(shù)釋放內(nèi)存 。堆的大小是動(dòng)態(tài)變化的,可以根據(jù)程序的需求進(jìn)行擴(kuò)張或縮減 。它從低地址向高地址增長,就像一個(gè)可以不斷向上堆疊物品的貨架 。在 C語言中,使用malloc函數(shù)分配內(nèi)存時(shí),例如int *p = (int *)malloc(10 * sizeof(int));,這 10 個(gè)int類型大小的內(nèi)存空間就是從堆中分配出來的 。不過,如果在使用堆內(nèi)存時(shí),忘記釋放不再使用的內(nèi)存,就會(huì)導(dǎo)致內(nèi)存泄漏 。
  5. 棧(Stack Segment):用于存放函數(shù)的局部變量、函數(shù)調(diào)用的參數(shù)、返回地址等信息 。它是一種后進(jìn)先出(LIFO,Last In First Out)的數(shù)據(jù)結(jié)構(gòu),就像一個(gè)放盤子的棧,最后放上去的盤子最先被拿走 。棧由操作系統(tǒng)自動(dòng)管理,當(dāng)函數(shù)被調(diào)用時(shí),相關(guān)的局部變量和參數(shù)等會(huì)被壓入棧中;函數(shù)執(zhí)行結(jié)束后,這些數(shù)據(jù)會(huì)從棧中彈出,自動(dòng)釋放內(nèi)存 。棧的大小通常是固定的,在Linux系統(tǒng)中,一般默認(rèn)的棧大小為 8MB 。如果在函數(shù)中定義了過多的局部變量或者遞歸調(diào)用層數(shù)過深,導(dǎo)致棧空間不夠用,就會(huì)發(fā)生棧溢出(Stack Overflow)錯(cuò)誤 。
  6. 文件映射段(Memory - Mapped Segment):用于映射文件、共享內(nèi)存、動(dòng)態(tài)鏈接庫等 。通過mmap系統(tǒng)調(diào)用可以將文件的一部分或全部映射到進(jìn)程的虛擬地址空間中,使得進(jìn)程可以像訪問內(nèi)存一樣訪問文件,提高了文件 I/O 的效率 。同時(shí),共享內(nèi)存也利用了這個(gè)區(qū)域,多個(gè)進(jìn)程可以通過映射同一個(gè)共享內(nèi)存區(qū)域來實(shí)現(xiàn)數(shù)據(jù)共享和進(jìn)程間通信 。動(dòng)態(tài)鏈接庫在加載時(shí)也會(huì)被映射到這個(gè)區(qū)域,實(shí)現(xiàn)代碼和數(shù)據(jù)的共享 。

5.2進(jìn)程內(nèi)存分配

進(jìn)程在運(yùn)行時(shí),經(jīng)常需要?jiǎng)討B(tài)分配內(nèi)存來存儲(chǔ)各種數(shù)據(jù) 。在 C 語言中,最常用的內(nèi)存分配函數(shù)就是 malloc 。當(dāng)我們調(diào)用 malloc 函數(shù)時(shí),它會(huì)在堆上為我們分配一塊指定大小的內(nèi)存空間 。例如, int *p = (malloc(10 * sizeof(int))); 這行代碼就會(huì)在堆上分配 10 個(gè) int 類型大小的內(nèi)存空間,并返回一個(gè)指向這塊內(nèi)存起始地址的指針p 。

那么,malloc函數(shù)是如何在堆上分配內(nèi)存的呢?實(shí)際上,malloc并不是直接與操作系統(tǒng)的內(nèi)存管理機(jī)制打交道,而是通過 glibc(GNU C Library)來實(shí)現(xiàn)的 。在 glibc 中,維護(hù)了一個(gè)內(nèi)存池,當(dāng)我們調(diào)用malloc時(shí),它首先會(huì)在內(nèi)存池中查找是否有足夠的空閑內(nèi)存來滿足請(qǐng)求 。如果內(nèi)存池中有足夠的空閑內(nèi)存,就直接從內(nèi)存池中分配內(nèi)存,并返回相應(yīng)的指針 。這樣可以減少系統(tǒng)調(diào)用的開銷,提高內(nèi)存分配的效率 。因?yàn)橄到y(tǒng)調(diào)用涉及到用戶態(tài)和內(nèi)核態(tài)的切換,這種切換會(huì)帶來一定的性能損耗 。

然而,如果內(nèi)存池中的空閑內(nèi)存不足以滿足請(qǐng)求,malloc函數(shù)就會(huì)借助系統(tǒng)調(diào)用與操作系統(tǒng)進(jìn)行交互 。在 Linux 系統(tǒng)中,主要涉及到兩個(gè)系統(tǒng)調(diào)用:brk和mmap 。

brk 系統(tǒng)調(diào)用:brk系統(tǒng)調(diào)用通過移動(dòng)程序數(shù)據(jù)段的結(jié)束地址(也就是 “堆頂” 指針)來增加堆的大小,從而分配新的內(nèi)存 。例如,假設(shè)當(dāng)前堆的大小為 100 字節(jié),當(dāng)調(diào)用brk函數(shù)并傳入一個(gè)大于當(dāng)前堆頂?shù)刂返闹担?150 字節(jié)時(shí),堆就會(huì)擴(kuò)展到 150 字節(jié),新增加的 50 字節(jié)內(nèi)存就可以用于分配 。brk分配的內(nèi)存是連續(xù)的,適合小塊內(nèi)存的頻繁分配和釋放 。但是,由于brk分配的內(nèi)存是基于堆的連續(xù)擴(kuò)展,如果頻繁地分配和釋放小塊內(nèi)存,可能會(huì)導(dǎo)致堆內(nèi)存碎片化,即堆中出現(xiàn)很多不連續(xù)的空閑小內(nèi)存塊,這些小內(nèi)存塊可能無法滿足后續(xù)較大內(nèi)存塊的分配請(qǐng)求 。例如,先分配了一個(gè) 10 字節(jié)的內(nèi)存塊,再釋放它,然后又分配一個(gè) 20 字節(jié)的內(nèi)存塊,這樣在堆中就可能會(huì)產(chǎn)生一個(gè) 10 字節(jié)的空閑小內(nèi)存塊,而如果后續(xù)需要分配一個(gè) 30 字節(jié)的內(nèi)存塊,由于這個(gè) 10 字節(jié)的空閑塊無法滿足需求,且與其他空閑塊不連續(xù),就可能導(dǎo)致分配失敗 。

mmap 系統(tǒng)調(diào)用:mmap系統(tǒng)調(diào)用則是通過在文件映射區(qū)域分配一塊內(nèi)存來滿足請(qǐng)求 。它可以將文件的全部或部分內(nèi)容映射到進(jìn)程的虛擬內(nèi)存中,進(jìn)程可以像訪問內(nèi)存一樣讀寫文件的內(nèi)容,而不需要顯式地進(jìn)行文件 I/O 操作 。同時(shí),mmap也可以創(chuàng)建匿名映射,即不與任何文件關(guān)聯(lián)的內(nèi)存映射,用于在進(jìn)程間共享內(nèi)存或作為大塊內(nèi)存的分配器 。通常情況下,當(dāng)請(qǐng)求的內(nèi)存大小小于一定閾值(在大多數(shù)系統(tǒng)中,這個(gè)閾值通常為 128KB)時(shí),malloc函數(shù)會(huì)優(yōu)先使用brk系統(tǒng)調(diào)用來分配內(nèi)存;當(dāng)請(qǐng)求的內(nèi)存大小大于這個(gè)閾值時(shí),則會(huì)使用mmap系統(tǒng)調(diào)用 。這是因?yàn)閙map分配內(nèi)存的開銷相對(duì)較大,對(duì)于小塊內(nèi)存的分配不太劃算,而對(duì)于大塊內(nèi)存的分配,mmap可以避免堆內(nèi)存碎片化的問題,并且能更好地管理和釋放內(nèi)存 。例如,當(dāng)需要分配一個(gè) 1MB 的內(nèi)存塊時(shí),使用mmap可以直接在文件映射區(qū)域分配一塊連續(xù)的 1MB 內(nèi)存,而如果使用brk,可能需要多次擴(kuò)展堆,并且容易導(dǎo)致堆內(nèi)存碎片化 。

Part6.交換空間

6.1交換空間的作用

交換空間(swap space)在虛擬內(nèi)存中扮演著至關(guān)重要的角色,它就像是一個(gè) “內(nèi)存儲(chǔ)備倉庫” 。當(dāng)系統(tǒng)的物理內(nèi)存(RAM)不足以滿足所有正在運(yùn)行的進(jìn)程和應(yīng)用程序的內(nèi)存需求時(shí),交換空間就會(huì)發(fā)揮作用 。

具體來說,Linux 內(nèi)核會(huì)將那些暫時(shí)不活躍(也就是很長時(shí)間沒有被訪問)的內(nèi)存頁(pages)從物理內(nèi)存轉(zhuǎn)移到交換空間(通常是硬盤上的特定區(qū)域) ,這個(gè)過程被稱為 “換出”(swapping out)或 “頁面置換”(paging out) 。通過這種方式,物理內(nèi)存中就騰出了空間,可供那些更活躍、更急需內(nèi)存的進(jìn)程使用 。

例如,當(dāng)你同時(shí)打開了多個(gè)大型應(yīng)用程序,如瀏覽器、視頻編輯軟件、音樂播放器等,物理內(nèi)存可能會(huì)被迅速耗盡 。此時(shí),系統(tǒng)會(huì)將一些暫時(shí)不需要訪問的內(nèi)存數(shù)據(jù),比如音樂播放器當(dāng)前沒有播放的音頻數(shù)據(jù)、瀏覽器中暫時(shí)未顯示的網(wǎng)頁緩存數(shù)據(jù)等,轉(zhuǎn)移到交換空間中 。這樣,其他更需要內(nèi)存的操作,如視頻編輯軟件的實(shí)時(shí)預(yù)覽、瀏覽器加載新的網(wǎng)頁,就可以在有限的物理內(nèi)存中順利進(jìn)行 。

而當(dāng)進(jìn)程需要訪問已經(jīng)被交換到磁盤上的內(nèi)存頁時(shí),就會(huì)發(fā)生 “缺頁中斷”(page fault) 。這時(shí),操作系統(tǒng)會(huì)從交換空間中把相應(yīng)的內(nèi)存頁讀取回物理內(nèi)存,這個(gè)過程被稱為 “換入”(swapping in)或 “頁面調(diào)入”(paging in) 。

雖然交換空間能夠在物理內(nèi)存不足時(shí),提供額外的內(nèi)存支持,防止系統(tǒng)因內(nèi)存耗盡而崩潰,讓系統(tǒng)仍然可以繼續(xù)運(yùn)行 。但是,由于硬盤的讀寫速度遠(yuǎn)遠(yuǎn)低于內(nèi)存的讀寫速度,頻繁地使用交換空間會(huì)導(dǎo)致系統(tǒng)性能明顯下降 。所以,交換空間只是物理內(nèi)存的一種補(bǔ)充手段,理想情況下,系統(tǒng)應(yīng)該有足夠的物理內(nèi)存,盡量減少對(duì)交換空間的依賴 。

6.2交換空間的類型

交換空間主要有兩種類型:交換分區(qū)和交換文件,它們各自有不同的特點(diǎn)。

①交換分區(qū):交換分區(qū)是硬盤上專門劃分出來用于交換空間的獨(dú)立分區(qū),在系統(tǒng)安裝過程中就可以進(jìn)行設(shè)置 。比如在安裝 Linux 系統(tǒng)時(shí),通過分區(qū)工具(如 fdisk、parted 等)將硬盤的一部分空間指定為交換分區(qū),其分區(qū)類型一般為 “Linux swap” 。它獨(dú)立于系統(tǒng)的主文件系統(tǒng)運(yùn)行,就像一個(gè)獨(dú)立的小倉庫,專門用來存放從物理內(nèi)存中換出的內(nèi)存頁 。

  • 優(yōu)點(diǎn):交換分區(qū)的效率相對(duì)較高,因?yàn)樗谟脖P上是連續(xù)的空間,沒有文件系統(tǒng)的額外開銷 。而且在安裝階段創(chuàng)建時(shí),通常會(huì)被放置在硬盤驅(qū)動(dòng)器的較快區(qū)域(更靠近外邊緣),這使得數(shù)據(jù)的訪問和寫入速度更快 。同時(shí),它與主文件系統(tǒng)分開,能有效防止碎片化,減少對(duì)系統(tǒng)文件的干擾 ,就像一個(gè)獨(dú)立的小房間,不會(huì)和其他雜物混在一起 。
  • 缺點(diǎn):一旦創(chuàng)建,交換分區(qū)的大小就相對(duì)固定,如果想要更改其大小,就需要對(duì)磁盤進(jìn)行重新分區(qū) 。這是一個(gè)比較復(fù)雜且有風(fēng)險(xiǎn)的操作,可能會(huì)導(dǎo)致數(shù)據(jù)丟失或系統(tǒng)故障 ,就好比你要擴(kuò)大一個(gè)房間的面積,需要對(duì)整個(gè)房子的結(jié)構(gòu)進(jìn)行大改造,很容易出問題 。另外,如果交換分區(qū)設(shè)置得過大,而實(shí)際使用量很少,就會(huì)浪費(fèi)磁盤空間;反之,如果設(shè)置得過小,在內(nèi)存需求高峰期可能無法滿足系統(tǒng)的需求,限制系統(tǒng)性能 。

②交換文件:交換文件是在系統(tǒng)現(xiàn)有文件系統(tǒng)中的一種特殊文件,其作用和交換分區(qū)相同 。可以通過命令(如dd命令創(chuàng)建文件,再用mkswap命令將其設(shè)置為交換文件)在需要時(shí)創(chuàng)建 。例如,使用dd if=/dev/zero of=/swapfile bs=1024 count=8192命令創(chuàng)建一個(gè)大小約為 8MB 的交換文件/swapfile ,然后使用mkswap /swapfile將其初始化為交換文件 。

  • 優(yōu)點(diǎn):交換文件具有很高的靈活性 。它可以根據(jù)系統(tǒng)的實(shí)際需求隨時(shí)調(diào)整大小、刪除或移動(dòng) 。比如,當(dāng)系統(tǒng)內(nèi)存需求突然增加時(shí),可以增大交換文件的大小;當(dāng)內(nèi)存需求減少時(shí),又可以減小或刪除交換文件,這使得它非常適合內(nèi)存需求不斷變化的系統(tǒng) ,就像一個(gè)可以隨時(shí)調(diào)整大小的收納箱 。此外,交換文件使用現(xiàn)有文件系統(tǒng)中的空間,在不使用時(shí)不會(huì)浪費(fèi)磁盤空間,并且可以根據(jù)內(nèi)存需求動(dòng)態(tài)增長 。
  • 缺點(diǎn):由于交換文件存在于文件系統(tǒng)中,文件系統(tǒng)的管理和維護(hù)會(huì)帶來一些額外的開銷,而且可能會(huì)產(chǎn)生碎片化問題 。在傳統(tǒng)的文件系統(tǒng)中,交換文件的性能通常比交換分區(qū)慢 。不過,隨著現(xiàn)代文件系統(tǒng)(如 ext4、Btrfs 等)的發(fā)展,這些問題得到了很大程度的緩解,現(xiàn)在交換文件和交換分區(qū)的性能差異已經(jīng)不是很明顯 。但在高負(fù)載情況下,大量的交換操作仍可能對(duì)文件系統(tǒng)的正常文件操作產(chǎn)生干擾 。

6.3 Swappiness 參數(shù)

Swappiness 是 Linux 操作系統(tǒng)中一個(gè)非常重要的參數(shù),它控制著內(nèi)核將內(nèi)存頁交換到磁盤(也就是使用交換空間)的傾向程度 。其取值范圍是 0 - 100,代表的是一個(gè)百分比 。

當(dāng) Swappiness 的值為 0 時(shí),意味著內(nèi)核盡可能地避免使用交換空間,即使物理內(nèi)存非常緊張,也會(huì)優(yōu)先嘗試其他方式來滿足內(nèi)存需求,比如回收緩存等 。而當(dāng) Swappiness 的值為 100 時(shí),則表示內(nèi)核總是傾向于使用交換空間,即使物理內(nèi)存還有較多的空閑空間,也可能會(huì)將內(nèi)存頁交換到磁盤 。Swappiness 參數(shù)的調(diào)整對(duì)于系統(tǒng)性能有著直接且顯著的影響 。

在系統(tǒng)內(nèi)存緊張時(shí),合理設(shè)置 Swappiness 值可以幫助優(yōu)化系統(tǒng)的反應(yīng)速度和整體性能 。例如,對(duì)于高負(fù)載的數(shù)據(jù)庫服務(wù)器,由于數(shù)據(jù)庫操作對(duì)內(nèi)存的讀寫速度要求極高,頻繁的磁盤 I/O 操作會(huì)嚴(yán)重影響性能,因此通常會(huì)將 Swappiness 值設(shè)置得很低(如 10 - 20) ,以減少交換的使用頻率,避免因頻繁訪問交換空間(磁盤)導(dǎo)致的性能瓶頸 。而在一些內(nèi)存較大且對(duì)內(nèi)存使用不太敏感的系統(tǒng)中,適當(dāng)提高 Swappiness 值(如設(shè)置為 30 - 50) ,可以有效利用系統(tǒng)資源,將一些暫時(shí)不用的內(nèi)存頁交換到磁盤,減少因內(nèi)存資源閑置而造成的浪費(fèi) 。

調(diào)整方法:查看當(dāng)前系統(tǒng)的 Swappiness 值,可以使用命令cat /proc/sys/vm/swappiness 。如果想要臨時(shí)修改 Swappiness 值(重啟后失效) ,可以使用sysctl命令,例如將 Swappiness 值臨時(shí)設(shè)置為 10 ,命令為sysctl vm.swappiness=10 。如果希望永久修改 Swappiness 值,則需要編輯/etc/sysctl.conf文件,在文件中添加或修改vm.swappiness = 10這一行,然后執(zhí)行sysctl -p使修改生效 。在調(diào)整 Swappiness 參數(shù)時(shí),需要謹(jǐn)慎操作,因?yàn)椴缓线m的設(shè)置可能會(huì)帶來一些問題 。

如果設(shè)置得過小,雖然減少了交換空間的使用,但可能會(huì)導(dǎo)致物理內(nèi)存被過度使用,當(dāng)物理內(nèi)存耗盡時(shí),系統(tǒng)可能會(huì)觸發(fā) “內(nèi)存不足(OOM,Out - Of - Memory)” 殺手機(jī)制,強(qiáng)制殺掉一些進(jìn)程來釋放內(nèi)存,這可能會(huì)影響系統(tǒng)的正常運(yùn)行 。而如果設(shè)置得過大,系統(tǒng)會(huì)頻繁地進(jìn)行內(nèi)存頁的交換操作,由于磁盤 I/O 速度遠(yuǎn)低于內(nèi)存速度,會(huì)導(dǎo)致系統(tǒng)性能大幅下降,用戶會(huì)明顯感覺到系統(tǒng)變得卡頓 。所以,在調(diào)整 Swappiness 參數(shù)之前,需要對(duì)系統(tǒng)的內(nèi)存使用情況、應(yīng)用程序的特點(diǎn)等進(jìn)行充分的了解和分析,并且在調(diào)整后密切監(jiān)控系統(tǒng)的性能指標(biāo),如內(nèi)存使用率、磁盤 I/O 情況、系統(tǒng)響應(yīng)時(shí)間等,以確保調(diào)整后的參數(shù)能夠使系統(tǒng)達(dá)到最佳的性能狀態(tài) 。

Part7.內(nèi)存管理單元(MMU)

7.1 MMU的功能

內(nèi)存管理單元(Memory Management Unit,MMU)是計(jì)算機(jī)硬件中負(fù)責(zé)處理中央處理器(CPU)內(nèi)存訪問請(qǐng)求的關(guān)鍵組件 ,在虛擬內(nèi)存管理中扮演著核心角色。

圖片圖片

它的首要功能是地址轉(zhuǎn)換,這是實(shí)現(xiàn)虛擬內(nèi)存機(jī)制的基礎(chǔ)。在現(xiàn)代操作系統(tǒng)中,每個(gè)進(jìn)程都擁有自己獨(dú)立的虛擬地址空間,程序在運(yùn)行時(shí)使用的是虛擬地址 。而 MMU 的職責(zé)就是將這些虛擬地址轉(zhuǎn)換為實(shí)際的物理地址,以便 CPU 能夠正確訪問內(nèi)存中的數(shù)據(jù) 。例如,在 x86 架構(gòu)的計(jì)算機(jī)中,當(dāng)一個(gè)進(jìn)程嘗試訪問虛擬地址 0x12345678 時(shí),MMU 會(huì)通過查詢頁表(Page Table),將這個(gè)虛擬地址映射到對(duì)應(yīng)的物理地址上,如 0x87654321 ,從而實(shí)現(xiàn)進(jìn)程對(duì)內(nèi)存的訪問 。

MMU 還承擔(dān)著內(nèi)存保護(hù)的重要任務(wù) 。它通過硬件機(jī)制來確保進(jìn)程只能訪問被授權(quán)的內(nèi)存區(qū)域,防止進(jìn)程間的非法內(nèi)存訪問 。比如,MMU 可以為每個(gè)內(nèi)存頁面設(shè)置訪問權(quán)限,如只讀、讀寫、執(zhí)行等 。當(dāng)一個(gè)進(jìn)程試圖以不被允許的方式訪問內(nèi)存時(shí),MMU 會(huì)觸發(fā)異常,通知操作系統(tǒng)進(jìn)行處理 。假設(shè)一個(gè)進(jìn)程嘗試寫入一個(gè)被設(shè)置為只讀的內(nèi)存頁面,MMU 就會(huì)檢測到這個(gè)非法操作,并產(chǎn)生一個(gè)內(nèi)存訪問錯(cuò)誤異常,操作系統(tǒng)可以根據(jù)這個(gè)異常來采取相應(yīng)的措施,如終止該進(jìn)程,以保護(hù)系統(tǒng)的穩(wěn)定性和安全性 。這種內(nèi)存保護(hù)機(jī)制對(duì)于多任務(wù)操作系統(tǒng)來說至關(guān)重要,它使得多個(gè)進(jìn)程能夠在同一臺(tái)計(jì)算機(jī)上安全、穩(wěn)定地運(yùn)行,避免了因一個(gè)進(jìn)程的錯(cuò)誤而導(dǎo)致整個(gè)系統(tǒng)崩潰的情況 。

mmu開啟以后會(huì)有以下特點(diǎn):

  • 多個(gè)程序獨(dú)立運(yùn)行
  • 虛擬地址是連續(xù)的(物理內(nèi)存可以有碎片)
  • 允許操作系統(tǒng)管理內(nèi)存

下圖顯示的系統(tǒng)說明了內(nèi)存的虛擬和物理視圖。單個(gè)系統(tǒng)中的不同處理器和設(shè)備可能具有不同的虛擬地址映射和物理地址映射。操作系統(tǒng)編寫程序,使MMU在這兩個(gè)內(nèi)存視圖之間進(jìn)行轉(zhuǎn)換:

圖片圖片

要做到這一點(diǎn),虛擬內(nèi)存系統(tǒng)中的硬件必須提供地址轉(zhuǎn)換,即將處理器發(fā)出的虛擬地址轉(zhuǎn)換為主內(nèi)存中的物理地址。MMU使用虛擬地址中最重要的位來索引轉(zhuǎn)換表中的條目,并確定正在訪問哪個(gè)塊。MMU將代碼和數(shù)據(jù)的虛擬地址轉(zhuǎn)換為實(shí)際系統(tǒng)中的物理地址。該轉(zhuǎn)換將在硬件中自動(dòng)執(zhí)行,并且對(duì)應(yīng)用程序是透明的。除了地址轉(zhuǎn)換之外,MMU還可以控制每個(gè)內(nèi)存區(qū)域的內(nèi)存訪問權(quán)限、內(nèi)存順序和緩存策略。

MMU對(duì)執(zhí)行的任務(wù)或應(yīng)用程序可以不了解系統(tǒng)的物理內(nèi)存映射,也可以不了解同時(shí)運(yùn)行的其他程序。每個(gè)程序可以使用相同的虛擬內(nèi)存地址空間。即使物理內(nèi)存是碎片化的,還可以使用一個(gè)連續(xù)的虛擬內(nèi)存映射。此虛擬地址空間與系統(tǒng)中內(nèi)存的實(shí)際物理映射分開的。應(yīng)用程序被編寫、編譯和鏈接,以在虛擬內(nèi)存空間中運(yùn)行。

圖片圖片

如上圖所示,TLB是MMU中最近訪問的頁面翻譯的緩存。對(duì)于處理器執(zhí)行的每個(gè)內(nèi)存訪問,MMU將檢查轉(zhuǎn)換是否緩存在TLB中。如果所請(qǐng)求的地址轉(zhuǎn)換在TLB中導(dǎo)致命中,則該地址的翻譯立即可用。TLB本質(zhì)是一塊高速緩存。數(shù)據(jù)cache緩存地址(虛擬地址或者物理地址)和數(shù)據(jù)。TLB緩存虛擬地址和其映射的物理地址。TLB根據(jù)虛擬地址查找cache,它沒得選,只能根據(jù)虛擬地址查找。所以TLB是一個(gè)虛擬高速緩存。

每個(gè)TLB entry通常不僅包含物理地址和虛擬地址,還包含諸如內(nèi)存類型、緩存策略、訪問權(quán)限、地址空間ID(ASID)和虛擬機(jī)ID(VMID)等屬性。如果TLB不包含處理器發(fā)出的虛擬地址的有效轉(zhuǎn)換,稱為TLB Miss,則將執(zhí)行外部轉(zhuǎn)換頁表查找。MMU內(nèi)的專用硬件使它能夠讀取內(nèi)存中的轉(zhuǎn)換表。

然后,如果翻譯頁表沒有導(dǎo)致頁面故障,則可以將新加載的翻譯緩存在TLB中,以便進(jìn)行后續(xù)的重用。簡單概括一下就是:硬件存在TLB后,虛擬地址到物理地址的轉(zhuǎn)換過程發(fā)生了變化。虛擬地址首先發(fā)往TLB確認(rèn)是否命中cache,如果cache hit直接可以得到物理地址。否則,一級(jí)一級(jí)查找頁表獲取物理地址。并將虛擬地址和物理地址的映射關(guān)系緩存到TLB中。

如果操作系統(tǒng)修改了可能已經(jīng)緩存在TLB中的轉(zhuǎn)換的entry,那么操作系統(tǒng)就有責(zé)任使這些未更新的TLB entry invaild。當(dāng)執(zhí)行A64代碼時(shí),有一個(gè)TLBI,它是一個(gè)TLB無效的指令:

TLBI <type><level>{IS} {, <Xt>}

TLB可以保存固定數(shù)量的entry。可以通過由轉(zhuǎn)換頁表遍歷引起的外部內(nèi)存訪問次數(shù)和獲得高TLB命中率來獲得最佳性能。ARMv8-A體系結(jié)構(gòu)提供了一個(gè)被稱為連續(xù)塊entry的特性,以有效地利用TLB空間。轉(zhuǎn)換表每個(gè)entry都包含一個(gè)連續(xù)的位。當(dāng)設(shè)置時(shí),這個(gè)位向TLB發(fā)出信號(hào),表明它可以緩存一個(gè)覆蓋多個(gè)塊轉(zhuǎn)換的單個(gè)entry。查找可以索引到連續(xù)塊所覆蓋的地址范圍中的任何位置。因此,TLB可以為已定義的地址范圍緩存一個(gè)entry從而可以在TLB中存儲(chǔ)更大范圍的虛擬地址。

7.2 MMU 與 TLB(轉(zhuǎn)譯后備緩沖器)

轉(zhuǎn)譯后備緩沖器(Translation Lookaside Buffer,TLB)是 MMU 中的一個(gè)高速緩存 ,它在虛擬內(nèi)存管理中與 MMU 協(xié)同工作,極大地提高了地址轉(zhuǎn)換的效率 。

TLB 中存儲(chǔ)了最近使用的頁表項(xiàng)(Page Table Entry,PTE) ,這些頁表項(xiàng)記錄了虛擬地址到物理地址的映射關(guān)系 。當(dāng) CPU 需要進(jìn)行地址轉(zhuǎn)換時(shí),MMU 首先會(huì)在 TLB 中查找對(duì)應(yīng)的虛擬地址 。如果在 TLB 中找到了匹配的頁表項(xiàng)(即 TLB 命中) ,MMU 就可以直接獲取到對(duì)應(yīng)的物理地址,而無需訪問內(nèi)存中的頁表 ,這大大縮短了地址轉(zhuǎn)換的時(shí)間 。例如,假設(shè) CPU 需要訪問虛擬地址 0x12345678,MMU 會(huì)先在 TLB 中查找這個(gè)虛擬地址 。如果 TLB 中已經(jīng)緩存了該虛擬地址對(duì)應(yīng)的頁表項(xiàng),MMU 就能立即得到物理地址,直接從內(nèi)存中讀取數(shù)據(jù),整個(gè)過程非常快速 。

然而,如果在 TLB 中沒有找到匹配的頁表項(xiàng)(即 TLB 未命中) ,MMU 就需要從內(nèi)存中的頁表中讀取相應(yīng)的頁表項(xiàng) 。這個(gè)過程相對(duì)較慢,因?yàn)閮?nèi)存訪問的速度遠(yuǎn)低于 TLB 的訪問速度 。在從內(nèi)存中讀取到頁表項(xiàng)后,MMU 會(huì)將其存入 TLB 中,以便下次訪問相同的虛擬地址時(shí)能夠快速命中 。假設(shè)在上述例子中,TLB 未命中,MMU 就會(huì)訪問內(nèi)存中的頁表,找到虛擬地址 0x12345678 對(duì)應(yīng)的物理地址 。然后,MMU 會(huì)把這個(gè)頁表項(xiàng)存入 TLB 中,當(dāng)下次 CPU 再次訪問這個(gè)虛擬地址時(shí),就可以在 TLB 中快速找到對(duì)應(yīng)的物理地址,提高了地址轉(zhuǎn)換的效率 。

LB的原理如下:

  1. 當(dāng)CPU訪問一個(gè)虛擬地址時(shí),首先檢查TLB中是否有對(duì)應(yīng)的頁表項(xiàng)。
  2. 如果TLB中有對(duì)應(yīng)的頁表項(xiàng)(即命中),則直接從TLB獲取物理地址。
  3. 如果TLB中沒有對(duì)應(yīng)的頁表項(xiàng)(即未命中),則需要訪問內(nèi)存來獲取正確的頁表項(xiàng)。
  4. 在未命中情況下,操作系統(tǒng)會(huì)進(jìn)行相應(yīng)處理,從主存中獲取正確的頁表項(xiàng),并將其加載到TLB中以供后續(xù)使用。
  5. 一旦正確的頁表項(xiàng)加載到TLB中,CPU再次訪問相同虛擬地址時(shí)就可以直接在TLB中找到映射關(guān)系,提高了轉(zhuǎn)換效率。

TLB具有快速查找和高效緩存機(jī)制,能夠極大地減少查詢頁表所需的時(shí)間。然而,由于TLB是有限容量的,在大型程序或多任務(wù)環(huán)境下可能無法完全覆蓋所有需要轉(zhuǎn)換的頁面。當(dāng)發(fā)生TLB未命中時(shí),則會(huì)導(dǎo)致額外的內(nèi)存訪問開銷;操作系統(tǒng)會(huì)負(fù)責(zé)管理和維護(hù)TLB,包括緩存策略、TLB的刷新機(jī)制等。常見的緩存策略有全相聯(lián)、組相聯(lián)和直接映射等。可以說,TLB 就像是 MMU 的 “高速助手”,通過緩存常用的頁表項(xiàng),減少了 MMU 訪問內(nèi)存中頁表的次數(shù),從而顯著提高了虛擬地址到物理地址的轉(zhuǎn)換速度,進(jìn)而提升了整個(gè)系統(tǒng)的性能 。

Part8.內(nèi)存相關(guān)工具與命令

8.1查看內(nèi)存使用情況的命令

①free:這是一個(gè)非常基礎(chǔ)且常用的命令,用于快速查看系統(tǒng)內(nèi)存的使用情況,它的輸出結(jié)果是對(duì)/proc/meminfo文件信息的一個(gè)簡潔概述 。執(zhí)行free命令后,會(huì)得到如下格式的輸出(以字節(jié)為單位):

total        used        free      shared  buff/cache   available
Mem:      16355940352  2766014464 12694421504    126959616  909540432  13069271040
Swap:     2097147904    53391360  2043756544

其中,total表示總內(nèi)存大小;used表示已使用的內(nèi)存大小;free表示空閑內(nèi)存大小;shared表示共享內(nèi)存大小;buff/cache表示緩沖區(qū)和緩存所占用的內(nèi)存大小;available表示系統(tǒng)可用于分配給新進(jìn)程的內(nèi)存大小 。通過這些信息,我們可以快速了解系統(tǒng)內(nèi)存的整體使用狀況,判斷是否存在內(nèi)存不足或內(nèi)存使用不合理的情況 。如果used接近或超過total,且available較小,可能意味著系統(tǒng)內(nèi)存緊張,需要進(jìn)一步排查和優(yōu)化 。

②top:是一個(gè)動(dòng)態(tài)顯示系統(tǒng)資源使用情況的命令,類似于 Windows 系統(tǒng)中的任務(wù)管理器 。它不僅能實(shí)時(shí)展示內(nèi)存使用情況,還能查看 CPU 使用率、每個(gè)進(jìn)程的資源占用等詳細(xì)信息 。在終端中輸入top命令后,會(huì)進(jìn)入一個(gè)交互式界面,不斷實(shí)時(shí)更新系統(tǒng)狀態(tài) 。界面的主要部分包括:

top - 14:20:12 up 2 days,  1:23,  2 users,  load average: 0.00, 0.01, 0.05
Tasks: 152 total,   1 running, 151 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.3 us,  0.3 sy,  0.0 ni, 99.3 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  8161844 total,  7433924 free,   149744 used,   578176 buff/cache
KiB Swap:  2097148 total,  2097148 free,        0 used.  7663360 avail Mem

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
    1 root      20   0   12844   7448   4564 S   0.0  0.1   0:02.33 systemd
    2 root      20   0       0      0      0 S   0.0  0.0   0:00.00 kthreadd
  • 系統(tǒng)狀態(tài)信息:如系統(tǒng)當(dāng)前時(shí)間、系統(tǒng)已運(yùn)行時(shí)間、當(dāng)前登錄用戶數(shù)、系統(tǒng)負(fù)載(1 分鐘、5 分鐘、15 分鐘的平均負(fù)載) 。例如,top - 15:32:45 up 2 days, 3:12, 1 user, load average: 0.05, 0.03, 0.01,這里顯示了系統(tǒng)在 15:32:45 時(shí)的狀態(tài),已運(yùn)行 2 天 3 小時(shí) 12 分鐘,有 1 個(gè)用戶登錄,1 分鐘、5 分鐘、15 分鐘的平均負(fù)載分別為 0.05、0.03、0.01 。
  • 進(jìn)程狀態(tài)信息:展示所有進(jìn)程的總數(shù)、正在運(yùn)行的進(jìn)程數(shù)、睡眠的進(jìn)程數(shù)、停止的進(jìn)程數(shù)和僵尸進(jìn)程數(shù) 。比如,Tasks: 200 total, 2 running, 198 sleeping, 0 stopped, 0 zombie,表示共有 200 個(gè)進(jìn)程,2 個(gè)正在運(yùn)行,198 個(gè)處于睡眠狀態(tài),沒有停止和僵尸進(jìn)程 。
  • CPU 使用情況:顯示用戶空間(us)、內(nèi)核空間(sy)、改變過優(yōu)先級(jí)的進(jìn)程(ni)、空閑(id)、等待 I/O(wa)、硬中斷(hi)、軟中斷(si)、虛擬 CPU 等待實(shí)際 CPU(st)等各部分占用 CPU 的百分比 。例如,%Cpu(s): 0.5 us, 0.3 sy, 0.0 ni, 99.1 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st,表示用戶空間占用 CPU 0.5%,內(nèi)核空間占用 0.3%,空閑 CPU 為 99.1% 等 。
  • 內(nèi)存使用情況:呈現(xiàn)物理內(nèi)存(Mem)和交換內(nèi)存(Swap)的總量、已使用量、空閑量等 。例如,KiB Mem :16355940 total, 12694421 free, 2766014 used, 909540 buff/cache,KiB Swap:2097148 total, 2043756 free, 53391 used 。在top命令的交互界面中,還可以通過一些按鍵操作進(jìn)行更多的功能設(shè)置 。比如按M鍵可以根據(jù)駐留內(nèi)存大小對(duì)進(jìn)程進(jìn)行排序,方便快速找到占用內(nèi)存較大的進(jìn)程;按P鍵可以根據(jù) CPU 使用百分比對(duì)進(jìn)程排序;按1鍵可以顯示各個(gè)邏輯 CPU 的使用情況等 。

③htop:是top命令的增強(qiáng)版本,提供了更直觀、更豐富的信息展示 。它以彩色界面顯示,并且支持鼠標(biāo)操作,使得用戶交互更加便捷 。在htop界面中,不僅可以看到每個(gè)進(jìn)程的內(nèi)存實(shí)時(shí)使用率,還能詳細(xì)了解每個(gè)進(jìn)程的常駐內(nèi)存大小(RES)、程序總內(nèi)存大小(VIRT)、共享庫大小(SHR)等信息 。與top相比,htop的進(jìn)程列表可以水平及垂直滾動(dòng),方便查看更多進(jìn)程的詳細(xì)信息 。例如,在處理大量進(jìn)程的服務(wù)器環(huán)境中,htop能夠更輕松地定位到需要關(guān)注的進(jìn)程,并且其直觀的界面設(shè)計(jì)使得內(nèi)存使用情況一目了然,對(duì)于系統(tǒng)管理員來說是一個(gè)非常實(shí)用的工具 。

8.2內(nèi)存分析工具

Valgrind:是一款功能強(qiáng)大的內(nèi)存調(diào)試、內(nèi)存泄漏檢測以及性能分析工具 。它主要用于檢測 C 和 C++ 程序中的內(nèi)存錯(cuò)誤,包括內(nèi)存泄漏、非法內(nèi)存訪問(如越界訪問、使用未初始化的內(nèi)存等) 。例如,在一個(gè) C 語言程序中,如果使用malloc分配了內(nèi)存,但在程序結(jié)束時(shí)沒有調(diào)用free釋放內(nèi)存,就會(huì)導(dǎo)致內(nèi)存泄漏 。使用 Valgrind 可以很容易地檢測到這種問題 。假設(shè)我們有一個(gè)簡單的 C 程序test.c:

#include <stdio.h>
#include <stdlib.h>

int main() {
    int *p = (int *)malloc(10 * sizeof(int));
    // 這里沒有釋放p指向的內(nèi)存
    return 0;
}

編譯該程序后,使用 Valgrind 運(yùn)行:valgrind./test ,Valgrind 會(huì)輸出詳細(xì)的錯(cuò)誤信息,指出內(nèi)存泄漏的位置和大小,類似如下內(nèi)容:

==12345== Memcheck, a memory error detector
==12345== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==12345== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==12345== Command:./test
==12345== 
==12345== HEAP SUMMARY:
==12345==     in use at exit: 40 bytes in 1 blocks
==12345==   total heap usage: 1 allocs, 0 frees, 40 bytes allocated
==12345== 
==12345== 40 bytes in 1 blocks are definitely lost in loss record 1 of 1
==12345==    at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==12345==    by 0x10918F: main (test.c:6)
==12345== 
==12345== LEAK SUMMARY:
==12345==    definitely lost: 40 bytes in 1 blocks
==12345==    indirectly lost: 0 bytes in 0 blocks
==12345==      possibly lost: 0 bytes in 0 blocks
==12345==    still reachable: 0 bytes in 0 blocks
==12345==         suppressed: 0 bytes in 0 blocks
==12345== 
==12345== For lists of detected and suppressed errors, rerun with: -s
==12345== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

通過這些信息,我們可以清楚地知道在test.c的第 6 行分配的 40 字節(jié)內(nèi)存沒有被釋放,從而能夠及時(shí)修復(fù)代碼中的內(nèi)存泄漏問題 。除了檢測內(nèi)存泄漏,Valgrind 還能檢測其他內(nèi)存錯(cuò)誤,如數(shù)組越界訪問:

#include <stdio.h>

int main() {
    int arr[5];
    arr[10] = 100; // 越界訪問
    return 0;
}

使用 Valgrind 運(yùn)行該程序,它會(huì)輸出類似如下的錯(cuò)誤提示:

==12345== Memcheck, a memory error detector
==12345== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==12345== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==12345== Command:./test
==12345== 
==12345== Invalid write of size 4
==12345==    at 0x10919D: main (test.c:5)
==12345==  Address 0x4c38050 is 20 bytes after a block of size 20 alloc'd
==12345==    at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==12345==    by 0x10918F: main (test.c:4)
==12345== 
==12345== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

提示在test.c的第5行發(fā)生了無效的寫操作,因?yàn)樵L問的數(shù)組下標(biāo)超出了數(shù)組的范圍 。Valgrind的這些功能對(duì)于提高程序穩(wěn)定性和可靠性非常重要,特別是在開發(fā)大型項(xiàng)目時(shí),能夠幫助開發(fā)者及時(shí)發(fā)現(xiàn)和解決內(nèi)存相關(guān)的問題 。

Part9.實(shí)際問題與案例分析

9.1內(nèi)存泄漏問題

內(nèi)存泄漏是指程序在申請(qǐng)內(nèi)存后,無法釋放已申請(qǐng)的內(nèi)存空間 ,導(dǎo)致這些內(nèi)存被持續(xù)占用,無法被其他程序或進(jìn)程使用 。從進(jìn)程的角度來看,當(dāng)一個(gè)進(jìn)程不斷地分配內(nèi)存,但沒有及時(shí)釋放不再使用的內(nèi)存,隨著時(shí)間的推移,進(jìn)程占用的內(nèi)存會(huì)越來越多,而系統(tǒng)中可用于分配的內(nèi)存則會(huì)逐漸減少 。例如,在一個(gè)長時(shí)間運(yùn)行的服務(wù)器程序中,如果存在內(nèi)存泄漏問題,可能會(huì)導(dǎo)致服務(wù)器的內(nèi)存被逐漸耗盡,最終影響整個(gè)系統(tǒng)的穩(wěn)定性和性能 。

在 Linux 系統(tǒng)中,可以使用 Valgrind 工具來檢測內(nèi)存泄漏 。假設(shè)我們有一個(gè)簡單的 C 程序leak.c:

#include <stdio.h>
#include <stdlib.h>

int main() {
    int *p = (int *)malloc(10 * sizeof(int));
    // 這里沒有釋放p指向的內(nèi)存
    return 0;
}

編譯該程序后,使用 Valgrind 運(yùn)行:valgrind --leak-check=full./leak ,Valgrind 會(huì)輸出詳細(xì)的內(nèi)存泄漏信息,如下:

==1234== Memcheck, a memory error detector
==1234== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==1234== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==1234== Command:./leak
==1234== 
==1234== HEAP SUMMARY:
==1234==     in use at exit: 40 bytes in 1 blocks
==1234==   total heap usage: 1 allocs, 0 frees, 40 bytes allocated
==1234== 
==1234== 40 bytes in 1 blocks are definitely lost in loss record 1 of 1
==1234==    at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==1234==    by 0x10918F: main (leak.c:6)
==1234== 
==1234== LEAK SUMMARY:
==1234==    definitely lost: 40 bytes in 1 blocks
==1234==    indirectly lost: 0 bytes in 0 blocks
==1234==      possibly lost: 0 bytes in 0 blocks
==1234==    still reachable: 0 bytes in 0 blocks
==1234==         suppressed: 0 bytes in 0 blocks
==1234== 
==1234== For lists of detected and suppressed errors, rerun with: -s
==1234== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

從輸出中可以看出,在leak.c的第 6 行分配了 40 字節(jié)的內(nèi)存,但沒有釋放,導(dǎo)致內(nèi)存泄漏 。

一旦檢測到內(nèi)存泄漏,就需要及時(shí)處理 。對(duì)于簡單的內(nèi)存泄漏問題,如上述例子,只需要在合適的位置添加內(nèi)存釋放的代碼即可 。在上述程序中,我們可以在return 0;之前添加free(p);來釋放分配的內(nèi)存 。但在實(shí)際的大型項(xiàng)目中,內(nèi)存泄漏的排查和處理可能會(huì)比較復(fù)雜,需要仔細(xì)分析代碼邏輯,找出內(nèi)存分配和釋放的不合理之處 。有時(shí)候,內(nèi)存泄漏可能是由于復(fù)雜的數(shù)據(jù)結(jié)構(gòu)或函數(shù)調(diào)用關(guān)系導(dǎo)致的,這就需要借助調(diào)試工具和技術(shù),逐步跟蹤內(nèi)存的分配和使用情況,以定位和解決內(nèi)存泄漏問題 。

9.2 OOM(Out Of Memory)問題

OOM 即內(nèi)存溢出,是指程序在申請(qǐng)內(nèi)存時(shí),沒有足夠的內(nèi)存空間供其使用 。在 Linux 系統(tǒng)中,當(dāng)系統(tǒng)內(nèi)存嚴(yán)重不足時(shí),內(nèi)核有兩種主要的應(yīng)對(duì)策略:一是直接觸發(fā)系統(tǒng)崩潰(panic) ,這是一種極端情況,通常只有在系統(tǒng)內(nèi)存極度匱乏且無法通過其他方式解決時(shí)才會(huì)發(fā)生;二是啟動(dòng) OOM Killer(內(nèi)存不足殺手)機(jī)制 ,內(nèi)核會(huì)根據(jù)一定的算法選擇并殺掉一些占用內(nèi)存較大的進(jìn)程,以釋放內(nèi)存,保證系統(tǒng)的基本運(yùn)行 。

OOM Killer 在選擇要?dú)⒌舻倪M(jìn)程時(shí),會(huì)為每個(gè)進(jìn)程計(jì)算一個(gè) oom_score 值 。oom_score 的計(jì)算涉及多個(gè)因素,包括進(jìn)程占用的物理內(nèi)存頁數(shù)、交換區(qū)頁數(shù)以及頁表(Page Table)數(shù)量等 。例如,一個(gè)進(jìn)程占用了大量的物理內(nèi)存,且頻繁地進(jìn)行內(nèi)存交換操作,它的 oom_score 值就會(huì)相對(duì)較高,也就更容易被 OOM Killer 選中殺掉 。此外,每個(gè)進(jìn)程還有一個(gè) oom_score_adj 參數(shù),用戶可以通過調(diào)整這個(gè)參數(shù)來改變進(jìn)程被 OOM Killer 殺掉的優(yōu)先級(jí) 。當(dāng) oom_score_adj 的值為 - 1000 時(shí),表示該進(jìn)程不會(huì)被 OOM Killer 殺掉 ;而值越大,進(jìn)程被 OOM Killer 殺掉的可能性就越高 。

為了避免 OOM 問題的發(fā)生,可以采取以下措施:

  • 優(yōu)化程序內(nèi)存使用:仔細(xì)檢查程序代碼,避免內(nèi)存泄漏問題,及時(shí)釋放不再使用的內(nèi)存 。合理設(shè)計(jì)數(shù)據(jù)結(jié)構(gòu)和算法,減少不必要的內(nèi)存占用 。例如,在處理大數(shù)據(jù)集時(shí),可以采用分塊處理的方式,避免一次性加載過多數(shù)據(jù)到內(nèi)存中 。
  • 監(jiān)控內(nèi)存使用情況:使用如 top、htop、free 等命令實(shí)時(shí)監(jiān)控系統(tǒng)和進(jìn)程的內(nèi)存使用情況 。通過監(jiān)控?cái)?shù)據(jù),可以及時(shí)發(fā)現(xiàn)內(nèi)存使用異常的進(jìn)程,提前采取措施,如調(diào)整進(jìn)程的內(nèi)存分配或優(yōu)化其算法 。
  • 合理設(shè)置系統(tǒng)參數(shù):根據(jù)系統(tǒng)的實(shí)際需求,合理調(diào)整 Swappiness 參數(shù),平衡物理內(nèi)存和交換空間的使用 。同時(shí),也可以根據(jù)需要調(diào)整其他與內(nèi)存管理相關(guān)的系統(tǒng)參數(shù),如/proc/sys/vm/overcommit_memory 。該參數(shù)用于控制內(nèi)存分配的策略,取值為 0 時(shí),內(nèi)核會(huì)盡量檢查是否有足夠的內(nèi)存可供分配,只有在確定有足夠內(nèi)存時(shí)才會(huì)分配;取值為 1 時(shí),內(nèi)核允許分配超過實(shí)際物理內(nèi)存大小的內(nèi)存,這可能會(huì)導(dǎo)致內(nèi)存不足的風(fēng)險(xiǎn)增加,但在某些情況下可以提高系統(tǒng)的性能;取值為 2 時(shí),內(nèi)核會(huì)嚴(yán)格按照系統(tǒng)的物理內(nèi)存和交換空間大小來分配內(nèi)存,不允許超過這個(gè)范圍 。在實(shí)際應(yīng)用中,需要根據(jù)系統(tǒng)的負(fù)載和內(nèi)存需求,謹(jǐn)慎選擇合適的overcommit_memory值 。
責(zé)任編輯:武曉燕 來源: 深度Linux
相關(guān)推薦

2009-06-17 15:39:00

本機(jī)內(nèi)存硬件限制虛擬內(nèi)存

2010-06-10 17:12:23

Linux 內(nèi)存監(jiān)控

2019-03-14 09:29:02

Linux系統(tǒng)內(nèi)存

2025-04-30 04:20:00

操作系統(tǒng)虛擬內(nèi)存

2020-04-14 16:03:31

Linux虛擬內(nèi)存操作系統(tǒng)

2019-12-26 08:45:46

Linux虛擬內(nèi)存

2020-11-06 07:11:40

內(nèi)存虛擬Redis

2009-08-17 08:32:56

Linux操作系統(tǒng)內(nèi)存管理Linux

2019-03-20 14:29:46

Linux虛擬內(nèi)存

2021-06-01 08:20:55

Linux虛擬內(nèi)存命令

2009-12-09 17:25:19

Linux操作系統(tǒng)

2009-12-16 09:40:26

Linux操作系統(tǒng)

2010-06-02 11:33:26

Linux 內(nèi)存監(jiān)控

2022-08-21 16:52:27

Linux虛擬內(nèi)存

2014-01-14 10:52:06

Linux vmsta虛擬內(nèi)存

2022-08-02 09:02:17

虛擬內(nèi)存操作系統(tǒng)

2009-10-28 15:45:22

linux虛擬化技術(shù)

2020-12-29 16:39:01

Linux代碼命令

2021-05-17 07:45:06

Linux系統(tǒng)程序

2010-04-20 14:17:21

Unix操作系統(tǒng)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

欧美日韩亚洲第一| 久久99精品久久久久久久青青日本| 精品一区二区6| 精品999日本久久久影院| 亚洲欧美日韩国产手机在线| 99电影在线观看| 国产精品久久久久久人| 国产精品伦理久久久久久| 精品久久人人做人人爰| 无遮挡又爽又刺激的视频| 免费黄网站在线播放| 国产精品一区一区三区| 国产精品69av| 亚洲精品在线观看av| 欧美女王vk| 亚洲第一精品自拍| 日韩成人精品视频在线观看| 五月天av在线| 亚洲精品伦理在线| 亚洲7777| 日韩精品福利| 国产91精品一区二区麻豆网站| 国产精品成人久久久久| 国产在线视频99| 亚洲mv大片欧洲mv大片| 亚洲美女喷白浆| 野战少妇38p| 成人在线啊v| 欧美影片第一页| 青青草原成人网| 第一av在线| 亚洲欧洲无码一区二区三区| 久久综合色一本| 免费观看黄色av| 国产一区二区三区免费| 国产精品一二区| 91视频在线视频| 国产精品试看| 欧美激情在线播放| 91高清免费观看| 成人精品影院| 国产一区二区欧美日韩| 91久久免费视频| 日本国产精品| 日韩精品在线播放| 一级国产黄色片| 另类尿喷潮videofree| 日韩欧美国产1| 欧美一区二区三区影院| 免费观看在线一区二区三区| 欧美日韩中文另类| 亚洲 欧美 日韩系列| 成人软件在线观看| 91精品1区2区| 精品久久久久久中文字幕2017| 欧美a级在线观看| 精品国产91久久久久久| 久久综合九色综合88i| 蜜桃视频动漫在线播放| 姬川优奈aav一区二区| 国产特级黄色大片| 樱桃视频成人在线观看| 色偷偷成人一区二区三区91 | 国产精品久久久久国产a级| 黄色片免费观看视频| 国产欧美日本| 日韩美女主播视频| 在线视频1卡二卡三卡| 久久国产欧美日韩精品| 成人久久一区二区三区| 精品区在线观看| 成人黄色在线视频| 麻豆蜜桃91| 成人精品一区二区三区校园激情| 国产精品全国免费观看高清| 亚洲精品视频一二三| 成人午夜在线影视| 亚洲福利视频导航| 久久久久久久久久久免费视频| 日日av拍夜夜添久久免费| 欧美三区在线视频| 中文字幕剧情在线观看| 国产亚洲成av人片在线观黄桃| 日韩精品小视频| 少妇愉情理伦三级| 国产主播一区| 69视频在线播放| 在线免费观看高清视频| 国产精品911| 蜜桃91精品入口| 色多多视频在线观看| 一区二区成人在线观看| 免费观看精品视频| 在线播放成人| 日韩激情av在线免费观看| 性猛交ⅹxxx富婆video| 亚洲精品小说| 欧美在线视频播放| 国产欧美久久久| 26uuu色噜噜精品一区| 亚洲图片欧洲图片日韩av| 欧美日韩色网| 欧美性大战久久| 中文字幕人妻一区二区三区| 成人激情电影在线| 91精品国产免费久久久久久 | 精品一区二区三区不卡 | 偷拍精品一区二区三区| 国产精品免费久久久久| 天天夜碰日日摸日日澡性色av| 玖玖精品在线| 日韩精品极品在线观看播放免费视频 | www.欧美黄色| 久久久免费人体| 国产午夜精品久久久| 国产一二三四区| 欧美96一区二区免费视频| 国产视频精品网| 国产在线二区| 在线观看免费视频综合| 亚洲激情 欧美| 综合在线一区| 国产日韩精品入口| 福利在线视频导航| 精品久久久香蕉免费精品视频| 国产不卡的av| 97欧美在线视频| 国产精品成人播放| 黄色软件在线| 一本色道久久综合精品竹菊| 久久久久久久人妻无码中文字幕爆| 五月婷婷亚洲| 国产日韩精品在线观看| www日韩tube| 色女孩综合影院| 国产美女免费无遮挡| 99热在线精品观看| 国产精品免费在线| 麻豆福利在线观看| 精品成a人在线观看| 极品盗摄国产盗摄合集| 国产自产v一区二区三区c| 视频一区亚洲| 国产第一精品| www.亚洲男人天堂| 一区二区三区播放| 国产精品久久久久久久久搜平片| 国产一级做a爰片久久| 欧美特黄一级大片| 国产日韩欧美在线播放| 日本免费中文字幕在线| 欧美日本在线观看| 操她视频在线观看| 麻豆精品蜜桃视频网站| 亚洲一区二区三区精品在线观看 | 国产精品主播| 久久狠狠久久综合桃花| 亚洲国产福利| 正在播放亚洲1区| 亚洲天堂手机版| 中文字幕亚洲视频| 亚洲欧美日韩一二三区| 欧美fxxxxxx另类| 国产精品视频免费观看| 色一区二区三区| 国产香蕉精品视频一区二区三区| 中文区中文字幕免费看| 亚洲欧美在线观看| 中文字幕在线观看视频www| 午夜精品免费| 久久亚洲国产精品日日av夜夜| 中文字幕在线免费观看视频| 在线观看国产精品淫| 国产免费黄色大片| 亚洲第一福利视频在线| 国精产品一区一区三区免费视频| 日韩影院精彩在线| 国产精品久久成人免费观看| 超碰一区二区三区| 国产成人精品在线播放| 男人影院在线观看| 亚洲电影在线观看| 最新中文字幕在线观看视频| 亚洲人一二三区| a级一a一级在线观看| 欧美aaaaa成人免费观看视频| 欧美另类videosbestsex日本| 卡通动漫国产精品| 国产免费一区二区三区在线能观看 | 欧美另类在线播放| 人成在线免费视频| 91精品国产91久久久久久一区二区 | 色综合天天爱| 国产欧美日韩视频一区二区三区| 91看片一区| 97精品国产97久久久久久免费 | 欧美伦理91i| 免费看男男www网站入口在线| 在线不卡一区二区| 欧美h在线观看| 亚洲欧美激情一区二区| 在线免费观看成年人视频| 国产乱一区二区| 日本www.色| 99精品福利视频| 国产成人精品免费看在线播放 | 亚洲欧美精品伊人久久| 999免费视频| 91黄色免费网站| 99免费在线观看| 亚洲男人天堂av网| 国产三级短视频| 99国产精品国产精品久久| 国产探花在线观看视频| 蜜桃视频在线观看一区| 国产亚洲精品网站| 在线不卡视频| 人妻无码一区二区三区四区| 久久在线电影| 午夜欧美性电影| 日韩大片在线免费观看| 成人av播放| 韩国三级大全久久网站| 国产精品一区二区3区| 久久爱91午夜羞羞| 69久久夜色精品国产7777 | 国产精品h在线观看| 国产美女精品写真福利视频| 欧美成人在线网站| 成人影欧美片| 久久夜色精品亚洲噜噜国产mv| 91福利在线视频| 国产亚洲人成a一在线v站| 亚洲欧洲视频在线观看| 亚洲国产精品系列| 亚洲av无码乱码国产精品久久| 777午夜精品免费视频| 在线免费观看一级片| 欧美吻胸吃奶大尺度电影| 无码人妻丰满熟妇区bbbbxxxx| 精品国产福利在线| 亚洲精品国产精品乱码| 天天综合天天做天天综合| 日本一区二区不卡在线| 亚洲国产成人va在线观看天堂| 久久丫精品久久丫| 亚洲高清久久久| 你懂的国产视频| 色婷婷一区二区| 国产黄色免费视频| 欧美视频在线一区| 91成人国产综合久久精品| 欧美区视频在线观看| 国产精品久久免费| 欧美一卡二卡在线| 亚洲欧美黄色片| 精品一区精品二区| 国产精品四虎| 日韩在线视频观看| 国产黄大片在线观看画质优化| 欧美成人精品在线观看| 波多野结衣久久| 日本国产高清不卡| 日韩电影精品| 成人羞羞视频免费| 网红女主播少妇精品视频| 欧美尤物一区| 亚洲精品在线观看91| 久久精品xxx| 欧美一级播放| 不卡的在线视频| 懂色av一区二区夜夜嗨| 麻豆国产精品一区| 中文字幕在线视频一区| 青娱乐国产在线| 欧美日韩另类在线| 亚洲一二区视频| 亚洲第一男人av| av二区在线| 九九热这里只有精品免费看| 日韩精品极品| 国产日韩综合一区二区性色av| av日韩在线播放| 日韩av一区二区三区美女毛片| 国产电影一区二区在线观看| 黄色成人在线免费观看| 久久激情视频| 在线观看欧美一区二区| 久久久久久久综合日本| 免费成人深夜夜行网站| 欧美视频免费在线观看| 一级黄色片在线播放| 亚洲精品电影网在线观看| av大全在线免费看| 午夜精品久久久久久久99热| 成人综合网站| 久久国产精品-国产精品| 外国成人免费视频| 日韩精品一区二区三区色欲av| 狠狠狠色丁香婷婷综合激情| 在线观看国产三级| 亚洲视频在线观看三级| 69亚洲精品久久久蜜桃小说 | 一区二区国产在线| 黄在线观看网站| 成人午夜激情影院| 永久免费看片直接| 欧美一a一片一级一片| 日本美女一级片| 欧美美女18p| 日韩专区视频| 日本高清不卡一区二区三| 亚洲午夜精品久久久久久app| 日本在线一二三区| 久久精品日产第一区二区三区高清版| 欧美久久久久久久久久久久| 欧美精品在线一区二区三区| 欧美少妇另类| 4438全国亚洲精品在线观看视频| 日韩免费精品| 亚洲欧洲三级| 日韩高清中文字幕一区| 亚洲精品中文字幕在线播放| 一区二区三区在线视频播放| 国产又大又长又粗| 色综合影院在线| av有声小说一区二区三区| 欧美大香线蕉线伊人久久| 在线精品亚洲| 久草视频福利在线| 一区二区三区加勒比av| 国产男女猛烈无遮挡| 久久久精品国产网站| 欧美高清免费| 在线视频亚洲自拍| 久久国产三级精品| 91麻豆精品成人一区二区| 欧美日韩午夜精品| 一级毛片视频在线| 国产综合在线观看视频| 欧美成人精品一区二区三区在线看| 538在线视频观看| 国产精品你懂的在线| 国产精品国产精品国产| 伊人久久免费视频| 深夜视频一区二区| 亚洲国产精品www| 久久99精品久久久久婷婷| 亚洲熟女少妇一区二区| 欧美顶级少妇做爰| 色婷婷视频在线观看| 福利视频久久| 亚洲一级在线| 亚洲人成人无码网www国产 | 欧美中文字幕在线视频| 一区二区三区视频免费观看| 国语对白做受xxxxx在线中国| 亚洲国产成人私人影院tom| 岳乳丰满一区二区三区| 久久亚洲成人精品| 国产精伦一区二区三区| 免费观看日韩毛片| 国产精品欧美久久久久无广告| 国产精品无码在线播放| 久久久久久高潮国产精品视| 卡通动漫国产精品| 国产又粗又长又大的视频| 成人欧美一区二区三区在线播放| 国产哺乳奶水91在线播放| 97精品一区二区三区| 神马电影久久| 中文字幕在线视频一区二区| 偷偷要91色婷婷| chinese偷拍一区二区三区| 成人欧美一区二区三区黑人| 国产精品av久久久久久麻豆网| 国产黑丝一区二区| 欧美视频中文一区二区三区在线观看| 成人在线免费看黄| 美日韩精品免费| 国产一区亚洲一区| 日韩 欧美 精品| 中文字幕日韩专区| 成人av动漫| 男操女免费网站| 亚洲午夜久久久久久久久电影院| 免费一级在线观看| 亚洲自拍小视频| 日韩成人精品在线观看| 欧美日韩在线观看免费| 亚洲最新av在线网站| 日韩影片在线观看| 精品999在线| 香港成人在线视频| 久久亚洲天堂| 欧美日韩国产不卡在线看| 国产精品一二三四五| wwwwww在线观看| 91国产精品视频在线| 亚洲人metart人体|