linux 的swap、swappiness及kswapd原理【轉】

本文討論的 swap基於Linux4.4內核代碼 。Linux內存管理是一套很是複雜的系統,而swap只是其中一個很小的處理邏輯。linux

但願本文能讓讀者瞭解Linux對swap的使用大概是什麼樣子。閱讀完本文,應該能夠幫你解決如下問題:算法

  1. swap究竟是幹嗎的?
  2. swappiness究竟是用來調節什麼的?
  3. kswapd何時會進行swap操做?
  4. 什麼是內存水位標記?
  5. swap分區的優先級(priority)有啥用?

一、什麼是SWAP,究竟是幹嗎的?

咱們通常所說的swap,指的是一個交換分區或文件。在Linux上可使用swapon -s命令查看當前系統上正在使用的交換空間有哪些,以及相關信息:數據庫

[zorro@zorrozou-pc0 linux-4.4]$ swapon -s
Filename            Type        Size        Used    Priority
/dev/dm-4           partition    33554428    0        -1

從功能上講,交換分區主要是在內存不夠用的時候,將部份內存上的數據交換到swap空間上,以便讓系統不會因內存不夠用而致使oom或者更致命的狀況出現。apache

因此,當內存使用存在壓力,開始觸發內存回收的行爲時,就可能會使用swap空間。編程

內核對swap的使用其實是跟內存回收行爲緊密結合的。那麼關於內存回收和swap的關係,咱們須要思考如下幾個問題:緩存

  1. 爲何要進行內存回收?
  2. 哪些內存可能會被回收呢?
  3. 回收的過程當中何時會進行交換呢?
  4. 具體怎麼交換?

下面咱們就從這些問題出發,一個一個進行分析。服務器

爲何要進行內存回收?

內核之因此要進行內存回收,主要緣由有兩個:架構

  1. 內核須要爲任什麼時候刻突發到來的內存申請提供足夠的內存。因此通常狀況下保證有足夠的free空間對於內核來講是必要的。<br>另外,Linux內核使用cache的策略雖然是不用白不用,內核會使用內存中的page cache對部分文件進行緩存,以便提高文件的讀寫效率。<br> 因此內核有必要設計一個週期性回收內存的機制,以便cache的使用和其餘相關內存的使用不至於讓系統的剩餘內存長期處於不多的狀態。app

  2. 當真的有大於空閒內存的申請到來的時候,會觸發強制內存回收。 <br>因此,內核在應對這兩類回收的需求下,分別實現了兩種不一樣的機制:less

因此,內核在應對這兩類回收的需求下,分別實現了兩種不一樣的機制:

  • 一個是使用 kswapd進程對內存進行週期檢查 ,以保證日常狀態下剩餘內存儘量夠用。
  • 另外一個是 直接內存回收(directpagereclaim) ,就是當內存分配時沒有空閒內存能夠知足要求時,觸發直接內存回收。

這兩種內存回收的觸發路徑不一樣:

  • 一個是使用 kswapd進程對內存進行週期檢查 ,以保證日常狀態下剩餘內存儘量夠用。
  • 另外一個是 直接內存回收(directpagereclaim) ,就是當內存分配時沒有空閒內存能夠知足要求時,觸發直接內存回收。

這兩種內存回收的觸發路徑不一樣:

  • 一個是由內核進程kswapd直接調用內存回收的邏輯進行內存回收;<br> 參見mm/vmscan.c中的kswapd()主邏輯

  • 另外一個是內存申請的時候進入slow path的內存申請邏輯進行回收。<br> 參見內核代碼中的mm/page_alloc.c中的__alloc_pages_slowpath方法

這兩個方法中實際進行內存回收的過程異曲同工,最終都是 調用shrink_zone() 方法進行鍼對每一個zone的內存頁縮減。

這個方法中會再調用shrink_lruvec()這個方法對每一個組織頁的鏈表進程檢查。找到這個線索以後,咱們就能夠清晰的看到內存回收操做究竟針對的page有哪些了。

這些鏈表主要定義在mm/vmscan.c一個enum中:

根據這個enum能夠看到,內存回收主要須要進行掃描的鏈表有以下4個:

  • anon的inactive
  • anon的active
  • file的inactive
  • file的active

