Как сохранить исполняемый код в памяти даже под давлением памяти? в Linux
Цель здесь состоит в том, чтобы сохранить исполняемый код каждого запущенного процесса в памяти во время сжатия памяти, в Linux.
В Linux, я могу мгновенно (1 сек) вызвать высокое давление памяти и вызвать OOM-killer с помощью
stress --vm-bytes $(awk '/MemAvailable/{printf "%dn", $2 + 4000;}' < /proc/meminfo)k --vm-keep -m 4 --timeout 10s
(код из здесь )
с 24000 МБ оперативной памяти внутри Qubes OS R4.0 Fedora 28 AppVM. EDIT4: возможно, уместным, и все же я забыл упомянуть, является тот факт, что у меня не включен swap (т. е. 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
они находятся в килобайтах и очень малы.
Я вижу интересный код в ядре 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
, тогда работа должна быть сделана. Или, может быть, есть какой-то другой способ?
Я могу исправить и протестировать пользовательское ядро. У меня просто нет ноу-хау относительно того, что нужно изменить в коде, чтобы всегда держать активный исполняемый код в памяти(что, по моему мнению, позволит избежать дисковой трэшировки).
EDIT: вот что я получил, работая до сих пор (применяется поверх ядра 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 )
Я протестировал вышеописанный патч(на 4000MB max RAM сейчас, да на 20G меньше, чем раньше!) даже с компиляцией 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 у меня было около 500meg Active(file)
(EDIT2: но это согласно: cat /proc/meminfo|grep -F -- 'Active(file)'
, который показывает другое значение, чем выше active_file:
из dmesg!!!) что заставляет меня сомневаться, что это были только бывшие / либы...
Может быть, кто-то подскажет, как сохранить только исполняемый код ?(если это не то, что уже происходит)
Мысли?
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) - я не уверен...
Результаты без вышеуказанного патча: здесь
Результаты с вышеуказанным патчем: здесь
1 ответ:
До дальнейшего уведомления(или кто-то придумает что-то получше), я использую (и это работает, для меня) следующий патч , чтобы избежать любого дискового трэша / замораживания ОС, когда вот-вот закончится память и, таким образом, ООМ-киллер срабатывает как можно скорее(максимум 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) внутри Qubes OS 4.0 's dom0 (Fedora 25) и всех виртуальных машин (Fedora 28), которые я использую.
Для первой версии об этом патче, который также работает(по-видимому), смотрите
EDIT
на тот самый вопрос, на который это ответ.