Linux для DevOps і DevSecOps. Частина 4. Процеси, сигнали та керування системою

04.11.2026 5 хвилин Автор: Lady Liberty

Кожна запущена програма в Linux працює у вигляді одного або кількох процесів, якими керує ядро операційної системи. Від правильного розподілу процесорного часу, оперативної пам’яті та інших ресурсів залежить стабільність серверів, застосунків і системних служб. У четвертій, завершальній частині серії «Linux для DevOps і DevSecOps» розберемо життєвий цикл процесів, PID і PPID, сигнали, стани виконання, файлову систему /proc, планувальник Linux, керування пріоритетами, механізми OOM Killer і cgroups, а також інструменти моніторингу й діагностики системи.

У попередніх частинах серії ми розглянули файлову систему Linux, навігацію через термінал, керування користувачами та групами, а також механізми розмежування доступу. Тепер перейдемо до процесів, які безпосередньо виконують роботу в операційній системі. Саме вони обробляють мережеві запити, запускають вебзастосунки, виконують автоматизовані завдання, працюють із базами даних і забезпечують функціонування системних служб.

Для DevOps розуміння процесів необхідне під час діагностики навантаження, пошуку причин зависання застосунків, керування службами та налаштування ресурсних обмежень. Для DevSecOps ці знання також допомагають аналізувати підозрілу активність, визначати походження невідомих процесів, перевіряти їхні повноваження та виявляти нетипову поведінку системи.

У цій частині розглянемо не лише основні команди, а й механізми, які стоять за ними. Розберемо, як Linux створює процеси, чому вони переходять у різні стани, як ядро розподіляє ресурси та що відбувається, коли оперативної пам’яті стає недостатньо. Для практичних експериментів рекомендуємо використовувати окрему віртуальну машину з Ubuntu або Debian, щоб не впливати на роботу основного середовища.

Що таке процес у Linux і як він працює

Процес – це екземпляр програми, який виконується під керуванням операційної системи. Поки програма зберігається на диску, вона залишається набором інструкцій і даних. Коли користувач або інший процес запускає її, ядро Linux створює необхідне середовище виконання, визначає ідентифікатори, налаштовує пам’ять та інші ресурси.

Наприклад, коли адміністратор запускає команду python3 app.py, система створює процес інтерпретатора Python, який виконує код зазначеного файлу. Аналогічно працюють Nginx, PostgreSQL, SSH-сервери та більшість інших програм. Навіть звичайна командна оболонка Bash є процесом, який може створювати дочірні процеси для виконання команд.

Кожен процес має набір характеристик, що визначають його поведінку та взаємодію з операційною системою. До найважливіших належать:

Атрибут Призначення
PID Унікальний ідентифікатор процесу в межах відповідного простору PID
PPID Ідентифікатор батьківського процесу
UID / GID Ідентифікатори користувача та групи
CWD Поточний робочий каталог
Environment Змінні середовища
File Descriptors Відкриті файли, сокети та інші ресурси
Memory Віртуальний адресний простір
State Поточний стан виконання
Priority Параметри планування процесорного часу

Процес має власний віртуальний адресний простір, який відокремлює його пам’ять від пам’яті інших процесів. Це не означає, що кожен процес обов’язково отримує окрему фізичну ділянку RAM для всіх своїх даних. Ядро використовує механізми віртуальної пам’яті, спільних бібліотек, відображень файлів та інших оптимізацій.

Для роботи з файлами й мережевими з’єднаннями процес використовує файлові дескриптори. Зазвичай під час запуску консольної програми вже доступні три стандартні дескриптори: 0 для вводу, 1 для звичайного виводу та 2 для повідомлень про помилки. Проте конкретне оточення залежить від способу запуску, і деякі дескриптори можуть бути перенаправлені або закриті.

Наприклад, коли команда записує результат у файл через >, командна оболонка налаштовує відповідне перенаправлення. Коли застосунок відкриває TCP-з’єднання, він отримує дескриптор сокета, через який може обмінюватися даними.

Ще одна важлива характеристика – змінні середовища. Через них процес може отримувати налаштування, шляхи до каталогів, параметри локалізації та іншу інформацію. Зокрема, PATH визначає каталоги пошуку виконуваних програм, HOME позначає домашній каталог, а LANG впливає на мовні та регіональні налаштування.

Водночас змінні середовища іноді містять секрети: токени, паролі або ключі доступу до зовнішніх сервісів. Тому їх потрібно враховувати під час аналізу безпеки процесів та уникати випадкового виведення конфіденційних значень у журнали.

Чим процес відрізняється від потоку

Окрім процесів, Linux підтримує потоки виконання, або threads. Вони дозволяють одній програмі виконувати кілька завдань одночасно або паралельно, залежно від кількості доступних процесорних ядер і роботи планувальника.

Наприклад, вебсервер може використовувати кілька потоків для обробки різних запитів. Вони можуть спільно використовувати адресний простір, відкриті файлові дескриптори та інші ресурси одного процесу, але кожен потік має власний стан виконання, стек і низку індивідуальних характеристик.

У Linux потоки реалізовані через механізми ядра, споріднені зі створенням процесів. Кожен потік має свій TID (Thread ID), тоді як усі потоки одного процесу зазвичай належать до спільної thread group з одним TGID, який користувацькі утиліти часто показують як PID процесу.

