Linux 系統之 OOM 解析

Luga Lee twt企业IT社区
【摘要】基於 VM 環境所部署的 Spring Boot 應用服務,運行過程中內存利用往往達到 90% 甚至以上,本文嘗試對此類在實際業務場景中內存表現的活動現象及背後原因進行分析。
【作者】李傑,專注於Java虛擬機器技術、雲端原生技術領域的探索與研究,個人大眾號「架構驛站」。

在實際的業務場景中,有沒有發現這樣一種場景:基於VM 環境上面所部署的Spring Boot 應用服務,往往在運行過程中將內存利用的足夠“猥瑣”,常常達到90% 甚至以上,此時 ,很大一部分夥伴就開始「叫」了。 曰:領導,記憶不夠了,趕緊擴容! ! ! (此刻,有大佬肯定在想:擴你妹,整天搞這些沒用的~)

那個傻子是不是瘋了? 不知道身為所謂的「技術」人員,大家是如何面對的,如何解決? 本文將聚焦於 Linux 記憶體結構、記憶體分析以及 OOM killer 等 3 個面向以及筆者多年的實務經驗總結來進行解析。

記憶體結構

從宏觀角度而言,記憶體管理系統是作業系統最重要的部分之一。 在記憶體管理的系統呼叫方式,事實上,基於 POSIX 並沒有給記憶體管理指定任何的系統呼叫。 然而,Linux 卻有自己的記憶體系統調用,主要係統調用如下:

系統呼叫 描述
s = brk(addr) 改變資料段大小
a = mmap(addr,len,prot,flags,fd,offset) 進行映射
s = unmap(addr,len) 取消映射
1、brk 透過給出超過資料段之外的第一個位元組位址來指定資料段的大小。 如果新的值要比原來的大,那麼資料區就會變得越來越大,反之會越來越小。

2、mmap 和 unmap 系統呼叫會控制映射檔。 mmp 的第一個參數 addr 決定了檔案對映的位址。 它必須是頁面大小的倍數。 如果參數是 0,系統會指派位址並傳回 a。 第二個參數是長度,它告訴了需要映射多少位元組。 它也是頁面大小的倍數。 prot 決定了映射檔的保護位,保護位可以標記為 可讀、可寫、可執行或這些的結合。 第四個參數 flags 能夠控製檔案是私有的還是可讀的以及 addr 是必須的還是只是進行提示。 第五個參數 fd 是要對應的檔案描述子。 只有開啟的檔案是可以被映射的,因此如果想要進行檔案映射,必須開啟檔案;最後一個參數 offset 會指示檔案從何時開始,不一定每次都要從零開始。

針對 Linux 記憶體管理及實現,其實其涉及的面較廣,較為複雜,從電腦早期開始,我們在實際的業務場景中所使用的記憶體往往都要比系統中實際存在的記憶體多。 為此,記憶體分配策略克服了這個限制,而其中最有名的就是引入: 虛擬記憶體(Virtual Memory)。 透過在多個競爭的進程之間共享虛擬內存,虛擬內存得以讓系統有更多的內存,以方便維護系統資源的分配。 先來張總概覽圖,具體如下圖所示:

Linux 內存,通常被認為指的是“物理內存”,然而,只有內核才可以直接訪問物理內存,進程需要訪問內存,Linux 內核則需要為每個進程提供一個獨立的虛擬地址空間,訪問的是 虛擬記憶體。

通常而言,虛擬記憶體空間的內部被劃分為核心空間和使用者空間:

1.進程在用戶態,只能存取用戶空間內存

2.行程進入內核態才能存取核心空間內存

3.每個行程都包含內核空間,但這些內核空間都關聯相同的實體內存

而針對記憶體映射,其主要將虛擬記憶體位址映射到實體記憶體位址,為了完成記憶體映射。 核心每個進程都維護了一張頁表,記錄虛擬位址和實體位址的映射關係,頁表實際儲存在CPU 的記憶體管理單元 MMU,這樣處理器就可以直接透過硬體找出要存取的記憶體。

