10

ここでの目標は、Linux でメモリが不足している間、実行中のすべてのプロセスの実行可能コードをメモリに保持することです。Linux では、Qubes OS R4.0 Fedora 28 AppVM 内の 24000MB の最大 RAM を使用して、
即座に (1 秒) 高いメモリ負荷を引き起こし、OOM キラーをトリガーすることができます stress --vm-bytes $(awk '/MemAvailable/{printf "%d\n", $2 + 4000;}' < /proc/meminfo)k --vm-keep -m 4 --timeout 10s(コードはhereから)。EDIT4:おそらく関連していますが、言及するのを忘れていたのは、スワップが有効になっていない(つまりCONFIG_SWAP、設定されていない) という事実です

dmesg レポート:

[  867.746593] Mem-Info:
[  867.746607] active_anon:1390927 inactive_anon:4670 isolated_anon:0
                active_file:94 inactive_file:72 isolated_file:0
                unevictable:13868 dirty:0 writeback:0 unstable:0
                slab_reclaimable:5906 slab_unreclaimable:12919
                mapped:1335 shmem:4805 pagetables:5126 bounce:0
                free:40680 free_pcp:978 free_cma:0

興味深いのはactive_file:94 inactive_file:72、それらがキロバイト単位であり、非常に小さいことです。

ここでの問題は、メモリ不足の期間中に実行可能コードがディスクから再読み取りされ、ディスクのスラッシングが発生して OS がフリーズすることです。(ただし、上記の場合は 1 秒未満しか発生しません)

kernel に興味深いコードがあります:mm/vmscan.c

        if (page_referenced(page, 0, sc->target_mem_cgroup,
                            &vm_flags)) {
                nr_rotated += hpage_nr_pages(page);
                /*
                 * Identify referenced, file-backed active pages and
                 * give them one more trip around the active list. So
                 * that executable code get better chances to stay in
                 * memory under moderate memory pressure.  Anon pages
                 * are not likely to be evicted by use-once streaming
                 * IO, plus JVM can create lots of anon VM_EXEC pages,
                 * so we ignore them here.
                 */
                if ((vm_flags & VM_EXEC) && page_is_file_cache(page)) {
                        list_add(&page->lru, &l_active);
                        continue;
                }
        }

誰かがこれを に変更する方法を指摘できればgive them one more trip around the active listgive them infinite trips around the active list仕事は完了するはずです。それとも何か他の方法があるのでしょうか?

カスタム カーネルにパッチを適用してテストできます。アクティブな実行可能コードを常にメモリに保持するためにコードの何を変更すればよいかについてのノウハウがありません(実際には、ディスクのスラッシングを回避できると思います)。

編集:これまでに作業したことは次のとおりです (カーネル 4.18.5 の上に適用されます):

diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index 32699b2..7636498 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -208,7 +208,7 @@ enum lru_list {

 #define for_each_lru(lru) for (lru = 0; lru < NR_LRU_LISTS; lru++)

-#define for_each_evictable_lru(lru) for (lru = 0; lru <= LRU_ACTIVE_FILE; lru++)
+#define for_each_evictable_lru(lru) for (lru = 0; lru <= LRU_INACTIVE_FILE; lru++)

 static inline int is_file_lru(enum lru_list lru)
 {
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 03822f8..1f3ffb5 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -2234,7 +2234,7 @@ static void get_scan_count(struct lruvec *lruvec, struct mem_cgroup *memcg,

    anon  = lruvec_lru_size(lruvec, LRU_ACTIVE_ANON, MAX_NR_ZONES) +
        lruvec_lru_size(lruvec, LRU_INACTIVE_ANON, MAX_NR_ZONES);
-   file  = lruvec_lru_size(lruvec, LRU_ACTIVE_FILE, MAX_NR_ZONES) +
+   file  = //lruvec_lru_size(lruvec, LRU_ACTIVE_FILE, MAX_NR_ZONES) +
        lruvec_lru_size(lruvec, LRU_INACTIVE_FILE, MAX_NR_ZONES);

    spin_lock_irq(&pgdat->lru_lock);
@@ -2345,7 +2345,7 @@ static void shrink_node_memcg(struct pglist_data *pgdat, struct mem_cgroup *memc
             sc->priority == DEF_PRIORITY);

    blk_start_plug(&plug);
-   while (nr[LRU_INACTIVE_ANON] || nr[LRU_ACTIVE_FILE] ||
+   while (nr[LRU_INACTIVE_ANON] || //nr[LRU_ACTIVE_FILE] ||
                    nr[LRU_INACTIVE_FILE]) {
        unsigned long nr_anon, nr_file, percentage;
        unsigned long nr_scanned;
@@ -2372,7 +2372,8 @@ static void shrink_node_memcg(struct pglist_data *pgdat, struct mem_cgroup *memc
         * stop reclaiming one LRU and reduce the amount scanning
         * proportional to the original scan target.
         */
-       nr_file = nr[LRU_INACTIVE_FILE] + nr[LRU_ACTIVE_FILE];
+       nr_file = nr[LRU_INACTIVE_FILE] //+ nr[LRU_ACTIVE_FILE]
+           ;
        nr_anon = nr[LRU_INACTIVE_ANON] + nr[LRU_ACTIVE_ANON];

        /*
@@ -2391,7 +2392,8 @@ static void shrink_node_memcg(struct pglist_data *pgdat, struct mem_cgroup *memc
            percentage = nr_anon * 100 / scan_target;
        } else {
            unsigned long scan_target = targets[LRU_INACTIVE_FILE] +
-                       targets[LRU_ACTIVE_FILE] + 1;
+                       //targets[LRU_ACTIVE_FILE] + 
+                       1;
            lru = LRU_FILE;
            percentage = nr_file * 100 / scan_target;
        }

上記のコードでは、タブがスペースに変換されたため、github にも表示されますmirror1mirror2
上記のパッチをテストしました(現在4000MBの最大RAMで、以前より20G少ないです!)、OSを永久フリーズにディスクスラッシュすることが知られているFirefoxコンパイルでさえ、それはもう起こりません(oom-killer は、問題のあるプロセスをほぼ即座に強制終了しています)。また、上記のstressコマンドを使用すると、次の結果が得られます。

[  745.830511] Mem-Info:
[  745.830521] active_anon:855546 inactive_anon:20453 isolated_anon:0
                active_file:26925 inactive_file:76 isolated_file:0
                unevictable:10652 dirty:0 writeback:0 unstable:0
                slab_reclaimable:26975 slab_unreclaimable:13525
                mapped:24238 shmem:20456 pagetables:4028 bounce:0
                free:14935 free_pcp:177 free_cma:0

それactive_file:26925 inactive_file:76は、ほぼ 27 MB のアクティブなファイルです...
だから、これがどれほど良いかわかりません。実行可能ファイルだけでなく、すべてのアクティブなファイルをメモリに保持していますか? Firefoxのコンパイル中に、500メガのようなものを持っていましたActive(file)( EDIT2 しかし、それによると: dmesgからcat /proc/meminfo|grep -F -- 'Active(file)'上記とは異なる値を示していますactive_file:!!!)、それはexes / libsだけだったのではないかと疑っています...
多分誰かが方法を提案できます実行可能コードのみを保持しますか
? (それがすでに起こっていない場合)

EDIT3:上記のパッチでは、(定期的に?)sudo sysctl vm.drop_caches=1古いメモリを解放するために実行する必要があるようです(? ) 。その後、もう一度実行すると、次のようになります: (22メガ) - 私は確信が持てません... stressactive_file:142281 inactive_file:0 isolated_file:0echo 1|sudo tee /proc/sys/vm/drop_cachesstressactive_file:22233 inactive_file:160 isolated_file:0

上記のパッチを適用しない場合の結果:こちら
上記のパッチを適用した場合の結果:こちら

4

3 に答える 3

2

質問に答えるために、256 MiB 未満の場合にActive(file)( に見られるように) エビクトしないための簡単な/予備的なパッチを次に示します。これはlinux-stable 5.2.4で正常に動作するようです (ディスクのスラッシングなし) :/proc/meminfo

diff --git a/mm/vmscan.c b/mm/vmscan.c
index dbdc46a84f63..7a0b7e32ff45 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -2445,6 +2445,13 @@ static void get_scan_count(struct lruvec *lruvec, struct mem_cgroup *memcg,
            BUG();
        }

+    if (NR_ACTIVE_FILE == lru) {
+      long long kib_active_file_now=global_node_page_state(NR_ACTIVE_FILE) * MAX_NR_ZONES;
+      if (kib_active_file_now <= 256*1024) {
+        nr[lru] = 0; //don't reclaim any Active(file) (see /proc/meminfo) if they are under 256MiB
+        continue;
+      }
+    }
        *lru_pages += size;
        nr[lru] = scan;
    }

このパッチがなくても、カーネル 5.3.0-rc4-gd45331b00ddb のいくつかのまだ発見されていない リグレッションがシステム フリーズを引き起こすことに注意してください (ディスク スラッシングなしで、sysrq は引き続き動作します)。

(これに関連する新しい開発はここで行われるべきです。 )

于 2019-08-13T14:51:36.773 に答える