GNU/Linux >> Linux Esercitazione >  >> Linux

Come mantenere il codice eseguibile in memoria anche sotto pressione della memoria? su Linux

ATTENZIONE: Non usare questa patch se hai abilitato lo scambio, perché due utenti hanno riportato effetti peggiori. Ho testato questa patch solo con lo swap disabilitato nel kernel! (es. CONFIG_SWAP non è impostato)

Fino a nuovo avviso (o qualcuno esce con qualcosa di meglio), sto usando (e funziona, per me) la seguente patch per evitare qualsiasi thrashing del disco / blocco del sistema operativo quando sta per esaurire la memoria e quindi il killer OOM si attiva il prima possibile (max 1 sec):

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

Sfortunatamente le schede sopra convertite in spazi, quindi se vuoi la patch grezza è qui.

Ciò che fa questa patch non è rimuovere Active(file) pagine sotto pressione della memoria e quindi non causare kswapd0 (ma visto in iotop come ogni programma stesso) per rileggere le pagine eseguibili di ogni processo in esecuzione ogni volta che c'è un cambio di contesto per consentire al programma di (continuare a) funzionare. Pertanto, viene evitata una tonnellata di thrashing del disco e il sistema operativo non si blocca in una ricerca per indicizzazione.

Quanto sopra è stato testato con il kernel 4.18.5 (e ora test 4.18.7) all'interno di dom0 (Fedora 25) di Qubes OS 4.0 e tutte le VM (Fedora 28) che sto usando.

Per la prima versione di questa patch, che funziona anch'essa (apparentemente), guarda il EDIT proprio sulla domanda di cui questa è una risposta.

AGGIORNAMENTO: Dopo aver usato questa patch per un po' su un laptop ArchLinux con 16G RAM (meno 512M riservati alla scheda grafica integrata) e nessuno scambio (disabilitato anche nel kernel) posso dire che il sistema può esaurire la memoria prima che senza le9d.patch (rev. 3), e quindi OOM-killer si attiva per Xorg o chromium o altro quando non avrebbe avuto senza la patch. E quindi come mitigazione, che finora sembra funzionare per me, ho eseguito echo 1 > /proc/sys/vm/drop_caches ogni volta che il Active(file) il numero in /proc/meminfo è superiore a 2G ovvero 2000000 KB (ad es. ottieni il numero di KB tramite questo codice:grep 'Active(file):' /proc/meminfo|tr -d ' '|cut -f2 -d:|sed 's/kB//' ) e facendo questo controllo con un sleep 5 dopo. Ma ultimamente per compilare firefox-hg in /tmp che è tmpfs e che alla fine utilizza 12G e assicurarmi che non venga ucciso da OOM, ho usato 500000 invece di 2000000 KB. È sicuramente meglio che congelare l'intero sistema (cioè senza le9d.patch) che sarebbe successo in questo caso di compilazione di firefox. Senza questo controllo, Active(file) non supera il 4G, ma è sufficiente per OOM-kill Xorg se qualcosa richiede più memoria, come in questo caso di compilation di firefox o anche quando si copiano solo molti gigabyte tramite il comandante di mezzanotte (se ricordo bene).


Il parametro memory.min nel controller di memoria cgroups-v2 dovrebbe essere d'aiuto.

Vale a dire, lasciatemi citare:

"Protezione della memoria rigida. Se l'utilizzo della memoria di un cgroup rientra nel suo limite minimo effettivo, la memoria del cgroup non verrà recuperata in nessuna condizione. Se non è disponibile memoria recuperabile non protetta, viene invocato OOM killer."

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


Per rispondere alla domanda, ecco una patch semplice/preliminare per non sfrattare Active(file) (come visto in /proc/meminfo ) se è inferiore a 256 MiB, sembra funzionare bene (nessun disco thrashing) con 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;
    }

Nota che alcuni ancora da trovare la regressione sul kernel 5.3.0-rc4-gd45331b00ddb causerà un blocco del sistema (senza thrashing del disco e sysrq funzionerà ancora) anche senza questa patch.

(qualsiasi nuovo sviluppo relativo a questo dovrebbe avvenire qui.)


Linux
  1. Linux:come verificare se un binario Linux è stato compilato come codice indipendente dalla posizione?

  2. Come smontare, modificare e poi rimontare un eseguibile Linux?

  3. Come codificare un modulo del kernel Linux?

  4. Come trovare l'utilizzo della memoria dell'utente in Linux

  5. Come limitare la velocità di una pipe sotto Linux?

Come cancellare la memoria di scambio in Linux

Come disabilitare permanentemente lo scambio in Linux

Come rendere eseguibile un file nel terminale Linux?

Come funziona la memoria di scambio in Linux?

Come mantenere aggiornato Rocky Linux 8

Come posso profilare il codice C++ in esecuzione su Linux?