Процессы и сигналы в Linux

Процесс — выполняющийся экземпляр программы. У него есть идентификатор PID, родительский процесс PPID, владелец, состояние, приоритет, открытые файлы и лимиты ресурсов. Ядро Linux планирует выполнение процессов и распределяет между ними ресурсы.

echo $$    # PID текущего shell
echo $!    # PID последней фоновой команды
echo $?    # код завершения последней команды

PID, PPID и дерево процессов

PID (Process ID) — идентификатор процесса в работающей системе. После завершения процесса его PID позднее может быть использован повторно.

PPID (Parent Process ID) — PID процесса, создавшего текущий процесс.

ps -o pid,ppid,user,stat,comm -p 1234

На большинстве современных систем процесс с PID 1 — менеджер инициализации, обычно systemd. Он запускает службы и принимает осиротевшие процессы.

Показать дерево процессов:

ps -ef --forest
ps axjf
pstree -p
pstree -ps 1234    # цепочка предков процесса

pstree может устанавливаться отдельным пакетом, часто psmisc.

Состояния процессов

Код Состояние
R выполняется или готов к выполнению
S прерываемый сон, ожидание события
D непрерываемое ожидание, часто ввод-вывод
T остановлен сигналом или отладчиком
Z zombie: завершён, но родитель ещё не получил статус
I неактивный поток ядра

В STAT встречаются дополнительные признаки: N — пониженный приоритет, < — высокий приоритет, s — лидер сеанса, l — многопоточный процесс, + — участник foreground-группы терминала.

Zombie-процесс уже не выполняет код. Посылать ему SIGKILL бессмысленно: родитель должен прочитать статус завершения дочернего процесса.


Просмотр процессов: ps

ps показывает снимок процессов на момент запуска.

ps              # процессы текущего терминала
ps aux          # все процессы, BSD-формат
ps -ef          # все процессы, UNIX-формат

Настраиваемый вывод:

ps -eo pid,ppid,user,%cpu,%mem,stat,etime,comm,args
Поле Значение
%CPU использование процессора
%MEM доля оперативной памяти
RSS резидентная память в KiB
VSZ виртуальная память в KiB
STAT состояние процесса
ETIME время с момента запуска
COMM краткое имя команды
ARGS команда с аргументами

Сортировка по CPU и памяти:

ps -eo pid,user,%cpu,%mem,comm --sort=-%cpu | head
ps -eo pid,user,%mem,rss,comm --sort=-%mem | head

Поиск процессов:

ps aux | grep '[n]ginx'
pgrep nginx
pgrep -a nginx
pgrep -u username -a python

Для сценариев pgrep обычно надёжнее, чем разбор ps | grep. Полная командная строка может содержать пароли или токены, поэтому её нельзя бездумно записывать в журналы.

Просмотр потоков:

ps -eLf
ps -L -p 1234 -o pid,tid,psr,stat,comm

Интерактивный просмотр: top и htop

top

top
top -p 1234    # один процесс
top -d 2       # обновление каждые 2 секунды

Полезные клавиши внутри top:

Клавиша Действие
P сортировка по CPU
M сортировка по памяти
T сортировка по процессорному времени
1 статистика отдельно по логическим CPU
H показать потоки
k отправить сигнал
r изменить nice
u фильтр по пользователю
c полная командная строка
q выход

Load average — среднее число выполняющихся или ожидающих ресурсы задач за 1, 5 и 15 минут. Значения нужно сопоставлять с количеством CPU и характером нагрузки.

Поля CPU: us — пользовательский код, sy — ядро, ni — задачи с изменённым nice, id — простой, wa — ожидание ввода-вывода, st — время, отнятое гипервизором.

htop

htop — интерактивная альтернатива с удобной навигацией:

htop

Полезные клавиши: F3 — поиск, F4 — фильтр, F5 — дерево, F6 — сортировка, F9 — сигнал, F10 — выход. Программа может устанавливаться отдельным пакетом.


jobs: задания текущего shell

Задание (job) — процесс или пайплайн, запущенный интерактивным shell. Номер задания %1 не равен PID.

jobs
jobs -l    # вместе с PID

jobs показывает только задания текущего shell, а не все процессы системы.

Foreground и background

Foreground-процесс работает на переднем плане, получает ввод с терминала и занимает приглашение shell:

long_command

Запуск в фоне:

long_command &

Временно остановить foreground-задание можно через Ctrl+Z. Затем:

bg %1      # продолжить в фоне
fg %1      # вернуть на передний план

Остановить и продолжить задание сигналами:

kill -STOP %1
kill -CONT %1

Пайплайн обычно образует группу процессов. Сигналы терминала, например Ctrl+C, отправляются foreground-группе, поэтому могут затронуть весь пайплайн.


nohup и работа после закрытия терминала

nohup запускает команду с игнорированием SIGHUP:

nohup long_command >app.log 2>&1 &
echo $!

Без явного перенаправления вывод обычно попадает в nohup.out.

В Bash уже запущенное задание можно удалить из таблицы заданий:

long_command &
disown %1