Щоб переглянути потоки, можна використати:

ps -eLf

Або запустити top у режимі відображення окремих потоків:

top -H

Для DevOps це важливо під час аналізу багатопотокових застосунків. Якщо один процес використовує кілька ядер або створює надмірну кількість потоків, потрібно розуміти, які саме завдання виконуються всередині нього та як вони впливають на загальне навантаження.

Життєвий цикл процесу: створення, виконання та завершення

Життєвий цикл процесу починається з його створення та завершується тоді, коли ядро й батьківський процес виконали необхідні дії для звільнення ресурсів і отримання інформації про завершення.

У класичній моделі Unix новий процес створюється за допомогою системного виклику fork(). Він формує дочірній процес, який спочатку має характеристики, успадковані від батьківського. Після цього часто використовується один із викликів сімейства exec, який замінює виконуваний образ процесу новою програмою.

У сучасному Linux також застосовуються clone() і clone3(), які дозволяють точніше визначати, які ресурси будуть спільними між завданнями. Саме ці механізми використовуються, зокрема, під час роботи з потоками та просторами імен.

Уявімо, що користувач відкрив Bash і виконав команду:

sleep 120

Командна оболонка організовує запуск програми sleep, яка очікуватиме 120 секунд. Для цього створюється відповідний процес, а Bash зазвичай чекає його завершення, якщо команда запущена у звичайному режимі переднього плану.

Якщо додати символ &, процес буде запущений як фонове завдання оболонки:

sleep 120 &

Тепер Bash поверне можливість вводити нові команди, не очікуючи завершення sleep.

Переглянути поточні фонові завдання можна командою:

jobs -l

А для повернення завдання на передній план використовується fg. Команда bg дозволяє відновити призупинене завдання у фоновому режимі.

Важливо розрізняти фонові завдання оболонки та повноцінні системні служби. Процес, запущений через &, не обов’язково переживе закриття термінала. Для тривалої роботи серверних застосунків зазвичай використовують спеціальні менеджери служб, зокрема systemd.

Що відбувається після завершення процесу

Коли програма завершує виконання, вона повідомляє системі код завершення. За загальноприйнятою домовленістю значення 0 означає успіх, а ненульові значення часто сигналізують про помилку або інший результат, який потребує обробки.

У Bash код останньої завершеної команди доступний через $?:

true
echo $?

false
echo $?

Перша команда зазвичай завершиться з кодом 0, друга – з кодом 1. Для автоматизації це має особливе значення. Скрипти розгортання, перевірки стану застосунків та CI/CD-процеси часто визначають успішність операції саме за кодом завершення.

Після завершення процесу ядро звільняє більшість його ресурсів, проте тимчасово зберігає інформацію, необхідну батьківському процесу для отримання статусу. Для цього використовуються системні виклики сімейства wait().

Якщо батьківський процес ще не отримав цю інформацію, завершений дочірній процес може залишатися в стані zombie. Такий процес уже не виконує код і не споживає процесорний час, але його запис продовжує існувати в таблицях ядра.

Кілька короткочасних zombie-процесів не обов’язково означають проблему. Небезпека виникає тоді, коли вони накопичуються через помилки в батьківській програмі, яка не обробляє завершення дочірніх процесів.

Перевірити їх можна так:

ps -eo pid,ppid,stat,comm | awk '$3 ~ /^Z/'

Тут ми використовуємо ps для отримання інформації про процеси, а awk для відбору записів зі станом Z.

Важливо, що завершити zombie звичайним сигналом уже неможливо: процес фактично завершив роботу. Потрібно досліджувати батьківський процес, який не отримує його статус. Якщо батько завершується, дочірні процеси-сироти передаються відповідному процесу-усиновлювачу, наприклад PID 1 або налаштованому subreaper, який може виконати необхідний wait().

PID, PPID та дерево процесів Linux

Кожен процес у Linux отримує числовий ідентифікатор PID (Process ID). Він використовується для звернення до процесу, надсилання сигналів, перегляду його стану та інших операцій керування. PPID (Parent Process ID) позначає ідентифікатор батьківського процесу. Завдяки цьому Linux підтримує ієрархію процесів, у якій можна простежити, яка програма запустила іншу.

Переглянути загальний список процесів можна командою:

ps -ef

Для більш зручного аналізу можна вибрати лише необхідні колонки:

ps -eo pid,ppid,user,stat,comm

Тут відображаються PID, PPID, користувач, стан процесу та коротка назва програми.

Щоб побачити ієрархію у вигляді дерева, використовується pstree:

pstree -p

На типовому сервері можна побачити, як від менеджера системи відгалужуються служби, користувацькі сесії та їхні дочірні процеси.

Особливе місце займає PID 1. У багатьох сучасних дистрибутивах це systemd, який запускається на ранньому етапі завантаження та відповідає за керування системними службами. Однак в інших середовищах PID 1 може належати іншій init-системі або спеціальному процесу, зокрема всередині контейнера.

Перевірити процес із PID 1 можна так:

ps -p 1 -o pid,ppid,comm,args