再來一張內核線形位址空間佈局圖,具體可參考如下「硬核」示意圖:

針對上述結構圖,簡單描述如下:

1.核心直接映射空間 PAGE_OFFSET~VMALLOC_START,kmalloc和__get_free_page()分配的是這裡的頁面。 二者是藉助 Slab分配器,直接分配實體頁再轉換為邏輯位址(實體位址連續)。 適合分配小段記憶體。 此區域 包含了核心鏡像、實體頁框表mem_map 等資源。

2.核心動態映射空間 VMALLOC_START~VMALLOC_END,被 vmalloc 用到,可表示的空間大。

3.核心永久映射空間 PKMAP_BASE ~ FIXADDR_START,kmap

4.核心臨時映射空間 FIXADDR_START~FIXADDR_TOP,kmap_atomic

記憶體分析

針對記憶體分析部分,其實可利用的手段或策略較多,基於不同段位的水平高低之分,通常,我們可以藉助Top、Free 指令以及Vmstat 指令進行追蹤及觀測記憶體的動態活動變化趨勢,以即時了解 目前作業系統的資源水位,具體如下所示:

[administrator@JavaLangOutOfMemory ~ ] %top
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1 root 20 0 128032 7996 5556 S 80.0 80.4 0:01.03 java
2 root 20 0 0 0 0 S 0.0 0.0 0:00.00 kthreadd
基於上述輸出結果,簡單解析如下:

1、VIRI: 虛擬內存,包括了進程的代碼段、數據段、共享內存、已經申請的堆內存和已經換出的內存等,已經申請的內存,即使還未分配物理內存,也算做虛擬內存

2、RSS: 常駐內存,是進程實際使用的實體內存,不包括 Swap 和共享內存

3、SHR: 共享內存,包括與其他進程共同使用的真實共享內存,包括加載的動態鏈接庫以及程序的代碼段

4、%MEM: 進程使用實體記憶體佔系統記憶體的百分比

[administrator@JavaLangOutOfMemory ~ ] %free
total used free shared buff/cache available
Mem: 2031744 98176 1826192 8784 107376 1800144
Swap: 2097148 0 2097148
此命令列輸出內容較為簡單:主要列印已使用、剩餘、可用、共享記憶體以及快取等資訊。 部分參數釋義如下圖所示:

1、Shared: 共享記憶體, 共享記憶體是透過 Tmpfs 實現的,它的大小就是 Tmpfs 使用的記憶體大小。

2、Available: 可用內存,是新進程可以使用的最大內存,包括剩餘內存和還未使用的內存。

3、Buffer/Cache: 快取包括兩部分,一部分是磁碟讀取檔案的頁緩存,用來緩存從磁碟讀取的數據,加速以後再次存取速度,另一部分是Slab 分配的可回收快取;緩衝區是 對原始磁碟的暫存,用來快取將要寫入磁碟的數據,統一最佳化磁碟寫入。

[administrator@JavaLangOutOfMemory ~ ] %vmstat 1 1
procs ———–memory———- —swap– —–io—- -system– ——cpu—–
r b swpd free buff cache si so bi bo in cs us sy id wa st
1 0 0 1815348 2108 111872 0 0 1 0 11 11 0 0 100 0 0
基於上述輸出結果,簡單解析如下:

1、si: 換入,每秒從磁碟讀入虛擬記憶體的大小,若此值長時間持續大於0,表示實體記憶體不夠或記憶體洩漏,需要定位問題。

2、so: 換出,每秒鐘從記憶體寫入磁碟的大小,若此值長時間持續大於0,表示實體記憶體不夠用,就需要排查記憶體問題。

OOM Killer