就是說,內存回收操做主要針對的就是內存中的文件頁(file cache)和匿名頁。

關於活躍(active)仍是不活躍(inactive)的判斷內核會使用lru算法進行處理並進行標記,咱們這裏不詳細解釋這個過程。

整個掃描的過程分幾個循環:

  1. 首先掃描每一個zone上的cgroup組;
  2. 而後再以cgroup的內存爲單元進行page鏈表的掃描;
  3. 內核會先掃描anon的active鏈表,將不頻繁的放進inactive鏈表中,而後掃描inactive鏈表,將裏面活躍的移回active中;
  4. 進行swap的時候,先對inactive的頁進行換出;
  5. 若是是file的文件映射page頁,則判斷其是否爲髒數據,若是是髒數據就寫回,不是髒數據能夠直接釋放。

這樣看來, 內存回收這個行爲會對兩種內存的使用進行回收:

  • 一種是anon的匿名頁內存,主要回收手段是swap;
  • 另外一種是file-backed的文件映射頁,主要的釋放手段是寫回和清空。

由於針對filebased的內存,不必進行交換,其數據本來就在硬盤上,回收這部份內存只要在有髒數據時寫回,並清空內存就能夠了,之後有須要再從對應的文件讀回來。

內存對匿名頁和文件緩存一共用了 四條鏈表 進行組織,回收過程主要是針對這四條鏈表進行掃描和操做。

二、swappiness究竟是用來調節什麼的?

不少人應該都知道 /proc/sys/vm/swappiness 這個文件,是個能夠用來調整跟swap相關的參數。這個文件的默認值是60,能夠的取值範圍是0-100。

這很容易給你們一個暗示:我是個百分比哦!

那麼這個文件具體到底表明什麼意思呢?咱們先來看一下說明:

======

swappiness

This control is used to define how aggressive the kernel will swap memory pages. Higher values will increase agressiveness, lower values decrease the amount of swap.

A value of 0 instructs the kernel not to initiate swap until the amount of free and file-backed pages is less than the high water mark in a zone.

The default value is 60.

======

這個文件的值用來定義內核使用swap的積極程度:

  • 值越高,內核就會越積極的使用swap;
  • 值越低,就會下降對swap的使用積極性。
  • 若是這個值爲0,那麼內存在free和file-backed使用的頁面總量小於高水位標記(high water mark)以前,不會發生交換。

在這裏咱們能夠理解file-backed這個詞的含義了,實際上就是上文所說的文件映射頁的大小。

那麼這個swappiness到底起到了什麼做用呢?

咱們換個思路考慮這個事情。假設讓咱們設計一個內存回收機制,要去考慮將一部份內存寫到swap分區上,將一部分file-backed的內存寫回並清空,剩餘部份內存出來,咱們將怎麼設計?

我想應該主要考慮這樣幾個問題:

  1. 若是回收內存能夠有兩種途徑(匿名頁交換和file緩存清空),那麼我應該考慮在本次回收的時候,什麼狀況下多進行file寫回,什麼狀況下應該多進行swap交換。說白了就是平衡兩種回收手段的使用,以達到最優。
  2. 若是符合交換條件的內存較長,是否是能夠不用所有交換出去?好比能夠交換的內存有100M,可是目前只須要50M內存,實際只要交換50M就能夠了,不用把能交換的都交換出去。

分析代碼會發現,Linux內核對這部分邏輯的實現代碼在 get_scan_count() 這個方法中,這個方法被 shrink_lruvec() 調用。

get_sacn_count()就是處理上述邏輯的,swappiness是它所須要的一個參數,這個參數其實是指導內核在清空內存的時候,是更傾向於清空file-backed內存仍是更傾向於進行匿名頁的交換的。

固然,這只是個傾向性,是指在兩個都夠用的狀況下,更願意用哪一個,若是不夠用了,那麼該交換仍是要交換。

簡單看一下get_sacn_count()函數的處理部分代碼,其中關於swappiness的第一個處理是:

這裏註釋的很清楚:

  1. 若是swappiness設置爲100,那麼匿名頁和文件將用一樣的優先級進行回收。

