1. 程式人生 > >開源技術分享:Linux 內核參數 swappiness細解

開源技術分享:Linux 內核參數 swappiness細解

系統 recent 文章 anything without cgroup [1] reat 基本

本篇文章主要是就swappiness的一個源碼上的解析(基於kernel版本 v4.14-13151-g5a787756b809),僅為個人見解,有不足歡迎相互交流。

關於Swap和swappiness

Swap(交換分區)是操作系統就內存不足的一個緩解。當內存緊張時候,會適當的根據一些配置值和當前的統計值進行一次判斷,會把一些anon內存(分配出去的內存)交換到Swap分區中。

Swappiness是系統的一個參數,可以調節swap的使用優先級。Linux文檔描述如下:

swappiness

This control is used to define how aggressive the kernel will swap

memory pages. Higher values will increase aggressiveness, 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.

翻譯過來就是
這個參數是定義內核交換內存頁的×××性(aggressive)。更大的值將增加×××性,較低的值會減少swap的數量。0值會命令內核不要使用swap,只有當free和文件使用的內存頁數量少於一個zone的高水位,才會使用swap。

默認值是60。

關於這裏的aggressive,看的是雲裏霧裏。只知道這個值大概意義。在一些環境,用戶一直抱怨為什麽Swap使用量這麽多,明明還有挺多的available內存。

Linux內存申請

Linux 內存申請一般來說會打上一些flag標誌,會對申請流程產生一些影響,這裏不細講。主要是講一般情況下(用戶態的申請和大部分內核態的社區都是可以等待內存釋放的)的內存申請。

__alloc_pages 一般第一次遍歷每一個內存區域(zone)尋找第一個可用的足夠的內存塊。如果一個區域滿了,那麽會尋找下一個區域。單數如果 CPUSETS被設置了,他就會觸發內存reclaim回收。

這裏Swappiness主要是在內存reclaim時候生效。

Reclaim的方式

基本上Reclaim的方式為一個是將file相關的內存進行回收,一個是將anon部分內存(即被分配出去的內存)交換到Swap分區。
Linux的內存使用的一個宗旨是盡可能使用內存。在文件被讀寫的時候,文件的cache會一直保留在系統內存中,一直到內存不夠時候,沒有主動釋放這部分內存的邏輯。這樣在下次讀取被緩存的文件時候可以直接從內存讀取,不必從磁盤進行IO操作,這樣文件讀取速度會更加快速。

造成的結果是其實available的內存還很多的情況下,仍然會有內存不夠,觸發Reclaim邏輯,將一部分內存交換到Swap分區。

Swappiness生效方式

Swappiness是在get_scan_count函數使用的。
如下代碼顯示:Swap滿時候,這個參數無影響。

2195 / If we have no swap space, do not bother scanning anon pages. /
2196 if (!sc->may_swap || mem_cgroup_get_nr_swap_pages(memcg) <= 0) {
2197 scan_balance = SCAN_FILE;
2198 goto out;
2199 }

在Cgroup的mem還沒達到limit時候,並且Swappiness為0,也僅僅掃描file cache部分。即不會考慮交換出去。

2201 /
2202
Global reclaim will swap to prevent OOM even with no
2203 swappiness, but memcg users want to use this knob to
2204
disable swapping for individual groups completely when
2205 using the memory controller‘s swap limit feature would be
2206
too expensive.
2207 */
2208 if (!global_reclaim(sc) && !swappiness) {
2209 scan_balance = SCAN_FILE;
2210 goto out;
2211 }

當系統接近OOM時候,並且swapiness非0,那麽會平等的掃描anon和file的內存。