nohup не перезапускает программу после сбоя и не делает её полноценной службой. Для постоянных сервисов лучше использовать systemd, а для длительной интерактивной работы — tmux или screen.


Сигналы

Сигнал — асинхронное уведомление процессу. Он может запросить завершение, остановку, продолжение или перечитывание конфигурации.

kill -l          # список сигналов
kill -l TERM     # номер сигнала
Сигнал Типичный номер Назначение
SIGHUP 1 разрыв терминала; часто перечитывание конфигурации
SIGINT 2 прерывание, обычно Ctrl+C
SIGQUIT 3 завершение, возможно с core dump
SIGKILL 9 безусловное завершение
SIGTERM 15 корректный запрос на завершение
SIGCONT 18 продолжить процесс
SIGSTOP 19 безусловно остановить процесс
SIGTSTP 20 интерактивная остановка, обычно Ctrl+Z

Номера части сигналов зависят от архитектуры, поэтому в командах понятнее использовать имена.

SIGTERM

kill -TERM 1234
kill 1234         # TERM по умолчанию

Процесс может обработать SIGTERM: завершить операции, закрыть файлы и сохранить состояние. Это основной способ запросить корректное завершение.

SIGKILL

kill -KILL 1234
kill -9 1234

SIGKILL нельзя перехватить или игнорировать. Процесс не успевает очистить ресурсы, поэтому возможны потеря данных и незавершённые транзакции. Использовать только после неудачного SIGTERM.

Если процесс находится в состоянии D, даже SIGKILL обычно будет обработан только после завершения непрерываемой операции ввода-вывода.

SIGHUP

kill -HUP 1234

Многие демоны используют SIGHUP для перечитывания конфигурации, но это не универсальное правило. Нужно проверить документацию программы.

Сигнал 0 ничего не отправляет, но проверяет существование процесса и права:

kill -0 1234

kill, killall и pkill

kill работает с PID или номером задания:

kill -TERM 1234
kill -TERM 1234 5678
kill -TERM %1

Обычный пользователь может сигнализировать своим процессам. Для чужих нужны дополнительные права.

killall выбирает процессы по имени:

killall -TERM nginx
killall -u username process_name

Команда способна затронуть несколько процессов. Её поведение различается между ОС; здесь имеется в виду распространённая Linux-версия.

pkill использует правила поиска, аналогичные pgrep:

pkill -TERM nginx
pkill -HUP -u username process_name

Поиск по полной командной строке:

pgrep -af 'python.*worker.py'
pkill -TERM -f 'python.*worker.py'

Перед killall или pkill -f нужно проверить совпадения через pgrep -a или pgrep -af, иначе можно завершить лишние процессы.

Безопасная последовательность:

pgrep -a process_name
kill -TERM PID
ps -p PID -o pid,stat,etime,cmd
# Только при необходимости:
kill -KILL PID

Для службы предпочтительнее её штатный менеджер:

sudo systemctl stop service-name
sudo systemctl reload service-name

Приоритеты: nice и renice

Nice-значение обычно находится в диапазоне от -20 до 19:

-20 — более высокий приоритет
  0 — стандартное значение
 19 — более низкий приоритет

Чем больше значение, тем процесс «вежливее» к другим. Nice влияет на относительный приоритет CPU, но не задаёт жёсткую долю процессорного времени и не ограничивает память.

Запустить команду с nice 10:

nice -n 10 long_command
nice -n 15 long_command >app.log 2>&1 &

Обычный пользователь может понижать приоритет своих процессов. Для повышения приоритета обычно нужны привилегии:

sudo nice -n -5 important_command

Изменить nice работающего процесса:

renice -n 10 -p 1234
sudo renice -n 10 -u username

Проверить:

ps -o pid,ni,pri,stat,comm -p 1234

Для настоящего ограничения CPU и памяти применяют cgroups и параметры systemd.


Файловая система /proc

/proc — виртуальная файловая система, создаваемая ядром в памяти. Каталог /proc/PID содержит сведения о процессе.

Путь Содержимое
/proc/PID/status текстовая сводка
/proc/PID/cmdline аргументы, разделённые нулевыми байтами
/proc/PID/environ окружение, разделённое нулевыми байтами
/proc/PID/exe ссылка на исполняемый файл
/proc/PID/cwd рабочий каталог
/proc/PID/fd/ открытые файловые дескрипторы
/proc/PID/maps отображения виртуальной памяти
/proc/PID/limits лимиты ресурсов
/proc/PID/task/ потоки процесса
cat /proc/1234/status
readlink -f /proc/1234/exe
readlink -f /proc/1234/cwd
ls -l /proc/1234/fd
cat /proc/1234/limits
tr '\0' ' ' </proc/1234/cmdline; echo

Окружение можно вывести командой:

tr '\0' '\n' </proc/1234/environ

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

Общесистемные файлы:

Путь Содержимое
/proc/cpuinfo сведения о CPU
/proc/meminfo статистика памяти
/proc/loadavg нагрузка и число задач
/proc/uptime время работы системы
/proc/mounts точки монтирования
/proc/sys/ параметры ядра