通常有這樣的一種場景:若一台VM (虛擬機)上部署多個應用服務,此處,暫以Spring Boot 微服務為例,在某種特殊的時刻,例如:業務促銷、壓力測試或 當某一個聯機負載節點或因網路抖動而掛掉時,此台VM 上的服務突然在毫無徵兆的情況下,突然被「掛掉」。

同時,我們開始蒐集相關線索,以便能夠快速定位到問題原因,將「罪魁禍首」逮捕歸案。

那麼,為什麼會出現這種問題呢? 它是如何產生的? OOM,全稱為 “Out Of Memory”,即 記憶體溢位。 OOM Killer 是 Linux 自我保護的方式,防止記憶體不足時出現嚴重問題。

Linux 核心所採用的此種機制會不時監控所運行中佔用記憶體過大的進程,尤其針對在某一種瞬間場景下佔用記憶體較快的進程,為了防止作業系統記憶體耗盡而不得不自動將此 進程Kill 掉。 通常,系統核心偵測到系統記憶體不足時,篩選並終止某個行程的過程可以參考核心原始碼:linux/mm/oom_kill.c,當系統記憶體不足的時候,out_of_memory()被觸發,然後呼叫select_bad_process( ) 選擇一個”bad” 進程殺掉。 如何判斷和選擇一個」bad 進程呢?Linux 作業系統選擇」bad」進程是透過呼叫 oom_badness(),挑選的演算法和想法都很簡單很樸實:最 bad 的那個進程就是那個最佔用記憶體的進程。

OOM Killer 原始碼解析

OOM killer的核心函數是 out_of_memory(), 執行流程如下:

1.呼叫 check_panic_on_oom() 檢查是否允許執行核心恐慌,假如允許,需要重新啟動系統。

2.若定義了 /proc/sys/vm/oom_kill_allocating_task 即允許 Kill 掉目前正在申請分配物理記憶體的程序,那麼殺死目前程序。

3.呼叫 select_bad_process,選擇 badness score 最高的進程。

4、呼叫 oom_kill_process, 殺死選擇的進程。

我們透過分析 Badness Score 的計算函數來理解 OOM Killer 是如何選擇需要被 Kill 掉的進程,具體原始碼可參考如下所示:

unsigned long oom_badness(struct task_struct *p, struct mem_cgroup *memcg,
const nodemask_t *nodemask, unsigned long totalpages)
{
long points;
long adj;

/* 假如该进程不能被kill, 则分数返回0. */
if (oom_unkillable_task(p, memcg, nodemask))
return 0;

p = find_lock_task_mm(p);
if (!p)
return 0;

/* 获取该进程的 oom_score_adj, 这个是用户为进程设置的 badness score
* 调整值,假如这个值为-1000或者进程被标记为不可被kill,或者进程处于
* vfork()过程,badness score返回0. */
adj = (long)p->signal->oom_score_adj;
if (adj == OOM_SCORE_ADJ_MIN ||
test_bit(MMF_OOM_SKIP, &p->mm->flags) ||
in_vfork(p)) {
task_unlock(p);
return 0;
}

/* badness score分数 = 物理内存页数 + 交换区页数 + 页表Page Table数量. */
points = get_mm_rss(p->mm) + get_mm_counter(p->mm, MM_SWAPENTS) +
mm_pgtables_bytes(p->mm) / PAGE_SIZE;
task_unlock(p);

/* 利用以下公式对 badness score 值进行调整. */
adj *= totalpages / 1000;
points += adj;

/* 返回 badness score, 假如等于0, 则返回 1. */
return points > 0 ? points : 1;
}
透過對 Badness Score 計算函數的分析,我們可以發現 OOM Killer 是基於 RSS 即常駐的實體記憶體來選擇進程進行 Kill 操作, 從而釋放相關記憶體以進行系統自我保護。 有關 OOM Killer 相關配置、查看及分析將於後續文章給出,大家到時留意查看。