У типовій системі з systemd ви побачите відповідний менеджер ініціалізації. Варто також згадати kthreadd, який у звичайному початковому просторі PID Linux часто має номер 2. Він пов’язаний зі створенням потоків ядра, що виконують внутрішні системні завдання. У списках процесів такі потоки нерідко відображаються в квадратних дужках, наприклад [kworker/0:0].

Ще один важливий момент – PID не є незмінним глобальним ідентифікатором. Після завершення процесу його номер згодом може бути використаний повторно. Крім того, завдяки PID namespaces один і той самий процес може мати різні ідентифікатори всередині контейнера та на хостовій системі.

Це потрібно враховувати під час написання скриптів, які працюють із PID, особливо якщо між пошуком процесу та виконанням операції минає певний час.

Основні стани процесів Linux

Під час роботи процес може виконувати код, очікувати отримання даних, бути призупиненим або завершуватися. Для відображення цих ситуацій Linux використовує спеціальні стани, які можна побачити в колонці STAT команди ps.

Наприклад:

ps aux

У результаті кожен процес матиме набір характеристик, серед яких є стан виконання.

Основні позначення:

Стан Значення Опис
R Running Виконується або очікує процесорного часу
S Interruptible Sleep Очікує подію та може реагувати на сигнали
D Uninterruptible Sleep Перебуває в очікуванні, яке не переривається звичайними сигналами
T Stopped Призупинений керувальним сигналом
t Tracing Stop Зупинений під час трасування
Z Zombie Завершений, але статус ще не отриманий батьківським процесом
I Idle Стан, який використовується для окремих потоків ядра

Не кожен процес, який перебуває у стані S, є завислим. У багатьох випадках це нормальна поведінка: програма очікує мережеве з’єднання, таймер, введення користувача або іншу подію. Сервер може більшу частину часу перебувати в стані очікування й переходити до виконання лише після надходження нових запитів.

Стан D часто привертає увагу адміністраторів, особливо якщо процес залишається в ньому надто довго. Це може бути пов’язано з очікуванням операцій вводу-виводу, файлових систем або пристроїв. Проте сам по собі цей стан не доводить несправність.

Якщо процес тривалий час не реагує на сигнали, потрібно перевіряти, на яку саме операцію він очікує. Наприклад, проблема може бути пов’язана з недоступним мережевим сховищем, повільним диском або завислою операцією в ядрі.

У колонці STAT також можуть відображатися додаткові символи. Наприклад, s позначає лідера сесії, l – багатопотоковий процес, < – підвищений пріоритет, N – знижений пріоритет, а + – належність до групи процесів переднього плану термінала.

Для перегляду лише вибраних характеристик можна виконати:

ps -eo pid,ppid,stat,%cpu,%mem,comm

Ця команда дозволяє швидко порівняти стани процесів із використанням процесора та оперативної пам’яті.

Сигнали Linux: керування виконанням процесів

Сигнали – це механізм, за допомогою якого ядро або інші процеси можуть повідомляти процесу про певну подію чи вимагати конкретної дії. Наприклад, коли користувач натискає Ctrl+C у терміналі, активна група процесів переднього плану зазвичай отримує сигнал SIGINT. Коли адміністратор хоче коректно завершити службу, він може надіслати SIGTERM. А якщо процес не реагує на звичайне завершення, іноді застосовується SIGKILL.

Процес може обробляти деякі сигнали через власні обробники, ігнорувати їх або використовувати стандартну поведінку системи. Водночас SIGKILL і SIGSTOP не можна перехопити чи проігнорувати звичайним користувацьким обробником.

Найважливіші сигнали:

Сигнал Типовий номер у Linux Призначення
SIGTERM 15 Запит на завершення процесу
SIGKILL 9 Примусове завершення
SIGINT 2 Переривання виконання
SIGHUP 1 Зміна стану термінала або визначена програмою дія
SIGSTOP 19 Примусове призупинення
SIGCONT 18 Відновлення виконання
SIGCHLD 17 Повідомлення про зміну стану дочірнього процесу

Номери деяких сигналів відрізняються залежно від архітектури, тому в скриптах часто зручніше використовувати їхні символьні назви.

Як правильно завершувати процеси

Для надсилання сигналів використовується команда kill. Попри назву, вона призначена не лише для примусового завершення, а й для передавання інших сигналів.

Наприклад:

kill -TERM 1234

Це надішле процесу з PID 1234 сигнал SIGTERM. Якщо програма має відповідний обробник, вона може завершити поточні операції, закрити з’єднання, зберегти необхідні дані та коректно завершитися.

Якщо процес не реагує, можна використати:

kill -KILL 1234

Або короткий запис:

kill -9 1234

Однак SIGKILL слід залишати крайнім варіантом. Він не дозволяє процесу виконати звичайний код очищення, тому може призвести до втрати незбережених даних, незавершених операцій або інших проблем.

До того ж навіть SIGKILL не гарантує миттєвого зникнення процесу, якщо той перебуває в непреривному очікуванні всередині ядра.

Для пошуку процесів за назвою та надсилання сигналів існують pgrep і pkill:

pgrep -a nginx 
pkill -TERM -x nginx

