Как сохранить исполняемый код в памяти даже под давлением памяти? в линуксе - PullRequest
0 голосов
/ 29 августа 2018

Целью здесь является сохранение исполняемого кода каждого запущенного процесса в памяти во время нехватки памяти в Linux.
В Linux я могу мгновенно (1 секунда) вызвать высокое давление памяти и вызвать OOM-killer с помощью stress --vm-bytes $(awk '/MemAvailable/{printf "%d\n", $2 + 4000;}' < /proc/meminfo)k --vm-keep -m 4 --timeout 10s (код от здесь ) с максимальной оперативной памятью 24000 МБ внутри операционной системы Qubes R4.0 Fedora 28. 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 они в килобайтах и ​​очень малы.

Проблема здесь заключается в том, что во время этого периода нехватки памяти исполняемый код перечитывается с диска, вызывая перегрузку диска, что приводит к зависанию ОС . (но в приведенном выше случае это происходит не более 1 секунды)

Я вижу интересный код в ядре 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 list мы получили значение give 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, потому что в приведенном выше коде вкладки превращаются в пробелы! ( mirror1 , mirror2 )
Я тестировал вышеупомянутый патч (на максимальной оперативной памяти 4000 МБ, да на 20 ГБ меньше, чем раньше!) Даже с известной компиляцией 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 мегабайт активного файла ...
Итак, я не знаю, насколько это хорошо. Сохраняю ли я все активные файлы вместо исполняемых файлов в памяти? Во время компиляции Firefox у меня было примерно 500 мг Active(file) ( EDIT2: , но это соответствует: cat /proc/meminfo|grep -F -- 'Active(file)', который показывает значение, отличное от приведенного выше active_file: из dmesg !!!), что вызывает у меня сомнения это были только exes / libs ...
Может быть, кто-то может подсказать, как сохранить ТОЛЬКО исполняемый код? (Если это не то, что уже происходит)
Мысли?

EDIT3: с вышеприведенным патчем, возможно, необходимо (периодически?) Запускать sudo sysctl vm.drop_caches=1 для освобождения некоторой устаревшей памяти (?), Так что если я вызову stress после компиляции Firefox Я получаю: active_file:142281 inactive_file:0 isolated_file:0 (142megs), затем удаляю файловые кэши (другой способ: echo 1|sudo tee /proc/sys/vm/drop_caches), затем снова запускаю stress, я получаю: active_file:22233 inactive_file:160 isolated_file:0 (22megs) - я не уверен ...

Результаты без вышеуказанного патча: здесь
Результаты с вышеуказанным патчем: здесь

Ответы [ 2 ]

0 голосов
/ 23 июня 2019

Параметр memory.min в контроллере памяти cgroups-v2 должен помочь.

А именно, позвольте мне процитировать:

"Жесткая защита памяти. Если использование памяти cgroup находится в пределах эффективная минимальная граница, память группы не будет восстановлена ​​в соответствии с любые условия. Если нет незащищенной исправимой памяти доступно, вызывается OOM killer. "

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

0 голосов
/ 11 сентября 2018

ПРЕДУПРЕЖДЕНИЕ: Не используйте этот патч, если у вас включен своп, поскольку два пользователя сообщили о худших эффектах. Я только что протестировал этот патч с отключенным в ядре свопом! (т.е. CONFIG_SWAP не установлен)

До дальнейшего уведомления (или если кто-то придумает что-то лучше), я использую (и это работает для меня) следующий патч , чтобы избежать перегрузки диска или зависания ОС при запуске Недостаточно памяти и, следовательно, OOM-killer запускается как можно быстрее (максимум 1 с):

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

К сожалению, приведенные выше конвертировали вкладки в пробелы, поэтому, если вам нужен необработанный патч, это здесь .

То, что делает этот патч, не высвобождает страницы Active(file), когда они находятся под давлением памяти, и, следовательно, не заставляет kswapd0 (но рассматривается в iotop как каждую программу) перечитывать исполняемые страницы каждого выполняющегося процесса каждый раз, когда есть переключатель контекста , чтобы позволить программе (продолжать) работать. Таким образом, можно избежать тонны перебивания диска, и ОС не зависает при сканировании.

Выше был протестирован с ядром 4.18.5 (и теперь тестирует 4.18.7) внутри dom0 Qubes OS 4.0 (Fedora 25) и всех виртуальных машин (Fedora 28), которые я использую.

Для первой версии этого патча, который также работает (по-видимому), см. EDIT по самому вопросу, на который это ответ.

ОБНОВЛЕНИЕ: После использования этого патча некоторое время на ноутбуке ArchLinux с 16 ГБ ОЗУ (минус 512 М зарезервировано для встроенной видеокарты) и без подкачки (также отключено в ядре) Я могу сказать, что система может не хватит памяти раньше, чем без le9d.patch (rev. 3), и поэтому OOM-killer запускает Xorg, chromium или другие, когда этого не было бы без патча. И поэтому, как мне кажется, это работает для меня, я запускаю echo 1 > /proc/sys/vm/drop_caches всякий раз, когда число Active(file) в / proc / meminfo превышает 2G, то есть 2000000 КБ (например, получить число КБ через этот код : grep 'Active(file):' /proc/meminfo|tr -d ' '|cut -f2 -d:|sed 's/kB//') и делать эту проверку с sleep 5 впоследствии. Но в последнее время для того, чтобы скомпилировать firefox-hg в / tmp, который является tmpfs и который в конечном итоге использует 12G, и убедиться, что он не уничтожен OOM, я использовал 500000 вместо 2000000 КБ. Это, конечно, лучше, чем заморозить всю систему (т.е. без le9d.patch), что произошло бы в этом случае компиляции firefox. Без этой проверки Active(file) идет не выше 4G, но этого достаточно для OOM-kill Xorg, если что-то требует больше памяти, например, в этом случае компиляции Firefox или даже при копировании большого количества гигабайт с помощью Midnight Commander (если я правильно помню это ).

...