<br> 很明顯,使用清空文件的方式將有利於減輕內存回收時可能形成的IO壓力。 <br> 由於若是file-backed中的數據不是髒數據的話,那麼能夠不用寫回,這樣就沒有IO發生,而一旦進行交換,就必定會形成IO。 <br> 因此係統默認將swappiness的值設置爲60,這樣回收內存時,對file-backed的文件cache內存的清空比例會更大,內核將會更傾向於進行緩存清空而不是交換。

  1. 這裏的swappiness值若是是60,那麼是否是說內核回收的時候,會按照60:140的比例去作相應的swap和清空file-backed的空間呢?並非。

<br> 在作這個比例計算的時候,內核還要參考當前內存使用的其餘信息。對這裏具體是怎麼處理感興趣的人,能夠本身詳細看get_sacn_count()的實現,本文就很少解釋了。 <br> 咱們在此要明確的概念是: swappiness的值是用來控制內存回收時,回收的匿名頁更多一些仍是回收的file cache更多一些 。

  1. swappiness設置爲0的話,是否是內核就根本不會進行swap了呢?這個答案也是否認的。

<br> 首先是內存真的不夠用的時候,該swap的話仍是要swap。 <br> 其次在內核中還有一個邏輯會致使直接使用swap, 內核代碼 是這樣處理的:

三、kswapd何時會進行swap操做?

咱們回到kswapd週期檢查和直接內存回收的兩種內存回收機制。

直接內存回收比較好理解,當申請的內存大於剩餘內存的時候,就會觸發直接回收。

那麼kswapd進程在週期檢查的時候觸發回收的條件是什麼呢?

仍是從設計角度來看,kswapd進程要週期對內存進行檢測,達到必定閾值的時候開始進行內存回收。

這個所謂的閾值能夠理解爲內存目前的使用壓力,就是說,雖然咱們還有剩餘內存,可是當剩餘內存比較小的時候,就是內存壓力較大的時候,就應該開始試圖回收些內存了,這樣才能保證系統儘量的有足夠的內存給突發的內存申請所使用。

四、什麼是內存水位標記?(watermark)

那麼如何描述內存使用的壓力呢?

Linux內核使用水位標記(watermark)的概念來描述這個壓力狀況。

  • Linux爲內存的使用設置了三種內存水位標記:high、low、min。他們 所標記的含義分別爲:

  • 剩餘內存在high以上表示內存剩餘較多,目前內存使用壓力不大;

  • high-low的範圍表示目前剩餘內存存在必定壓力;

  • low-min表示內存開始有較大使用壓力,剩餘內存很少了;

  • min是最小的水位標記,當剩餘內存達到這個狀態時,就說明內存面臨很大壓力。

  • 小於min這部份內存,內核是保留給特定狀況下使用的,通常不會分配。

內存回收行爲就是基於剩餘內存的水位標記進行決策的:

當系統剩餘內存低於watermark[low]的時候,內核的kswapd開始起做用,進行內存回收。直到剩餘內存達到watermark[high]的時候中止。

若是內存消耗致使剩餘內存達到了或超過了watermark[min]時,就會觸發直接回收(direct reclaim)。

明白了水位標記的概念以後,zonefile + zonefree <= high_wmark_pages(zone)這個公式就能理解了。

這裏的zonefile至關於內存中文件映射的總量,zonefree至關於剩餘內存的總量。

內核通常認爲,若是zonefile還有的話,就能夠儘可能經過清空文件緩存得到部份內存,而沒必要只使用swap方式對anon的內存進行交換。

整個判斷的概念是說,在全局回收的狀態下(有global_reclaim(sc)標記),若是當前的文件映射內存總量+剩餘內存總量的值評估小於等於watermark[high]標記的時候,就能夠進行直接swap了。

這樣是爲了防止進入cache陷阱,具體描述能夠見代碼註釋。

這個判斷對系統的影響是, swappiness設置爲0時,有剩餘內存的狀況下也可能發生交換。

那麼watermark相關值是如何計算的呢?

