Я не розробник ядра, але я витрачав роки на те, щоб філософствувати над цим питанням, тому що я багато разів натрапляв на цей ооооо. Я фактично придумав метафору для всієї ситуації, тому дозвольте мені це сказати. У своїй історії я припускаю, що таких речей, як "своп", не існує. Заміни не має великого сенсу з 32 ГБ оперативної пам’яті в будь-який час.
Уявіть собі сусідство, де вода до кожної будівлі підключається за допомогою труб і міста повинні управляти потужністю. Припустимо, що у вас видобуток лише 100 одиниць води в секунду (а вся невикористана потужність йде у відходи, оскільки у вас немає резервуарів для резервуарів). Кожен будинок (будинок = невелика програма, термінал, віджет годинника тощо) вимагає однієї одиниці води в секунду. Це все добре і добре, адже ваше населення приблизно 90, тому кожен отримує достатню кількість води.
Тепер мер (= ви) вирішує, що ви хочете відкрити великий ресторан (= браузер). У цьому ресторані розміститься кілька кухарів (= вкладки браузера). Кожному кухарю потрібно 1 одиниця води в секунду. Ви починаєте з 10 кухарів, тож загальне споживання води для всього мікрорайону становить 100 одиниць води, що все ще добре.
Тепер починається веселощі: ви наймаєте ще одного кухаря у свій ресторан, який забезпечує загальні потреби у воді 101, чого, очевидно, у вас немає. Вам потрібно щось зробити.
Управління водою (= ядро) має 3 варіанти.
1. Перший варіант - просто відключити послугу для будинків, які недавно не користувалися водою. Це добре, але якщо відключений будинок захоче знову використати воду, їм потрібно буде знову пройти довгий процес реєстрації. Керівництво може відключити кілька будинків, щоб звільнити більше водних ресурсів. Насправді вони відключать усі будинки, які останнім часом не вживали воду, таким чином залишаючи завжди доступною кількість безкоштовної води.
Хоча ваше місто продовжує функціонувати, недоліком є те, що прогрес припиняється. Більшість вашого часу витрачається на очікування управління водою, щоб відновити послугу.
Це те, що ядро робить із підтримуваними файлами сторінками. Якщо ви запускаєте великий виконуваний файл (наприклад, хром), його файл копіюється в пам'ять. Якщо не вистачає пам’яті або є частини, до яких недавно не можна було отримати доступ, ядро може скинути ці частини, оскільки воно може перезавантажити їх з диска в будь-якому випадку. Якщо це зроблено надмірно, це перерве ваш робочий стіл, оскільки все буде просто чекати дискового вводу. Зауважте, що ядро також викине багато останніх використаних сторінок, коли ви почнете робити багато IO. Ось чому для переходу на фоновий додаток потрібні віки після того, як ви скопіювали кілька великих файлів, таких як зображення DVD.
Для мене це найприємніша поведінка, тому що я ненавиджу гикавку, і ти не маєш над цим контролю. Було б непогано мати можливість вимкнути це. Я щось замислююся
sed -i 's/may_unmap = 1/may_unmap = (vm_swappiness >= 0)/' mm/vmscan.c
і тоді ви можете встановити vm_swappiness на -1, щоб відключити це. Це спрацювало досить добре в моїх маленьких тестах, але, на жаль, я не розробник ядра, тому я не надсилав його нікому (і очевидно, що невелика модифікація вище не є повною).
2.Керівництво може відхилити запит нового кухаря про воду. Це спочатку звучить як гарна ідея. Однак є два мінуси. По-перше, є компанії, які вимагають багато передплати на воду, навіть не використовуючи їх. Однією з можливих причин цього є уникнення всіх накладних розмов з управлінням води, коли вони потребують додаткової води. Їх споживання води збільшується вгору і вниз, залежно від часу доби. Наприклад, у випадку з рестораном, компанії потрібно набагато більше води протягом полудня порівняно з півночі. Тому вони просять всю можливу воду, яку вони можуть використовувати, але це витрачає виділення води протягом півночі. Проблема полягає в тому, що не всі компанії можуть правильно передбачити своє пікове використання, тому вони вимагають набагато більше, сподіваючись, що їм ніколи не потрібно буде турбуватися про запити більше.
Це те, що робить віртуальна машина Java: вона виділяє купу пам'яті при запуску, а потім працює з цього. За замовчуванням ядро виділяє пам'ять лише тоді, коли ваша програма Java фактично почне її використовувати. Однак якщо ви відключите перезавантаження, ядро сприйме резервування серйозно. Це дозволить розподілу досягти успіху лише в тому випадку, якщо він насправді має ресурси для цього.
Однак існує ще одна, більш серйозна проблема з цим підходом. Скажімо, одна компанія починає запитувати одну одиницю води щодня (а не з кроків 10). Зрештою ви досягнете стану, коли у вас є 0 безкоштовних одиниць. Тепер ця компанія не зможе виділити більше. Це добре, хто все одно переймається великими компаніями. Але проблема в тому, що і малі будинки не зможуть вимагати більше води! Ви не зможете побудувати невеликі громадські санвузли для боротьби з раптовим напливом туристів. Ви не зможете забезпечити аварійну воду для пожежі в сусідньому лісі.
В комп'ютерному відношенні: у ситуаціях з дуже низькою пам’яттю без перевиконання ви не зможете відкрити новий xterm, ви не зможете ssh у свою машину, ви не зможете відкрити нову вкладку для пошуку можливих виправлення. Іншими словами, вимкнення режиму перевиконання також робить ваш робочий стіл непотрібним, коли не вистачає пам'яті.
3. Тепер ось цікавий спосіб вирішення проблеми, коли компанія починає вживати занадто багато води. Управління водою підірває це! Буквально: він заходить на сайт ресторану, кидає в нього динамітів і чекає, поки він вибухне. Це миттєво скоротить потреби води у місті в значній мірі, щоб нові люди могли переселитися, ви могли створити громадські ванні кімнати тощо. Ви, як міський голова, можете відновити ресторан, сподіваючись, що цього разу йому буде потрібно менше води. Наприклад, ви скажете людям не ходити в ресторани, якщо всередині занадто багато людей (наприклад, ви відкриєте менше вкладок браузера).
Це насправді те, що ядро робить, коли у нього закінчуються всі параметри і йому потрібна пам'ять: він викликає вбивцю OOM. Він підбирає велику програму (на основі багатьох евристик) і вбиває її, звільняючи купу пам'яті, але підтримуючи чуйний робочий стіл. Насправді ядро Android робить це ще більш агресивно: воно вбиває найменш використовуваний додаток, коли пам’яті мало (порівняно з біржовим ядром, яке робить це лише в крайньому випадку). Це називається вбивця вікінгів в Android.
Я думаю, що це одне з найпростіших варіантів вирішення проблеми: це не так, як у вас є більше варіантів, ніж це, то чому б не подолати її раніше, ніж пізніше, правда? Проблема полягає в тому, що ядро іноді робить досить багато роботи, щоб уникнути виклику вбивці OOM. Ось чому ви бачите, що ваш робочий стіл дуже повільний і ядро нічого не робить з цим. Але на щастя, є можливість самому викликати вбивцю OOM! По-перше, переконайтеся, що магічна клавіша sysrq увімкнена (наприклад, echo 1 | sudo tee
/proc/sys/kernel/sysrq
потім, коли ви відчуєте, що ядро втрачає пам'ять, просто натисніть Alt + SysRQ, Alt + f.
Гаразд, все, що приємно, але ви хочете спробувати? Ситуація з низькою пам’яттю відтворюється дуже просто. У мене є дуже просте додаток для цього. Вам потрібно буде запустити його двічі. Перший запуск визначить, скільки вільної оперативної пам’яті у вас є, другий запуск створить ситуацію з низькою пам’яттю. Зверніть увагу, що цей метод передбачає, що ви замінили своп (наприклад, зробіть a sudo swapoff -a
). Код та використання:
// gcc -std=c99 -Wall -Wextra -Werror -g -o eatmem eatmem.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char** argv)
{
int limit = 123456789;
if (argc >= 2) {
limit = atoi(argv[1]);
}
setbuf(stdout, NULL);
for (int i = 1; i <= limit; i++) {
memset(malloc(1 << 20), 1, 1 << 20);
printf("\rAllocated %5d MiB.", i);
}
sleep(10000);
return 0;
}
Ось як ви це використовуєте:
$ gcc -std=c99 -Wall -Wextra -Werror -g -o eatmem eatmem.c
$ ./eatmem
Allocated 31118 MiB.Killed
$ ./eatmem 31110
Allocated 31110 MiB.Killed
Перший виклик виявив, що ми маємо 31,118 МБ вільної оперативної пам’яті. Тому я сказав програмі виділити 31,110 Мб оперативної пам’яті, щоб ядро її не вбивало, а з'їдало майже всю мою пам'ять. Моя система замерзла: навіть вказівник миші не зрушив з місця. Я натиснув Alt + SysRQ, Alt + f, і це знищило мій процес eatmem, і система відновилася.
Незважаючи на те, що ми висвітлювали наші варіанти того, що робити в умовах низької пам'яті, найкращий підхід (як і будь-яка інша небезпечна ситуація) - це уникати його в першу чергу. Існує багато способів зробити це. Один із поширених способів, який я бачив, - це помістити недоброзичливі програми (наприклад, браузери) в інші контейнери, ніж у решті системи. У такому випадку браузер не зможе впливати на ваш робочий стіл. Але сама профілактика виходить за рамки питання, тому я не пишу про це.
TL; DR: Хоча в даний час не існує можливості повністю уникнути пейджингу, ви можете пом'якшити повну зупинку системи, відключивши перезавантаження. Але ваша система все ще буде непридатною під час ситуації з низькою пам'яттю, але по-іншому. Незалежно від вищезазначеного, у ситуації з низькою пам’яттю натисніть Alt + SysRQ, Alt + f, щоб знищити великий процес вибору ядра. Ваша система повинна відновити свою чутливість через кілька секунд. Це передбачає, що у вас включена чарівна клавіша sysrq (вона не є за замовчуванням).