Linux. Управление процессами
Управление процессами в Linux — это довольно обширная тема. Но все сводится к одному, что мы различными способами меняем характеристики процессов. При этом мы можем преследовать совершенно разные цели — завершить или приостановить процесс, поменять ему приоритет, заставить работать в фоне и другое.
Сигналы
Сигналы нужны для оповещения процессов о разнообразных событиях в системе. Это могут быть события оборудования или события других процессов. Если процесс получает сигнал, то он прерывает свою обычную работу, обрабатывает сигнал, а затем продолжает работать.
Сигналы от ядра поступают процессам напрямую. А сигналы от одних процессов другим поступают через ядро, то есть с помощью системных вызовов. Системный вызов, который обрабатывает сигналы называется kill()
. Его так назвали, так как большинство стандартных сигналов завершают (убивают) процесс.
Список всех сигналов можно посмотреть с помощью команды
$ kill -l 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP 21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR 31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3 38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8 43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7 58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2 63) SIGRTMAX-1 64) SIGRTMAX
Давайте рассмотрим более подробно некоторые из них
SIGHUP (1)
— разрыв с управляющим терминалом. Процесс либо что-то предпримет (если программист об этом позаботился), либо завершится. Например, мы работотаем с сервером, подключившись к нему по ssh. И вдруг связь пропадает, ssh сессия обрывается. Все наши процессы неожиданно теряют управляющий терминал и начинают завершаться, так как получают от ядра этот сигнал.SIGINT (2)
— клавиатурный сигнал, срабатывает когда мы нажимаемCtrl+c
. Это штатное завершение, то есть процесс будет завершён корректно (если процесс вообще умеет завершаться корректно).SIGQUIT (3)
— клавиатурный сигнал, срабатывает когда мы нажимаемCtrl+\
. Аварийное завершение с выдачей отладочной информации.SIGABRT (6)
— это аналогSIGQUIT (3)
. Если у процесса нет управляющего терминала, то отправить ему клавиатурный сигнал не получится, поэтому используется этот сигнал.SIGKILL (9)
— этот сигнал сразу завершает процесс (некорректно). И это поведение нельзя изменить, то есть программист не может сам указать программе, что делать в случае получения этого сигнала.SIGTERM (15)
— это аналогSIGINT (2)
. Если у процесса нет управляющего терминала, то отправить ему клавиатурный сигнал не получится, поэтому используется этот сигнал.SIGTSTP (20)
— клавиатурный сигнал, когда мы нажимаемCtrl+z
. Этот сигнал приостанавливает процесс на управляющем терминале и переводит процесс на задний фон. То есть процесс переходит в состояниеT
(stopped by job control signal — остановленный специальным сигналом).
Команда kill
Команда позволяет отправлять сигналы процессам. Тип сигнала указывается как опция в виде номера (например, -9
) или имени (например, -SIGKILL
). В качестве аргумента нужно указать PID
процесса, которому мы отправляем сигнал. Если тип сигнала не указан, то будет отправлен сигнал SIGTERM
.
$ kill -9 9898 $ kill -SIGKILL 2565
Так можно отправить сигнал своему процессу, но если нужно отправить сигнал чужому — используем команду sudo
.
Команда pgrep
Команда позволяет находить идентификатор процесса на основе заданных критериев. Это может быть полное или частичное имя процесса, имя пользователя, запустившего процесс или другие критерии.
$ pgrep --help Использование: pgrep [параметры] <шаблон> Параметры: -d, --delimiter <строка> указать разделитель вывода -l, --list-name выводить PID и имена процессов -a, --list-full выводить PID и полную командную строку -v, --inverse negates the matching -w, --lightweight выводить все TID -c, --count вывод количества соответствующих шаблону процессов -f, --full use full process name to match -g, --pgroup <PGID,...> match listed process group IDs -G, --group <GID,...> match real group IDs -i, --ignore-case match case insensitively -n, --newest select most recently started -o, --oldest select least recently started -P, --parent <PPID,...> match only child processes of the given parent -s, --session <SID,...> match session IDs -t, --terminal <tty,...> match by controlling terminal -u, --euid <ID,...> match by effective IDs -U, --uid <ID,...> match by real IDs -x, --exact match exactly with the command name -F, --pidfile <файл> читать PIDы из файла -L, --logpidfile fail if PID file is not locked -r, --runstates <state> match runstates [D,S,Z,...] --ns <PID> match the processes that belong to the same namespace as <pid> --nslist <ns,...> list which namespaces will be considered for the --ns option. Available namespaces: ipc, mnt, net, pid, user, uts -h, --help вывести эту справку и выйти -V, --version вывести информацию и выйти
Шаблон имени задается с использованием расширенных регулярных выражений. Когда команда вызывается без опций — будут показаны PID
всех запущенных процессов, которые соответствуют указанному имени.
$ pgrep ssh 1039 2257 6850
Команда возвращает ноль, когда хотя бы один запущенный процесс соответствует запрошенному имени. В противном случае код выхода — единица. Это может быть полезно при использовании в сценариях оболочки.
Опция -d
позволяет указать другой разделитель вместо символа новой строки
$ pgrep -d' ' ssh 1039 2257 6850
Команда pkill
Команда позволяет отправлять управляющий сигнал процессу на основе заданных критериев. Это может быть полное или частичное имя процесса, имя пользователя, запустившего процесс или другие критерии.
$ pkill --help Использование: pkill [параметры] <шаблон> Параметры: -<sig>, --signal <sig> сигнал для отправки (число или имя) -q, --queue <value> integer value to be sent with the signal -e, --echo display what is killed -c, --count вывод количества соответствующих шаблону процессов -f, --full use full process name to match -g, --pgroup <PGID,...> match listed process group IDs -G, --group <GID,...> match real group IDs -i, --ignore-case match case insensitively -n, --newest select most recently started -o, --oldest select least recently started -O, --older <seconds> select where older than seconds -P, --parent <PPID,...> match only child processes of the given parent -s, --session <SID,...> match session IDs -t, --terminal <tty,...> match by controlling terminal -u, --euid <ID,...> match by effective IDs -U, --uid <ID,...> match by real IDs -x, --exact match exactly with the command name -F, --pidfile <файл> читать PIDы из файла -L, --logpidfile fail if PID file is not locked -r, --runstates <state> match runstates [D,S,Z,...] --ns <PID> match the processes that belong to the same namespace as <pid> --nslist <ns,...> list which namespaces will be considered for the --ns option. Available namespaces: ipc, mnt, net, pid, user, uts -h, --help вывести эту справку и выйти -V, --version вывести информацию и выйти
Шаблон имени задается с использованием расширенных регулярных выражений. Тип сигнала указывается как опция в виде номера (например, -9
) или имени (например, -SIGKILL
). Если тип сигнала не указан, то будет отправлен сигнал SIGTERM
.
Чтобы перезапустить процессы веб-сервера Nginx, выполянем команду
$ pkill -SIGHUP nginx
Перед использованием команды pkill
— полезно запустить команду pgrep
для проверки
$ pgrep ssh 1039 sshd 2257 ssh-agent 6850 ssh
Если шаблон затрагивает процессы, которым не нужно отправлять сигнал — требуется уточнение
$ pkill '^ssh$'
Здесь символ ^
соответствует началу строки, а символ $
— концу строки. Так что в итоге будет выбран процесс ssh
и не затронуты sshd
и ssh-agent
.
По умолчанию шаблон сопоставляется только с именем процесса. При использовании опции -f
— шаблон сопоставляется с полными списком аргументов.
$ pkill -9 -f "ping 8.8.8.8"
Опция -u
предписывает отправить сигнал процессам, запущенным указанным пользователем.
$ pkill -u evgeniy
Можно комбинировать опции и шаблон поиска. Команда ниже отправит сигнал SIGKILL
всем процессам, запущенным пользователем evgeniy
и соответствующим шаблону gnome
.
$ pkill -SIGKILL -u evgeniy gnome
Команда killall
Команда killall
очень похожа на pkill
, но принимает в качестве аргумента имя процесса, а не шаблон имени. То есть, если вместо «firefox» указать «firef», то pkill
закроет все процессы браузера Firefox, а killall
не закроет ни одного процесса.
$ killall --help Использование: killall [ПАРАМЕТР]… [--] ИМЯ… killall -l, --list killall -V, --version -e,--exact требовать полного совпадения для очень длинных имён -I,--ignore-case игнорировать регистр символов в именах процессов -g,--process-group завершать группу процесса вместо одного процесса -y,--younger-than завершить процессы, новее чем заданного ВРЕМЕНИ -o,--older-than завершить процессы, старее чем заданного ВРЕМЕНИ -i,--interactive запрашивать подтверждение перед завершением процессов -l,--list вывести список допустимых имён сигналов -q,--quiet не показывать подробные сообщения -r,--regexp рассматривать ИМЯ как расширенное регулярное выражение -s,--signal СИГНАЛ посылать указанный СИГНАЛ, а не SIGTERM -u,--user ПОЛЬЗВ завершить процесс(ы), запущенный только ПОЛЬЗОВАТЕЛЕМ -v,--verbose уведомлять только при успешной отправке сигнала -V,--version показать информацию о версии -w,--wait ожидать завершения процессов -n,--ns PID все процессы, принадлежащие тем же пространствам имён что и PID -Z,--context REGEXP прибивать только процессы, имеющие контекст (настройка должна предваряться другими аргументами)
Работа в фоне
Управление процессами можно применять и для того, чтобы в терминале можно было запускать процессы в фоновом режиме. Представьте, что при чтении справки man
потребовалось выполнить пару команд. Чтобы не закрывать справку, можно нажать Ctrl+z
и приостановить man
. При этом работа man
не просто приостанавливается, она уходит на задний фон.
$ man killall тут нажатие Ctrl+z [1]+ Остановлен man killall
Теперь можно выполнить несколько команд в терминале. Давайте посмотрим, какие процессы выполняются в фоне.
$ jobs [1]+ Остановлен man killall
Мы видим только одно фоновое задание с номером 1. Знак плюс означает, что это текущее фоновое задание. Перевести фоновое задание на передний план можно с помощью команды fg номер
. Если номер не указан — на передний план будет выведено текущее фоновое задание.
$ fg
И мы опять вернулись к чтению справки man
.
Процесс на заднем фоне может быть не только в приостановленном состоянии. Если выполнение процесса не требует от нас каких-то интерактивных действий (никакого ввода или вывода), то можно заставить процесс выполнятся на заднем фоне. Для этого нужно выполнить команду bg номер
.
Для примера, начнем скачивание большого файла с помощью wget
$ wget https://releases.ubuntu.com/22.04.1/ubuntu-22.04.1-desktop-amd64.iso
Затем переведём процесс скачивания на задний фон с помощью Ctrl+z
$ jobs [1]+ Остановлен wget https://releases.ubuntu.com/22.04.1/ubuntu-22.04.1-desktop-amd64.iso
Теперь, с помощью команды bg
, запустим процесс на заднем фоне
$ bg 1 [1]+ wget https://releases.ubuntu.com/22.04.1/ubuntu-22.04.1-desktop-amd64.iso & Вывод перенаправляется в wget-log
Утилита wget
должна выводить информацию о скачивании файла, но на заднем фоне запрещено выводить информацию на терминал. Именно поэтому весь вывод автоматически перенаправляется в файл wget-log
.
Проверим, что процесс скачивания теперь выполняется в фоновом режиме
$ jobs [1]+ Запущен wget https://releases.ubuntu.com/22.04.1/ubuntu-22.04.1-desktop-amd64.iso &
Запустить процесс на заднем фоне можно только, если он не интерактивный. Например запустить остановленный man
не получится. Как только man
попытается вывести на терминал справку, то сразу опять будет остановлен.
Приоритеты процессов
В работающей системе выполняется множество процессов. И каждому запущенному процессу назначается приоритет. Как ни странно, но чем больше числовое значение приоритета, тем меньше сам приоритет. То есть, процесс с числовым значением приоритета 10 будет более важным с точки зрения системы, чем процесс с числовым значением приоритета 20.
Пользователи не могут управлять приоритетами процессов, даже суперпользователь root
. Но могут управлять другим значением — nice
(вежливость, любезность), который варьируется от -20 до +19. По умолчанию, процесс созданный пользователем, имеет значение nice
равное нулю, и приоритет равный 20.
$ ps -u evgeniy -o user,pid,nice,priority,cmd USER PID NI PRI CMD evgeniy 711 0 20 /usr/bin/python3 -m http.server 8081 --directory=/ho... evgeniy 1224 0 20 /lib/systemd/systemd --user evgeniy 1225 0 20 (sd-pam) evgeniy 1243 -11 9 /usr/bin/pipewire evgeniy 1244 0 20 /usr/bin/pipewire-media-session evgeniy 1245 -11 9 /usr/bin/pulseaudio --daemonize=no --log-target=journal evgeniy 1253 0 20 /usr/bin/gnome-keyring-daemon --daemonize --login
Разница в том, что priority
— это реальный приоритет процесса в данный момент, а nice
— подсказка для ядра, указывающая нужно ли увеличить или уменьшить приоритет процессу.
Кроме команлы ps
, для просмотра приоритетов процессов можно использовать команды top
и htop
.
Команда nice
Команда настраивает приоритет процесса перед его запуском. Например, запустим две программы с разным приоритетом. Чтобы запустить их две в одном терминал — запустим выполнение на заднем фоне.
$ nice -15 md5sum /dev/urandom Ctrl+z $ bg
$ nice -5 md5sum /dev/urandom Ctrl+z $ bg
$ jobs [1]- Запущен nice -15 md5sum /dev/urandom & [2]+ Запущен nice -5 md5sum /dev/urandom &
Теперь, с помощью ps
, посмотрим на приоритеты процессов
$ ps -C md5sum -o user,pid,nice,priority,cmd,pcpu USER PID NI PRI CMD %CPU evgeniy 28958 15 35 md5sum /dev/urandom 10.0 evgeniy 28970 5 25 md5sum /dev/urandom 60.8
Как видите, команды md5sum
работают параллельно — но у одной команды приоритет стал 25, а у другой 35.
Теперь запустим еще один экземпляр md5sum
, но уменьшим nice
, тем самым увеличив приоритет. Делать это надо с помощью sudo
, так как обычный пользователь не имеет право запускать процессы с повышенным приоритетом.
$ sudo nice --5 md5sum /dev/urandom Ctrl+z $ bg
$ jobs [1] Запущен nice -15 md5sum /dev/urandom & [2]- Запущен nice -5 md5sum /dev/urandom & [3]+ Запущен sudo nice --5 md5sum /dev/urandom &
Теперь, с помощью ps
, посмотрим на приоритеты процессов
$ ps -C md5sum -o user,pid,nice,priority,cmd,pcpu USER PID NI PRI CMD %CPU evgeniy 28958 15 35 md5sum /dev/urandom 6.0 evgeniy 28970 5 25 md5sum /dev/urandom 34.1 root 28984 -5 15 md5sum /dev/urandom 40.2
Последний запущенный процесс сильнее всего нагружает процессор.
Команда renice
Команда позволяет изменить приоритет уже работающего процесса. Этой командой может пользоваться только пользователь root
. Вернем все процессам nice
по умолчанию, то есть ноль.
$ sudo renice -n 0 -p 28958 28958 (process ID) старый приоритет 15, новый приоритет 0 $ sudo renice -n 0 -p 28970 28970 (process ID) старый приоритет 5, новый приоритет 0 $ sudo renice -n 0 -p 28984 28984 (process ID) старый приоритет -5, новый приоритет 0
$ ps -C md5sum -o user,pid,nice,priority,cmd,pcpu USER PID NI PRI CMD %CPU evgeniy 28958 0 20 md5sum /dev/urandom 13.0 evgeniy 28970 0 20 md5sum /dev/urandom 32.4 root 28984 0 20 md5sum /dev/urandom 47.6
Поиск: CLI • Linux • Команда • Сигнал • Процесс • pgrep • pkill • nice