全部的內存watermark標記都是根據當前內存總大小和一個可調參數進行運算得來的,這個參數是: /proc/sys/vm/min_free_kbytes

  • 首先這個參數自己決定了系統中每一個zone的watermark[min]的值大小。
  • 而後內核根據min的大小並參考每一個zone的內存大小分別算出每一個zone的low水位和high水位值。

想了解具體邏輯能夠參見源代碼目錄下的該文件:

mm/page_alloc.c

在系統中能夠從/proc/zoneinfo文件中查看當前系統的相關的信息和使用狀況。

咱們會發現以上內存管理的相關邏輯都是以zone爲單位的,這裏zone的含義是指內存的分區管理。

Linux將內存分紅多個區,主要有:

  • 直接訪問區(DMA)
  • 通常區(Normal)
  • 高端內存區(HighMemory)

內核對內存不一樣區域的訪問由於硬件結構因素會有尋址和效率上的差異。若是在NUMA架構上,不一樣CPU所管理的內存也是不一樣的zone。

相關參數設置

  • zone_reclaim_mode:

zone_reclaim_mode模式是在2.6版本後期開始加入內核的一種模式,能夠用來管理當一個內存區域(zone)內部的內存耗盡時,是從其內部進行內存回收仍是能夠從其餘zone進行回收的選項,咱們能夠經過 /proc/sys/vm/zone_reclaim_mode 文件對這個參數進行調整。

在申請內存時(內核的get_page_from_freelist()方法中),內核在當前zone內沒有足夠內存可用的狀況下,會根據zone_reclaim_mode的設置來決策是從下一個zone找空閒內存仍是在zone內部進行回收。這個值爲0時表示能夠從下一個zone找可用內存,非0表示在本地回收。

這個文件能夠設置的值及其含義以下:

  1. echo 0 > /proc/sys/vm/zone_reclaim_mode:意味着關閉zone_reclaim模式,能夠從其餘zone或NUMA節點回收內存。
  2. echo 1 > /proc/sys/vm/zone_reclaim_mode:表示打開zone_reclaim模式,這樣內存回收只會發生在本地節點內。
  3. echo 2 > /proc/sys/vm/zone_reclaim_mode:在本地回收內存時,能夠將cache中的髒數據寫回硬盤,以回收內存。
  4. echo 4 > /proc/sys/vm/zone_reclaim_mode:能夠用swap方式回收內存。

不一樣的參數配置會在NUMA環境中對其餘內存節點的內存使用產生不一樣的影響,你們能夠根據本身的狀況進行設置以優化你的應用。

默認狀況下,zone_reclaim模式是關閉的。這在不少應用場景下能夠提升效率,好比文件服務器,或者依賴內存中cache比較多的應用場景。

這樣的場景對內存cache速度的依賴要高於進程進程自己對內存速度的依賴,因此咱們寧肯讓內存從其餘zone申請使用,也不肯意清本地cache。

若是肯定應用場景是內存需求大於緩存,並且儘可能要避免內存訪問跨越NUMA節點形成的性能降低的話,則能夠打開zone_reclaim模式。

此時頁分配器會優先回收容易回收的可回收內存(主要是當前不用的page cache頁),而後再回收其餘內存。

打開本地回收模式的寫回可能會引起其餘內存節點上的大量的髒數據寫回處理。若是一個內存zone已經滿了,那麼髒數據的寫回也會致使進程處理速度收到影響,產生處理瓶頸。

這會下降某個內存節點相關的進程的性能,由於進程再也不可以使用其餘節點上的內存。可是會增長節點之間的隔離性,其餘節點的相關進程運行將不會由於另外一個節點上的內存回收致使性能降低。

除非針對本地節點的內存限制策略或者cpuset配置有變化,對swap的限制會有效約束交換隻發生在本地內存節點所管理的區域上。

  • min_unmapped_ratio:

這個參數只在NUMA架構的內核上生效。這個值表示NUMA上每一個內存區域的pages總數的百分比。

在zone_reclaim_mode模式下,只有當相關區域的內存使用達到這個百分比,纔會發生區域內存回收。

在zone_reclaim_mode設置爲4的時候,內核會比較全部的file-backed和匿名映射頁,包括swapcache佔用的頁以及tmpfs文件的總內存使用是否超過這個百分比。