綜上所述,本篇文章主要透過基於對Linux 記憶體結構、分析及OOM Killer 3個核心維度,從主動及被動場景等2 面向對Linux 作業系統記憶體的剖析,以探討在實際的業務場景中, 記憶體表現的相關活動及經驗認知。

思科宣布完成對Splunk的收購

2024年3月19日,北京 —— 日前,思科宣布完成對網路安全軟體公司Splunk的收購,令思科具備更強的基礎得以服務客戶,為客戶的完整數位化足跡提供卓越的可見性和洞察力。

在數位新時代發展,企業需要全方位連結並保護其營運要素的安全,包括連結推動業務成長的人員、地點、應用、資料和設備,同時保護整個數位足跡免受網路安全威脅、停機時間和其他 關鍵業務風險的影響。

思科將整合網路解決方案、安全解決方案,和視覺化數據,充分發揮網路的全部潛能,為企業提供即時一致的完整數位景觀,幫助團隊主動保護關鍵基礎設施,預防網路中斷,並優化網路體驗。

思科將推動AI變革,保護AI變革中的網路安全
人工智慧(AI)技術的應用及其產生的影響,超越了以往我們所見的任何一項技術。

大規模有效利用適當的數據,是令AI發揮優勢並令企業以前所未有的效率達成業務目標的關鍵。 想要真正透過AI獲益,企業需要能夠支援AI算力的基礎設施、可供發展AI技術的資料、能夠保護AI安全的平台,以及可即時管理AI應用的視覺化平台。 思科在以上四個領域中皆有所長且可整合解決方案。

攜手共進:思科與Splunk將為客戶與合作夥伴帶來的優勢
思科和Splunk的結合將令我們的客戶:

更安全
高度整合的安全解決方案,充分利用雲端、網路和端點流量來實現前所未有的可見性,為各種規模的企業提供威脅預防、偵測、調查和回應能力。

更可視
高度全面的全端視覺化解決方案,在多雲混合環境中為使用者提供卓越的數位體驗。

更互聯
在智慧、有韌性和持續優化的網路基礎架構上提供領先的安全網路解決方案。
更卓越的AI
思科的網路產品組合,結合強勁安全保障、全端視覺化和全面的資料平台,使客戶能夠安全地在企業和應用中駕馭AI的力量。

更強的經濟效益

思科與Splunk的平台化方案將協助我們的客戶整合眾多單點產品,進而改善業務成果並降低成本。

思科和 Splunk 的結合,也將擁有豐富經驗的全球開發人員和合作夥伴社群聚在一起,為客戶提供預先包裝的應用程式和解決方案擴展其安全性、視覺化和資料平台功能。 結合後的合作夥伴生態系統可以透過高價值服務以及部署創新的新應用程式和人工智慧驅動的解決方案創造新的利潤。

關於思科
思科(NASDAQ:CSCO)是全球科技領導廠商,致力於安全地連接一切,使一切成為可能。 我們的目標是透過重新定義應用、賦能混合辦公、保護企業安全、基礎架構轉型,幫助客戶實現永續發展目標,為所有人打造一個包容性的未來。 您可以在cisco.com.cn上獲取更多信息,並關注我們的微信公眾號“思科聯天下”。

思科和思科標誌是思科或其附屬機構在美國和其他國家地區的商標或註冊商標。 您可以查看Cisco商標清單www.cisco.com/go/trademarks,其中提及的第三方商標是其各自所有者的財產。 使用「夥伴」一詞並不能直接表示思科與任何其他公司之間有合作關係。

無線網路管理系統與無線路由器的區別

通信弱电交流学习
很多朋友在做網路覆蓋的時候,常常會提到,是用無線ap還是用無線路由器,今天我們一起來了解下二者的差別。

一、無線路由器的應用

