GNU/Linux >> Znalost Linux >  >> Linux

Jak udržet spustitelný kód v paměti i pod tlakem paměti? v Linuxu

UPOZORNĚNÍ: Tuto opravu nepoužívejte, pokud máte povolenou swap, protože dva uživatelé hlásili horší efekty. Tento patch jsem testoval pouze se zakázaným swapem v jádře! (tj. CONFIG_SWAP není nastaven)

Dokud nebude další upozornění (nebo někdo nepřijde s něčím lepším), používám (a funguje to, pro mě) následující patch, abych se vyhnul jakémukoli rozbití disku / zamrznutí operačního systému, když dojde k nedostatku paměti, a tím i OOM-killeru spustí se co nejdříve (max 1 s):

revision 3
preliminary patch to avoid disk thrashing (constant reading) under memory pressure before OOM-killer triggers
more info: https://gist.github.com/constantoverride/84eba764f487049ed642eb2111a20830

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
@@ -2086,9 +2086,9 @@ static unsigned long shrink_list(enum lr
                 struct scan_control *sc)
 {
    if (is_active_lru(lru)) {
-       if (inactive_list_is_low(lruvec, is_file_lru(lru),
-                    memcg, sc, true))
-           shrink_active_list(nr_to_scan, lruvec, sc, lru);
+       //if (inactive_list_is_low(lruvec, is_file_lru(lru),
+       //           memcg, sc, true))
+       //  shrink_active_list(nr_to_scan, lruvec, sc, lru);
        return 0;
    }

@@ -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;
        }
@@ -2409,10 +2411,12 @@ static void shrink_node_memcg(struct pgl
        nr[lru] = targets[lru] * (100 - percentage) / 100;
        nr[lru] -= min(nr[lru], nr_scanned);

+       if (LRU_FILE != lru) { //avoid this block for LRU_ACTIVE_FILE
        lru += LRU_ACTIVE;
        nr_scanned = targets[lru] - nr[lru];
        nr[lru] = targets[lru] * (100 - percentage) / 100;
        nr[lru] -= min(nr[lru], nr_scanned);
+       }

        scan_adjusted = true;
    }

Bohužel výše uvedené karty převedly na mezery, takže pokud chcete nezpracovaný patch, je zde.

Tato oprava nevylučuje Active(file) stránky pod tlakem paměti, a tedy nezpůsobí kswapd0 (ale vidět v iotop jako každý program sám) znovu načíst spustitelné stránky každého běžícího procesu pokaždé, když dojde k přepnutí kontextu, aby bylo možné programu (pokračovat) spuštění. Vyhnete se tak spoustě disků a operační systém nezamrzne.

Výše uvedené bylo testováno s jádrem 4.18.5 (a nyní testováním 4.18.7) uvnitř dom0 Qubes OS 4.0 (Fedora 25) a všech virtuálních počítačů (Fedora 28), které používám.

Pro první verzi tohoto patche, který také funguje (zřejmě), viz EDIT právě na otázku, na kterou je toto odpověď.

AKTUALIZACE: Po chvíli používání tohoto patche na notebooku ArchLinux s 16G RAM (minus 512M vyhrazených pro integrovanou grafickou kartu) a bez swapu (vypnuto i v jádře) mohu říci, že systému může dojít paměť dříve než bez le9d.patch (rev. 3), a tak se OOM-killer spouští pro Xorg nebo chromium nebo jiné, když by to bez patche nebylo. A tak jako zmírnění, zdá se, že to pro mě zatím funguje, jsem spouštěl echo 1 > /proc/sys/vm/drop_caches kdykoli Active(file) číslo v /proc/meminfo je přes 2G alias 2000000 KB (např. získejte počet KB pomocí tohoto kódu:grep 'Active(file):' /proc/meminfo|tr -d ' '|cut -f2 -d:|sed 's/kB//' ) a tuto kontrolu proveďte pomocí sleep 5 později. Ale v poslední době, abych zkompiloval firefox-hg v /tmp, což je tmpfs a které nakonec používá 12G a zajistilo se, že nebude zabito OOM, používám 500 000 místo 2 000 000 KB. Určitě je to lepší, než zmrazit celý systém (tj. když bez le9d.patch), což by se stalo v tomto případě kompilace firefox. Bez této kontroly Active(file) nejde výše než 4G, ale to stačí k OOM-zabití Xorg, pokud něco chce více paměti, jako například v tomto případě kompilace firefox nebo dokonce při pouhém kopírování mnoha gigabajtů přes půlnoční velitele (pokud si to dobře pamatuji).


Pomoci by měl parametr memory.min v paměťovém řadiči cgroups-v2.

Dovolím si citovat:

"Ochrana pevné paměti. Pokud je využití paměti cgroup v rámci její efektivní minimální hranice, paměť cgroup nebude za žádných podmínek obnovena. Pokud není k dispozici žádná nechráněná regenerovatelná paměť, je vyvolán zabiják OOM."

https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html


Chcete-li odpovědět na otázku, zde je jednoduchá/předběžná oprava, která nezabrání vystěhování Active(file) (jak je vidět v /proc/meminfo ) pokud je to méně než 256 MiB, zdá se, že to funguje dobře (žádné lámání disku) s linux-stable 5.2.4:

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;
    }

Všimněte si, že některé zatím nebudou nalezeny regrese na jádře 5.3.0-rc4-gd45331b00ddb způsobí zamrznutí systému (bez rozbití disku a sysrq bude stále fungovat) i bez tohoto patche.

(jakýkoli nový vývoj související s tímto by se měl odehrát zde.)


Linux
  1. Jak vymazat mezipaměť v Linuxu

  2. Linux – Jak otestovat, zda byl binární soubor Linuxu zkompilován jako kód nezávislý na pozici?

  3. Jak používat FTP pod Linuxem k přenosu souborů

  1. Jak používat příkaz tar pod Linuxem

  2. Jak používat přesměrování příkazů pod Linuxem

  3. Jak kódovat modul jádra Linuxu?

  1. Jak rozebrat, upravit a poté znovu sestavit spustitelný soubor Linuxu?

  2. Jak rozebrat binární spustitelný soubor v Linuxu, abyste získali kód sestavení?

  3. Jak hodnotit-limitovat potrubí pod linuxem?