Перша команда знаходить відповідні процеси та показує їхні PID разом із командними рядками. Друга надсилає SIGTERM процесам із точним іменем nginx, доступним поточному користувачу. На робочому сервері потрібно особливо уважно ставитися до pkill, оскільки збіг за назвою може охопити кілька процесів одночасно.

Окремо варто згадати SIGHUP. Деякі демони використовують його для повторного читання конфігурації, але це поведінка конкретної програми, а не універсальне правило Linux. Наприклад, Nginx підтримує відповідний механізм перезавантаження конфігурації без повної зупинки сервера.

Для системних служб краще використовувати спеціалізовані інструменти керування, зокрема systemctl, оскільки вони враховують конфігурацію служби та її життєвий цикл.

Файлова система /proc: інформація про запущені процеси

У першій частині серії ми згадували каталог /proc як один із важливих компонентів файлової системи Linux. Тепер розглянемо його детальніше, оскільки він є одним із найкорисніших джерел інформації для діагностики процесів.

/proc – це віртуальна файлова система procfs, через яку ядро надає відомості про процеси та власний стан. Більшість її файлів не зберігається на диску у звичайному вигляді: вміст формуються ядром у відповідь на звернення.

Для кожного видимого процесу зазвичай існує каталог із його PID. Наприклад, /proc/1 містить інформацію про процес із PID 1, а /proc/self дозволяє звернутися до каталогу поточного процесу. Усередині каталогу процесу можна знайти дані про командний рядок, пам’ять, файлові дескриптори, робочий каталог, статус та інші характеристики.

Основні файли:

Шлях Інформація
/proc/PID/status Стан процесу, UID, GID, пам’ять, потоки
/proc/PID/cmdline Аргументи запуску
/proc/PID/environ Змінні середовища
/proc/PID/exe Посилання на виконуваний файл
/proc/PID/cwd Поточний робочий каталог
/proc/PID/fd/ Відкриті файлові дескриптори
/proc/PID/limits Ресурсні обмеження
/proc/PID/io Статистика операцій вводу-виводу
/proc/PID/maps Відображення віртуальної пам’яті
/proc/PID/cgroup Належність до контрольних груп

Наприклад, щоб перевірити базові характеристики PID 1, можна виконати:

grep -E '^(Name|State|Pid|PPid|Uid|VmRSS|Threads):' /proc/1/status

У результаті побачимо назву процесу, його стан, ідентифікатори, використання пам’яті та кількість потоків.

Для перегляду командного рядка:

tr '\0' ' ' < /proc/1/cmdline

Аргументи у файлі cmdline зазвичай розділяються нульовими байтами, тому tr використовується для їх перетворення на пробіли.

Щоб дізнатися, який виконуваний файл пов’язаний із процесом, можна переглянути символічне посилання:

ls -l /proc/1/exe

Іноді після оновлення програми посилання може містити позначку (deleted). Це означає, що шлях до виконуваного файлу більше не відповідає наявному файловому запису, хоча процес продовжує використовувати вже завантажений виконуваний образ.

Для DevOps це може бути сигналом, що службу ще не перезапустили після оновлення програмного забезпечення. Для DevSecOps такий випадок також може бути цікавим під час аналізу невідомих або підозрілих процесів, хоча сам по собі він не доводить компрометацію.

Файлові дескриптори та проблема Too many open files

Кожен процес може відкривати файли, сокети, канали й інші об’єкти через файлові дескриптори. Їхня кількість обмежується ресурсними лімітами та доступними можливостями системи. Якщо застосунок постійно створює нові з’єднання або відкриває файли, але не звільняє їх належним чином, він може досягти встановленого ліміту.

У такому разі в журналах іноді з’являється помилка:

Too many open files

Перевірити поточні дескриптори процесу можна через /proc:

ls -l /proc/self/fd

Для отримання інформації про ліміти процесу:

cat /proc/self/limits

На реальному сервері потрібно перевіряти конкретний PID застосунку, а не /proc/self, оскільки останній вказує на процес, який виконує поточне звернення.

Також можна використати lsof, який відображає відкриті файли та інші ресурси:

sudo lsof -p 1234

Тут 1234 – приклад PID процесу, який потрібно дослідити. Важливо розуміти, що велика кількість відкритих дескрипторів не завжди означає витік. Для високонавантажених мережевих служб тисячі активних з’єднань можуть бути нормальними. Проблемою є неконтрольоване зростання або невідповідність кількості дескрипторів реальним потребам програми.

Планувальник Linux і розподіл процесорного часу

На звичайному сервері кількість процесів і потоків значно перевищує кількість доступних ядер CPU. Тому ядро повинно вирішувати, які завдання отримають процесорний час, у якому порядку та з якими пріоритетами. За це відповідає планувальник процесів Linux. Він намагається ефективно використовувати процесорні ресурси, підтримувати реакцію інтерактивних програм і враховувати встановлені політики планування.

Історично стандартне планування звичайних завдань у Linux тривалий час пов’язували з CFS (Completely Fair Scheduler). У сучасних версіях ядра використовується також механізм EEVDF (Earliest Eligible Virtual Deadline First), інтегрований у систему справедливого планування. Тому конкретна поведінка залежить від версії ядра та обраної політики. Для адміністратора головне розуміти, що планувальник розподіляє процесорний час між завданнями, враховуючи їхні параметри, поточне навантаження та правила системи.