無線路由器其實就是無線AP+路由功能,現在很多的無線路由器都擁有AP功能。 如果你家是ADSL或小區寬頻,應該選擇無線路由而不是無線AP來共享網絡,如果你家有路由器了,買個無線AP就行了,對於一般的家庭用戶強烈建議選擇無線路由器。

在辦公環境中,一個無線路由就可以滿足需求了。 透過整合的寬頻存取路由器和無線AP功能,它可以輕鬆實現無線網路的連接。 無線路由器一般包括了網路位址轉換(NAT)協議,支援網路連接共享,這對於辦公室來說非常有用。

二、無線AP的功能

AP的一個重要的功能就是中繼,所謂中繼就是在兩個無線點間把無線訊號放大一次,使得遠端的客戶端可以接受到更強的無線訊號。 例如我在a點放置一個AP,而在c點有一個客戶端,之間有120米的距離,從a點到c點信號已經削弱很多,於是我在中途60米處的b點放一個AP 做為中繼,這樣c點的客戶端的訊號就可以有效的增強,保證了傳輸速度和穩定性。

AP另一個重要的功能是橋接,橋接就是鏈結兩個端點,實現兩個無線AP間的資料傳輸,想要把兩個有線區域網路連接起來,一般就選擇透過AP來橋接,例如我在a點有 一個15台電腦組成的有線區域網,b點有一個25台電腦組成的有線區域網,但是ab兩點的距離很遠,超過了100米,透過有線連接已不可能,那麼怎麼把兩個區域網路連接在 一起呢?

這就需要在a點和b點各設定一個AP,開啟AP橋接功能,這樣ab兩點的區域網路就可以互相傳輸資料了。 要提醒的是,沒有WDS功能的AP,橋接後兩點是沒有無線訊號覆蓋的。

最後一個功能是“主從模式”,在這個模式下工作的AP會被主AP或無線路由看做是一台無線客戶端,例如無線網卡或無線模組。 這樣可以方便網管統一管理子網絡,實現一點對多點的連接,AP的客戶端是多點,無線路由或主AP是一點。

這個功能常被應用在無線區域網路和有線區域網路的連接中,例如a點是一個20台電腦組成的有線區域網,b點是一個15台電腦組成的無線區域網,b點已經是有一台無線路由了, 如果a想接入b,在a點加一個AP,並開啟主從模式,並把AP接入a點的交換機,這樣所有a點的電腦就可以連接b點的了。

還有一點要說明的是:因為人們室內的用網環境有一部分是屬於高密環境的,無線AP的位置如果太多相近會很大程度上影響網路的使用,這個原理和微波爐會影響無線網路是 一個道理。 所以現在的企業級無線AP都會搭載抗干擾功能。

華為、華三、銳捷這些廠商的企業AP都有類似的抗干擾功能。

三、為何無線AP比無線路由器貴

企業級AP貴的最主要的原因如下:

1、集中管理:

家用路由或AP只能一個一個單獨的進行配置,30個AP配置維護起來工作量不小,如果網路大到三百個AP甚至三千個AP時,網路管理員可能就瘋掉了,所以集中 管理是企業網路中必須的。 集中管理也可以對整個網路進行統計、分析、監控,這也是家用產品無法實現的。

2、進階功能:

企業級AP在AC控制下,可以實現負責的功能如無縫漫遊、射頻優化,這些技術是比較複雜的,家庭環境可能用不上,但是對於企業這種客戶多、要求高的場景,是非常 有意義的,也是很值錢的技術,這些技術使得產品溢價較高。

3、售前售後技術支援:

無線網路環境是很複雜的,無線部署更是一個複雜課題,企業級產品一般同時提供售前網路規劃、安裝、調試、優化、以及專業的售後支持,這種服務也是很值錢的,會體現在 產品的價格之中。

4、其它:安裝方式、PoE供電等。

所以對於飯店佈網來說:

最好採用企業級產品這種AC+AP這種架構。

只要把所以AP的SSID設定成一樣,使用者就只能搜出來。