其餘設置的狀況下,只比較基於通常文件的未映射頁,不考慮其餘相關頁。

  • page-cluster:

page-cluster是用來控制從swap空間換入數據的時候,一次連續讀取的頁數,這至關於對交換空間的預讀。這裏的連續是指在swap空間上的連續,而不是在內存地址上的連續。

由於swap空間通常是在硬盤上,對硬盤設備的連續讀取將減小磁頭的尋址,提升讀取效率。

這個文件中設置的值是2的指數。就是說,若是設置爲0,預讀的swap頁數是2的0次方,等於1頁。若是設置爲3,就是2的3次方,等於8頁。

同時,設置爲0也意味着關閉預讀功能。文件默認值爲3。咱們能夠根據咱們的系統負載狀態來設置預讀的頁數大小。

swap的相關操縱命令

可使用mkswap將一個分區或者文件建立成swap空間。swapon能夠查看當前的swap空間和啓用一個swap分區或者文件。swapoff能夠關閉swap空間。

咱們使用一個文件的例子來演示一下整個操做過程

製做swap文件:

啓用swap文件:

關閉swap空間:

五、swap分區的優先級(priority)有啥用?

在使用多個swap分區或者文件的時候,還有一個優先級的概念(Priority)。

在swapon的時候,咱們可使用-p參數指定相關swap空間的優先級, 值越大優先級越高 ,能夠指定的數字範圍是-1到32767。

內核在使用swap空間的時候老是先使用優先級高的空間,後使用優先級低的。

固然若是把多個swap空間的優先級設置成同樣的,那麼兩個swap空間將會以輪詢方式並行進行使用。

若是兩個swap放在兩個不一樣的硬盤上,相同的優先級能夠起到相似RAID0的效果,增大swap的讀寫效率。

另外,編程時使用mlock()也能夠將指定的內存標記爲不會換出,具體幫助能夠參考man 2 mlock。

最後 關於swap的使用建議,針對不一樣負載狀態的系統是不同的。有時咱們但願swap大一些,能夠在內存不夠用的時候不至於觸發oom-killer致使某些關鍵進程被殺掉,好比數據庫業務。

也有時候咱們但願不要swap,由於當大量進程爆發增加致使內存爆掉以後,會由於swap致使IO跑死,整個系統都卡住,沒法登陸,沒法處理。

這時候咱們就但願不要swap,即便出現oom-killer也形成不了太大影響,可是不能容許服務器由於IO卡死像多米諾骨牌同樣所有死機,並且沒法登錄。跑cpu運算的無狀態的apache就是相似這樣的進程池架構的程序。

因此:

  • swap到底怎麼用?

  • 要仍是不要?

  • 設置大仍是小?

  • 相關參數應該如何配置?

是要根據咱們本身的生產環境的狀況而定的。

閱讀完本文後但願你們能夠明白一些swap的深層次知識。

Q&A:

  1. 一個內存剩餘還比較大的系統中,是否有可能使用swap?

A: 有可能,若是運行中的某個階段出發了這個條件」zonefile+zonefree<=high_wmark_pages(zone) 「,就可能會swap。

  1. swappiness設置爲0就至關於關閉swap麼?

A: 不是的,關閉swap要使用swapoff命令。swappiness只是在內存發生回收操做的時候用來平衡cache回收和swap交換的一個參數,調整爲0意味着,儘可能經過清緩存來回收內存。

  1. A: swappiness設置爲100表明系統會盡可能少用剩餘內存而多使用swap麼?

不是的,這個值設置爲100表示內存發生回收時,從cache回收內存和swap交換的優先級同樣。就是說,若是目前需求100M內存,那麼較大機率會從cache中清除50M內存,再將匿名頁換出50M,把回收到的內存給應用程序使用。可是這還要看cache中是否能有空間,以及swap是否能夠交換50m。內核只是試圖對它們平衡一些而已。

  1. kswapd進程何時開始內存回收?

A: kswapd根據內存水位標記決定是否開始回收內存,若是標記達到low就開始回收,回收到剩餘內存達到high標記爲止。

  1. 如何查看當前系統的內存水位標記?

A: cat /proc/zoneinfo。

相關文章
相關標籤/搜索