Nice і renice: зміна пріоритету процесів

Одним із найпростіших способів вплинути на розподіл процесорного часу є параметр nice. Для звичайних процесів він має значення від -20 до 19. Чим менше значення nice, тим вища відносна вага завдання під час стандартного планування. Чим більше значення, тим нижча його відносна пріоритетність. Значення 0 є типовим для багатьох процесів, хоча конкретні параметри можуть змінюватися залежно від способу запуску.

Для запуску програми зі зниженим пріоритетом використовується nice:

nice -n 10 sleep 120

Це створить процес sleep зі значенням nice 10.

У реальних сценаріях такий підхід може використовуватися для резервного копіювання, пакетної обробки даних, індексації або інших другорядних завдань, які не повинні надмірно конкурувати з критичними службами.

Щоб змінити nice вже запущеного процесу, використовується renice:

renice -n 15 -p 1234

Для підвищення пріоритету, тобто встановлення нижчого значення nice, зазвичай потрібні відповідні привілеї, наприклад CAP_SYS_NICE, або дозволи, пов’язані з ресурсними лімітами. Звичайний користувач за типової конфігурації може лише знижувати пріоритет власних процесів.

Переглянути значення nice можна так:

ps -eo pid,ni,pri,%cpu,comm --sort=ni

Важливо, що nice не резервує процесорне ядро й не гарантує певний відсоток CPU. Це лише параметр відносного планування для відповідних політик.

Політики планування та chrt

Для складніших завдань Linux підтримує різні політики планування. Вони визначають правила, за якими процеси конкурують за процесорний час.

Основні політики:

Політика Призначення
SCHED_OTHER Стандартне планування звичайних процесів
SCHED_BATCH Завдання з пакетною обробкою
SCHED_IDLE Фонові завдання з дуже низькою пріоритетністю
SCHED_FIFO Планування реального часу без звичайного квантування
SCHED_RR Планування реального часу з квантами часу
SCHED_DEADLINE Планування на основі заданих часових параметрів

Для перегляду та налаштування політик використовується chrt.

Наприклад, політику поточного процесу оболонки можна перевірити так:

chrt -p $$

Тут $$ містить PID поточної оболонки. Для звичайного адміністрування більшості застосунків достатньо стандартного планування та налаштувань nice. Політики реального часу потрібно використовувати дуже обережно, оскільки неправильні параметри можуть негативно впливати на реакцію системи.

Особливо небезпечно призначати високі пріоритети реального часу програмам, які можуть тривалий час виконувати обчислення без блокувань або добровільної передачі процесора. Тому експерименти з SCHED_FIFO, SCHED_RR і SCHED_DEADLINE краще проводити лише в окремому тестовому середовищі.

ionice і taskset: диск та процесорні ядра

Окрім CPU, процеси активно використовують дискову підсистему. Для впливу на пріоритет операцій вводу-виводу в Linux існує ionice.

Наприклад:

ionice -c 3 sleep 120

Така команда запускає процес із класом I/O idle. Однак фактичний ефект залежить від планувальника вводу-виводу, файлової системи, пристрою та налаштувань ядра.

Інший інструмент, taskset, дозволяє переглядати або змінювати CPU affinity, тобто набір логічних процесорів, на яких процесу дозволено виконуватися.

Наприклад, для перегляду поточної прив’язки оболонки:

taskset -cp $$

А для запуску команди на першому доступному логічному CPU можна використати:

taskset -c 0 sleep 120

При цьому CPU 0 має бути доступним у відповідному середовищі. Усередині контейнерів або систем із CPU-обмеженнями доступний набір логічних процесорів може відрізнятися.

CPU affinity може бути корисною для спеціалізованих навантажень, тестування продуктивності та окремих сценаріїв із низькими затримками. Але прив’язка процесу до певних ядер не гарантує, що інші процеси не використовуватимуть ці ядра.

OOM Killer: що відбувається, коли закінчується пам’ять

Оперативна пам’ять є обмеженим ресурсом. Коли процеси споживають занадто багато RAM, система може використовувати swap, механізми повернення пам’яті та інші способи звільнення ресурсів. Однак іноді цього недостатньо.

У таких ситуаціях ядро Linux може активувати OOM Killer (Out-of-Memory Killer), який вибирає процес для примусового завершення, щоб звільнити пам’ять і дозволити системі продовжити роботу.

Вибір процесу залежить від кількох чинників, включно з оцінкою використання пам’яті, відповідними налаштуваннями та областю, у якій виникла нестача ресурсу. Не завжди завершується саме процес із найбільшим обсягом RAM.

У Linux для аналізу використовуються значення oom_score та oom_score_adj. Перше відображає поточну оцінку, яку ядро може враховувати під час вибору жертви, а друге дозволяє коригувати цю оцінку.

Наприклад, для перевірки параметрів поточної оболонки:

cat /proc/$$/oom_score 
cat /proc/$$/oom_score_adj

Значення oom_score_adj може перебувати в діапазоні від -1000 до 1000. Нижчі значення зменшують імовірність вибору процесу, а -1000 зазвичай захищає його від відповідного механізму OOM-вибору ядра.