2213 /
2214
Do not apply any pressure balancing cleverness when the
2215 system is close to OOM, scan both anon and file equally
2216
(unless the swappiness setting disagrees with swapping).
2217 */
2218 if (!sc->priority && swappiness) {
2219 scan_balance = SCAN_EQUAL;
2220 goto out;
2221 }
當內存達到limit時候,會只釋放申請的內存。這裏結合前面提到的分支,可以知道,當Swappiness為0時候,沒有達到limit只釋放file cache,當達到limit時候,才考慮切換內存到swap中。

    /*
     * Prevent the reclaimer from falling into the cache trap: as
     * cache pages start out inactive, every cache fault will tip
     * the scan balance towards the file LRU.  And as the file LRU
     * shrinks, so does the window for rotation from references.
     * This means we have a runaway feedback loop where a tiny
     * thrashing file LRU becomes infinitely more attractive than
     * anon pages.  Try to detect this based on file LRU size.
     */
    if (global_reclaim(sc)) {
            unsigned long pgdatfile;
            unsigned long pgdatfree;
            int z;
            unsigned long total_high_wmark = 0;

            pgdatfree = sum_zone_node_page_state(pgdat->node_id, NR_FREE_PAGES);
            pgdatfile = node_page_state(pgdat, NR_ACTIVE_FILE) +
                       node_page_state(pgdat, NR_INACTIVE_FILE);

            for (z = 0; z < MAX_NR_ZONES; z++) {
                    struct zone *zone = &pgdat->node_zones[z];
                    if (!managed_zone(zone))
                            continue;

                    total_high_wmark += high_wmark_pages(zone);
            }

            if (unlikely(pgdatfile + pgdatfree <= total_high_wmark)) {
                    /*
                     * Force SCAN_ANON if there are enough inactive
                     * anonymous pages on the LRU in eligible zones.
                     * Otherwise, the small LRU gets thrashed.
                     */
                    if (!inactive_list_is_low(lruvec, false, memcg, sc, false) &&
                        lruvec_lru_size(lruvec, LRU_INACTIVE_ANON, sc->reclaim_idx)
                                    >> sc->priority) {
                            scan_balance = SCAN_ANON;
                            goto out;
                    }
            }
    }

當inactive的cache頁足夠的時候,只釋放file cache。

    /*
     * If there is enough inactive page cache, i.e. if the size of the
     * inactive list is greater than that of the active list *and* the
     * inactive list actually has some pages to scan on this priority, we
     * do not reclaim anything from the anonymous working set right now.
     * Without the second condition we could end up never scanning an
     * lruvec even if it has plenty of old anonymous pages unless the
     * system is under heavy pressure.
     */
    if (!inactive_list_is_low(lruvec, true, memcg, sc, false) &&
        lruvec_lru_size(lruvec, LRU_INACTIVE_FILE, sc->reclaim_idx) >> sc->priority) {
            scan_balance = SCAN_FILE;
            goto out;
    }

這裏強調一下,swappiness的一般作用這裏開始涉及。是把anon_prio設成相應的swappiness,file_prio 設成200-anon_prio。

    scan_balance = SCAN_FRACT;

    /*
     * With swappiness at 100, anonymous and file have the same priority.
     * This scanning priority is essentially the inverse of IO cost.
     */
    anon_prio = swappiness;
    file_prio = 200 - anon_prio;

這裏進一步使用anon_prio和file_prio來獲取ap和fp

    /*
     * The amount of pressure on anon vs file pages is inversely
     * proportional to the fraction of recently scanned pages on
     * each list that were recently referenced and in active use.
     */
    ap = anon_prio * (reclaim_stat->recent_scanned[0] + 1);
    ap /= reclaim_stat->recent_rotated[0] + 1;

    fp = file_prio * (reclaim_stat->recent_scanned[1] + 1);
    fp /= reclaim_stat->recent_rotated[1] + 1;

具體其他的細節或者後續的算法,留待後續分析。

總結

Swappiness的控制方式主要是在內存緊張時候才會觸發(這裏是指free的內存低)。具體如下:

  1. 當swappiness為0,那麽在available內存充足情況,只釋放file cache,當available內存不足情況下,那麽會將一些內存交換到swap空間。
  2. Swappiness不為0,那麽他的值大小主要是控制每次內存緊張時候,切換到swap和文件緩存釋放的比例。

註意:大部分人誤以為是控制內存剩余比例到swappiness值時,去切換內存到swap,這個是錯誤的。

開源技術分享:Linux 內核參數 swappiness細解