Параметры /proc/sys удобнее читать через sysctl:

sysctl kernel.pid_max

Доступ к данным чужих процессов может ограничиваться правами, параметром hidepid, модулями безопасности и пространствами имён контейнеров.


Ограничение ресурсов: ulimit

ulimit — встроенная команда shell. Она изменяет лимиты текущего shell и процессов, которые он запустит позднее.

ulimit -a     # все лимиты
ulimit -Sa    # soft limits
ulimit -Ha    # hard limits

Soft limit — текущий рабочий предел. Hard limit — верхняя граница для soft limit. Обычный пользователь может менять soft limit в пределах hard limit; повышение hard limit требует привилегий.

Частые опции Bash:

Опция Ресурс
-n открытые файловые дескрипторы
-u пользовательские процессы или задачи
-c размер core dump
-f размер создаваемого файла
-s размер стека
-t процессорное время
-v виртуальная память
-l блокируемая память
ulimit -n          # текущий лимит файлов
ulimit -Sn 4096    # новый soft limit
ulimit -c 0        # запрет core dump

Запуск программы с лимитом без изменения родительского shell:

(
    ulimit -n 1024
    exec application
)

Core dump может содержать секретные данные из памяти процесса, поэтому требует строгого контроля доступа.

Постоянные пользовательские лимиты в PAM-окружении часто задают в /etc/security/limits.conf и /etc/security/limits.d/*.conf:

username soft nofile 4096
username hard nofile 8192

Для служб systemd применяют параметры unit-файла:

[Service]
LimitNOFILE=8192

После изменения unit-файла:

sudo systemctl daemon-reload
sudo systemctl restart service-name

ulimit не заменяет cgroups. Для служб лимиты CPU, памяти и задач обычно задают параметрами CPUQuota=, MemoryMax= и TasksMax=.


Практические сценарии

Найти процессы пользователя:

ps -u username -o pid,ppid,stat,%cpu,%mem,etime,comm
pgrep -u username -a

Процессы с наибольшим потреблением ресурсов:

ps -eo pid,user,%mem,rss,comm --sort=-rss | head -n 11
ps -eo pid,user,%cpu,etime,comm --sort=-%cpu | head -n 11

Запустить длительную команду и наблюдать за журналом:

nohup command >command.log 2>&1 &
pid=$!
echo "PID: $pid"
tail -F command.log

Узнать открытые файлы и рабочий каталог:

ls -l /proc/1234/fd
readlink -f /proc/1234/cwd
readlink -f /proc/1234/exe

Если установлена lsof:

lsof -p 1234

Диагностика процесса в состоянии D:

ps -o pid,ppid,stat,wchan:32,comm -p 1234
sudo cat /proc/1234/stack

Такое состояние часто связано с диском, сетевой файловой системой, устройством или драйвером. Принудительное завершение обычно не устраняет причину ожидания.


Типичные ошибки

Сразу использовать kill -9

Сначала применяйте штатную остановку или SIGTERM. SIGKILL оставляйте на крайний случай.

Использовать слишком широкий pkill -f

Сначала проверяйте тот же шаблон через pgrep -af.

Искать все процессы через jobs

jobs видит только задания текущего shell. Для системы нужны ps, pgrep, top или htop.

Полагаться на простой command &

Фоновый процесс не обязательно переживёт закрытие сеанса. Используйте nohup, tmux, планировщик либо службу systemd.

Считать nice жёстким лимитом

Nice задаёт относительный приоритет CPU. Для реальных ограничений применяйте cgroups.

Менять ulimit не в том окружении

Лимиты наследуются дочерними процессами. Изменение в одном терминале не повлияет на уже запущенную службу или другой сеанс.


Краткая памятка

Задача Команда
Все процессы ps aux или ps -ef
Выбранные поля ps -eo pid,ppid,user,stat,%cpu,%mem,comm
Найти процесс pgrep -a name
Дерево процессов ps -ef --forest или pstree -p
Мониторинг top или htop
Задания shell jobs -l
Запустить в фоне command &
Остановить временно Ctrl+Z
Продолжить в фоне bg %1
Вернуть на передний план fg %1
Пережить закрытие терминала nohup command >log 2>&1 &
Корректно завершить kill -TERM PID
Принудительно завершить kill -KILL PID
Перечитать конфигурацию kill -HUP PID
Список сигналов kill -l
Запустить с nice nice -n 10 command
Изменить nice renice -n 10 -p PID
Сведения о процессе cat /proc/PID/status
Лимиты shell ulimit -a
Лимиты процесса cat /proc/PID/limits

Рекомендации по безопасной работе

Перед отправкой сигнала проверяйте PID, владельца и полную команду процесса. Службами управляйте через systemctl, заданиями shell — через jobs, fg и bg.

Сначала используйте штатный механизм приложения или SIGTERM. Применяйте SIGKILL, только если корректное завершение не сработало и последствия понятны.

Не публикуйте содержимое /proc/PID/environ и полные командные строки: они могут содержать секреты. Для постоянных процессов используйте менеджер служб; nohup не заменяет мониторинг, автоматический перезапуск и управление журналами.