Проте змінювати ці параметри без розуміння наслідків небезпечно. Надмірний захист одного ресурсоємного процесу може призвести до завершення інших, важливіших компонентів системи.

Щоб перевірити, чи відбувалися OOM-події, можна переглянути повідомлення ядра:

sudo journalctl -k --no-pager | grep -Ei 'out of memory|oom-kill|killed process'

У журналах можуть з’являтися повідомлення про те, який процес було завершено та які обставини спричинили нестачу пам’яті.

Для DevOps це важливо під час розслідування раптових перезапусків застосунків. Якщо служба періодично зникає без зрозумілої помилки в її власних журналах, причиною може бути саме OOM.

Окремо варто враховувати cgroup OOM, який виникає, коли процеси перевищують ліміт пам’яті своєї контрольної групи. У контейнеризованих середовищах це може відбутися навіть тоді, коли на хостовому сервері залишається вільна пам’ять.

Також у деяких дистрибутивах працює systemd-oomd, який може завершувати процеси або контрольні групи відповідно до власної політики реагування на тиск пам’яті. Це окремий механізм від OOM Killer ядра.

ulimit і cgroups: як обмежувати ресурси процесів

У Linux можна не лише спостерігати за споживанням ресурсів, а й визначати межі, у яких працюватимуть окремі процеси та групи процесів.

Для цього використовуються різні механізми. Один із них – ресурсні ліміти процесів, які в командній оболонці можна переглядати та змінювати через ulimit.

Наприклад:

ulimit -a

Команда покаже поточні обмеження оболонки, включно з кількістю відкритих файлів, максимальною кількістю певних процесів або потоків, розміром стека та іншими параметрами.

Для перевірки ліміту відкритих файлових дескрипторів:

ulimit -n

Однак ці обмеження мають свою область дії. Зміна ulimit впливає на поточну оболонку та процеси, які успадковують її параметри. Вона не змінює автоматично ліміти всіх уже запущених застосунків.

Для постійних налаштувань інтерактивних сесій можуть використовуватися PAM і файли на кшталт /etc/security/limits.conf. Але цей механізм не завжди застосовується до служб, запущених через systemd.

Що таке cgroups

Control Groups, або cgroups, – це механізм ядра Linux, який дозволяє об’єднувати процеси та контролювати їхнє використання ресурсів.

На відміну від ulimit, який встановлює окремі ресурсні обмеження для процесів, cgroups дозволяють керувати ресурсами цілих груп завдань, включно з дочірніми процесами. Завдяки цьому можна обмежувати споживання оперативної пам’яті, процесорного часу, кількість завдань та окремі характеристики вводу-виводу.

Цей механізм широко використовується в контейнеризації. Коли для контейнера Docker встановлюється ліміт пам’яті або CPU, відповідні обмеження зазвичай реалізуються через cgroups. У сучасних дистрибутивах дедалі частіше використовується cgroup v2 з уніфікованою ієрархією ресурсів.

Переглянути дерево контрольних груп можна командою:

systemd-cgls

А для інтерактивного моніторингу їхнього використання ресурсів:

systemd-cgtop

Якщо потрібно визначити, до якої cgroup належить поточна оболонка:

cat /proc/self/cgroup

Для DevOps cgroups особливо важливі тим, що дозволяють контролювати ресурси не окремих PID, а логічних груп процесів. Якщо застосунок створить десять або сто дочірніх процесів, налаштований груповий ліміт продовжить охоплювати відповідні завдання.

systemd: керування системними службами

У більшості сучасних серверних дистрибутивів Linux для керування системними службами використовується systemd. Він відповідає за запуск, зупинення, перезапуск і контроль багатьох компонентів операційної системи.

На відміну від запуску звичайної програми через термінал, systemd дозволяє визначити, як служба повинна поводитися після помилки, від яких компонентів залежить її запуск і які ресурси їй доступні. Для керування службами використовується команда systemctl.

Наприклад, щоб перевірити стан SSH-служби в Ubuntu:

systemctl status ssh

На інших дистрибутивах назва unit може відрізнятися, наприклад sshd.service.

Для запуску служби:

sudo systemctl start ssh

Для зупинення:

sudo systemctl stop ssh

Для перезапуску:

sudo systemctl restart ssh

Перезапуск SSH на віддаленому сервері може вплинути на можливість підключення, тому перед такими операціями потрібно перевіряти конфігурацію та мати спосіб відновлення доступу.

Щоб налаштувати автоматичний запуск під час завантаження системи:

sudo systemctl enable ssh

А для перевірки відповідного налаштування:

systemctl is-enabled ssh

Важливо розрізняти enable і start. Перша команда налаштовує запуск відповідно до механізму активації unit, а друга запускає службу в поточний момент. Для виконання обох операцій одночасно існує systemctl enable --now.

Як systemd контролює процеси

Служби systemd описуються unit-файлами. Для звичайних сервісів вони часто мають розширення .service та містять параметри запуску, залежності, правила перезапуску й обмеження ресурсів.

Наприклад, навчальна служба може мати таку конфігурацію:

[Unit]
Description=Linux Training Service
After=network.target

[Service]
Type=simple
User=nobody
ExecStart=/usr/bin/sleep infinity
Restart=on-failure
RestartSec=5
NoNewPrivileges=yes
PrivateTmp=yes
MemoryMax=128M
TasksMax=50

[Install]
WantedBy=multi-user.target

Цей приклад демонструє основні параметри systemd і не призначений для безпосереднього розгортання виробничого застосунку.

ExecStart визначає команду запуску, User – обліковий запис, Restart – поведінку після завершення, а MemoryMax і TasksMax задають відповідні ресурсні обмеження.

Параметр NoNewPrivileges=yes забороняє процесу та його нащадкам набувати нових привілеїв через механізми виконання програм. PrivateTmp=yes створює ізольоване представлення тимчасових каталогів для служби.

Окремі механізми безпеки systemd дозволяють додатково обмежувати доступ до файлової системи, пристроїв, мережевих можливостей і системних викликів. Однак налаштовувати їх потрібно відповідно до реальних потреб програми, щоб випадково не порушити її роботу.

Для перегляду фактичних параметрів певної служби використовується:

systemctl show ssh.service

А для отримання інформації про конкретні обмеження:

systemctl show ssh.service -p MemoryMax -p TasksMax -p CPUQuota

Ці інструменти особливо корисні під час діагностики застосунків, які завершуються після досягнення ліміту пам’яті або не можуть створити необхідну кількість процесів.

Журнали служб через journalctl

Systemd також інтегрується із системним журналом, для перегляду якого використовується journalctl.

Щоб отримати записи конкретної служби:

sudo journalctl -u ssh.service

Для перегляду останніх 50 повідомлень:

sudo journalctl -u ssh.service -n 50 --no-pager

А для відстеження нових записів у реальному часі:

sudo journalctl -u ssh.service -f

Це зручно, коли потрібно з’ясувати причину помилки запуску, перевірити повідомлення застосунку або проаналізувати події перед його завершенням.

Для DevSecOps журнали служб також допомагають відстежувати незвичні перезапуски, помилки автентифікації та інші події, які можуть мати значення під час розслідування інцидентів.

Моніторинг процесів: ps, top, htop та інші інструменти

Під час роботи із сервером важливо вчасно помічати процеси, які використовують надто багато ресурсів, перебувають у незвичних станах або поводяться не так, як очікувалося.

Для базового моніторингу Linux пропонує кілька утиліт. Ми вже використовували ps, яка показує знімок стану процесів на момент виконання команди. Для спостереження за змінами в реальному часі зручніше використовувати top і htop.

Команда top відображає поточне навантаження на процесор, використання оперативної пам’яті, кількість процесів та інші характеристики.

top

За замовчуванням інформація періодично оновлюється, тому можна спостерігати за навантаженням у динаміці.

Для оновлення кожну секунду:

top -d 1

Щоб відстежувати конкретний процес:

top -p 1234

Усередині top доступні інтерактивні клавіші. Наприклад, P сортує процеси за використанням CPU, M – за пам’яттю, а q завершує програму.

Для більш зручного інтерактивного моніторингу часто використовують htop. Він надає кольорове представлення ресурсів, дозволяє сортувати й фільтрувати процеси, переглядати дерево та виконувати окремі дії без запам’ятовування великої кількості параметрів.

В Ubuntu та Debian його можна встановити так:

sudo apt install htop

Після встановлення:

htop

Однак навіть зручний моніторинг не завжди одразу пояснює причину проблеми. Наприклад, високий відсоток використання CPU може бути нормальним для обчислювального застосунку, а низький CPU при повільній роботі сервера іноді свідчить про очікування диска, мережі або блокувань.

Тому під час діагностики варто також використовувати free, vmstat, iostat, ss та інші інструменти, залежно від характеру проблеми.

Наприклад:

free -h

Покаже використання оперативної пам’яті та swap.

vmstat 1 5

Виведе кілька послідовних знімків статистики пам’яті, процесорного часу та операцій вводу-виводу.

Для перевірки мережевих сокетів, які очікують підключення:

sudo ss -lntp

Це дозволяє побачити TCP-порти, відповідні процеси та ідентифікатори, якщо поточний користувач має достатні права для отримання інформації.

Для DevSecOps такий перегляд може допомогти виявити несподівану службу, яка почала прослуховувати мережевий порт. Водночас сам факт відкритого порту не свідчить про атаку: необхідно перевіряти призначення процесу, конфігурацію та очікувану поведінку системи.

Практичний приклад: дослідження процесу Linux

Щоб закріпити матеріал, створимо просте фонове завдання та перевіримо його характеристики за допомогою стандартних інструментів Linux. Усі дії можна виконувати у звичайній командній оболонці без адміністративних привілеїв.

Спочатку запустимо програму sleep у фоновому режимі:

sleep 300 &

Bash повідомить номер фонового завдання та PID запущеного процесу. Щоб зберегти PID у змінній, виконаємо:

LAB_PID=$!

Тепер перевіримо інформацію про процес:

ps -o pid,ppid,user,stat,ni,comm -p "$LAB_PID"

Команда покаже PID, PPID, власника, стан, значення nice та коротку назву програми.

Далі переглянемо дані з /proc:

cat "/proc/$LAB_PID/status" | grep -E '^(Name|State|Pid|PPid|Uid|Threads):'

Ми побачимо характеристики процесу, сформовані ядром Linux.

Тепер перевіримо його пріоритет:

ps -o pid,ni,pri,comm -p "$LAB_PID"

І знизимо відносну пріоритетність:

renice -n 10 -p "$LAB_PID"

Після цього повторно переглянемо значення nice, щоб переконатися, що зміна застосована.

Наступний крок – перевірка сигналів. Спочатку призупинимо виконання процесу:

kill -STOP "$LAB_PID"

Перевіримо його стан:

ps -o pid,stat,comm -p "$LAB_PID"

Тепер у колонці STAT повинен відображатися стан зупинення T.

Відновимо виконання:

kill -CONT "$LAB_PID"

І знову перевіримо стан:

ps -o pid,stat,comm -p "$LAB_PID"

Оскільки sleep переважно очікує завершення заданого часу, після відновлення він зазвичай перебуватиме у стані S.

Нарешті, коректно завершимо процес:

kill -TERM "$LAB_PID"

У поточній оболонці можна отримати його статус завершення:

wait "$LAB_PID" 
echo $?

Якщо процес був завершений сигналом SIGTERM, Bash часто повідомить код 143, що відповідає 128 + 15. Однак поведінка та формат повідомлень залежать від оболонки й конкретних обставин завершення.

Цей приклад дозволяє побачити практично весь базовий життєвий цикл процесу: запуск, отримання PID, перевірку стану, зміну nice, призупинення, відновлення та завершення.

Саме ці механізми лежать в основі керування багатьма серверними застосунками. У реальному середовищі замість sleep можуть працювати вебсервери, агенти моніторингу, скрипти обробки даних і системні служби.

Шпаргалка: основні команди для роботи з процесами Linux

🔎 Перегляд та пошук процесів

  • ps aux – показати процеси з інформацією про ресурси.

  • ps -ef – переглянути процеси в альтернативному форматі.

  • pstree -p – побачити дерево процесів із PID.

  • pgrep -a nginx – знайти процеси за назвою.

  • pidof nginx – отримати PID процесів із відповідним ім’ям.

  • jobs -l – переглянути фонові завдання оболонки.

⚙️ Керування процесами та сигналами

  • kill -TERM PID – надіслати запит на завершення.

  • kill -KILL PID – примусово завершити процес.

  • kill -STOP PID – призупинити процес.

  • kill -CONT PID – відновити виконання.

  • kill -l – переглянути доступні сигнали.

  • pkill – надіслати сигнал процесам за умовою пошуку.

🧠 Пріоритети та планування

  • nice -n 10 command – запустити команду зі зниженою пріоритетністю.

  • renice -n 15 -p PID – змінити nice активного процесу.

  • chrt -p PID – перевірити політику планування.

  • ionice -p PID – переглянути I/O-пріоритет.

  • taskset -cp PID – перевірити CPU affinity.

📁 Дослідження процесів через /proc

  • cat /proc/PID/status – переглянути стан та характеристики процесу.

  • cat /proc/PID/limits – перевірити ресурсні обмеження.

  • ls -l /proc/PID/fd – переглянути відкриті дескриптори.

  • cat /proc/PID/io – отримати статистику вводу-виводу.

  • cat /proc/PID/cgroup – перевірити належність до cgroup.

📊 Моніторинг і системні служби

  • top – спостерігати за процесами в реальному часі.

  • htop – інтерактивний моніторинг.

  • free -h – переглянути використання RAM.

  • systemctl status – перевірити стан служби.

  • journalctl -u – переглянути журнали служби.

  • systemd-cgls – побачити дерево контрольних груп.

  • systemd-cgtop – відстежувати використання ресурсів cgroups.

  • ulimit -a – переглянути ресурсні ліміти оболонки.

Висновок

Процеси є основою роботи Linux, адже саме через них операційна система виконує програми, обробляє запити, керує службами та використовує апаратні ресурси. Розуміння їхнього життєвого циклу, станів і взаємодії з ядром дозволяє не лише ефективніше адмініструвати сервери, а й швидше знаходити причини несправностей та проблем із продуктивністю.

У четвертій частині серії ми розібрали створення та завершення процесів, їхні ідентифікатори, сигнали, файлову систему /proc, планувальник Linux та інструменти керування пріоритетами. Окрему увагу приділили механізмам OOM Killer, ресурсним лімітам, cgroups, системним службам systemd та практичному моніторингу процесів.

Для DevOps ці знання допомагають підтримувати стабільність серверної інфраструктури, правильно розподіляти ресурси й автоматизувати керування застосунками. Для DevSecOps вони необхідні під час аналізу підозрілих процесів, перевірки повноважень, дослідження інцидентів і контролю безпеки робочого середовища.

На цьому завершуємо серію «Linux для DevOps і DevSecOps» із чотирьох частин. Ми пройшли шлях від файлової системи та базових команд до керування користувачами, правами доступу й процесами. Це фундамент, на якому будується подальша робота з Linux-серверами, контейнеризацією, автоматизацією інфраструктури та практичною кібербезпекою.

Підписатися
Сповістити про
0 Коментарі
Найстаріші
Найновіше Найбільше голосів
Знайшли помилку?
Якщо ви знайшли помилку, зробіть скріншот і надішліть його боту.
↑