В статье я покажу вам как осуществляется управление процессами в Linux. Вы узнаете про сигналы, передний и задний фон, и приоритеты процессов.
Сигналы
Управление процессами в Linux — это довольно обширная темя. Но все сводится к одному, мы различными способами меняем характеристики процессов. При этом мы можем преследовать совершенно разные цели: например мы можем хотеть завершить или приостановить процесс, поменять ему приоритет, заставить работать в фоне и другое.
Управление процессами может происходить по разному. Начнём наше обучение с сигналов, которые мы можем отправлять процессам.
Сигналы нужны для асинхронного оповещения процессов о разнообразных событиях в системе. Это могут быть события оборудования или события других процессов. Работа сигналов очень похожа на прерывания. То есть, если процесс получает сигнал, то он прерывает свою обычную работу, обрабатывает сигнал, а затем продолжает работать.
Сигналы от ядра поступают процессам напрямую. А сигналы от одних процессов другим поступают через ядро, то есть с помощью системных вызовов. Системный вызов, который обрабатывает сигналы называется — kill(). Его так назвали, так как большинство стандартных сигналов завершают (убивают) процесс.
Посмотреть обзор на сигналы можно выполнив man 7 signal
. Или вы можете почитать эту же документацию здесь.
Стандартные сигналы
В этой документации вы можете посмотреть список стандартных сигналов:
Разберём некоторые из низ:
- 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
Существует специальная команда — kill, она позволяет отправлять сигналы процессам. Тип сигнала указывается в качестве параметра в виде номера (например, -9) или имени (например, -SIGKILL). Следующим параметром нужно указать PID процесса, которому мы отправляем сигнал. Вот примеры:
$ kill -9 9898 $ kill -SIGKILL 2565
Вы можете отправить сигнал своему процессу, а если хотите отправить сигнал чужому, то нужно использовать sudo, или запускать команду kill из под пользователя root.
Передний и задний фон
Когда вы работаете в графической системе, то вы можете одновременно работать в нескольких программах. Для этого используются окна приложений. Пока вы работаете с одной программой, её окошко работает на переднем фоне. А в это время другие окна могут оставаться на заднем фоне.
Управление процессами в Linux можно применять и для того, чтобы в терминале можно было работать также, на заднем и переднем фоне. Представьте, что вы читаете справку (man) и захотели что-то попробовать. Чтобы не закрывать man, вы можете нажать Ctrl+z и приостановить man. При этом работа man не просто приостанавливается, она уходит на задний фон.
alex@deb-11:~$ man 7 signal (тут я нажал Ctrl+z) [1]+ Остановлен man 7 signal
Дальше вы можете поработать в терминале. А затем, когда захотите, вернёте man на передний фон. А чтобы увидеть процессы на заднем фоне используется команда jobs:
alex@deb-11:~$ jobs [1]+ Остановлен man 7 signal
Разберём вывод: [1] — номер задания, + означает что это последнее приостановленное задание, а дальше идёт состояние и название процесса.
И наконец, чтобы вернуть задание на передний фон, нужно выполнить fg %<номер задания>. Или можно выполнить fg без указания номера задания, тогда на передний план вернётся задание, которое было помечено плюсиком в выводе jobs.
alex@deb-11:~$ fg %1
После выполнения последней команды, на передний план вернется работа man.
Группа переднего фона — это терминал и то что на нём сейчас выполняется. Все остальные группы — это группы заднего фона.
Работа процесса на заднем фоне
Выше я показал как остановить процесс и поместить его на задний фон. Но на заднем фоне процесс может быть не только в приостановленном состоянии. Если выполнение процесса не требует от вас каких-то интерактивных действий (никакого ввода или вывода), то можно заставить процесс выполнятся на заднем фоне. Для этого нужно выполнить bg %<номер задания>.
Например, начнём скачивание большого файла с помощью команды wget:
alex@deb-11:~$ wget https://releases.ubuntu.com/22.04.1/ubuntu-22.04.1-desktop-amd64.iso
Затем переведём процесс скачивания на задний фон, то-есть нажмём Ctrl+z. И посмотрим список заданий на заднем фоне:
alex@deb-11:~$ jobs [1]- Остановлен man 7 signal [2]+ Остановлен wget https://releases.ubuntu.com/22.04.1/ubuntu-22.04.1-desktop-amd64.iso
Теперь, с помощью команды bg, запустим процесс на заднем фоне:
alex@deb-11:~$ bg %2 [2]+ wget https://releases.ubuntu.com/22.04.1/ubuntu-22.04.1-desktop-amd64.iso & Вывод перенаправляется в «wget-log».
Wget должен выводить информацию о скачивании файла, но на заднем фоне запрещено выводить информацию на терминал. Именно поэтому весь вывод автоматически перенаправляется в файл — wget-log.
С помощью команды ls можем убедиться что файл скачивается (его размер увеличивается):
alex@deb-11:~$ ls -lh ubuntu-22.04.1-desktop-amd64.iso -rw-r--r-- 1 alex alex 435M сен 26 11:30 ubuntu-22.04.1-desktop-amd64.iso alex@deb-11:~$ ls -lh ubuntu-22.04.1-desktop-amd64.iso -rw-r--r-- 1 alex alex 956M сен 26 11:31 ubuntu-22.04.1-desktop-amd64.iso
Выше я уже писал, что запустить процесс на заднем фоне можно только, если он не интерактивный. Например запустить остановленный man не получится:
alex@deb-11:~$ jobs [1]+ Остановлен man 7 signal [2]- Запущен wget https://releases.ubuntu.com/22.04.1/ubuntu-22.04.1-desktop-amd64.iso & alex@deb-11:~$ bg %1 [1]+ man 7 signal & [1]+ Остановлен man 7 signal alex@deb-11:~$ jobs [1]+ Остановлен man 7 signal [2]- Запущен wget https://releases.ubuntu.com/22.04.1/ubuntu-22.04.1-desktop-amd64.iso &
Как только man попытался вывести на терминал справку, то сразу был опять остановлен. И остановлен он был тоже с помощью сигнала. Вот два сигнала, которые работают для фоновых заданий:
- SIGTTIN (21) — остановлен за попытку чтения из stdin (на заднем фоне).
- SIGTTOU(22) — остановлен за попытку вывода на stdout (на заднем фоне).
Если плохо помните стандартные потоки ввода вывода (stdin, stdout, stderr), то про них я писал здесь.
Изменение приоритета процесса
Приоритеты процессов Linux
В работающей системе Linux выполняется множество процессов. И каждому запущенному процессу назначается приоритет. Как ни странно, но чем больше значение приоритета, тем меньше сам приоритет. То есть, процесс с приоритетом 15 будет более приоритетным, чем процесс с приоритетом 20.
Более приоритетные процессы получают больше процессорного времени. Они более отзывчивы, выполняются более быстро. Но при этом, они сильнее нагружают процессор и замедляют все остальные процессы.
Но никакие пользователи и даже root не могут управлять приоритетами процессов. Они могут управлять другим значением — nice. Диапазон nice имеет 40 приоритетов от -20 до +19. По умолчанию, любой процесс Linux, созданный пользователем, имеет значение nice равное 0, и приоритет равный 20.
Вы можете увидеть значение nice для процессов своего пользователя и своего терминала с помощью следующей команды (утилиту ps я рассматривал здесь):
alex@deb-11:~$ ps -o pid,comm,nice,priority PID COMMAND NI PRI 5564 bash 0 20 5761 ps 0 20
Или можно получить список всех процессов, а не только ваших:
alex@deb-11:~$ ps ax -o pid,comm,nice,priority PID COMMAND NI PRI 1 systemd 0 20 2 kthreadd 0 20 3 rcu_gp -20 0 4 rcu_par_gp -20 0 6 kworker/0:0H-ev -20 0 ****
Разница в том, что priority — это реальный приоритет процесса в данный момент, а nice — подсказка для ядра указывающая нужно ли увеличить или уменьшить приоритет процессу.
Для пользовательских процессов, в большинстве случаев, значение priority можно рассчитать по следующей формуле: priority = 20 + nice. Таким образом, процесс с nice=3 имеет priority=23, а процесс с nice=-7 имеет priority=13.
Кроме ps, для просмотра приоритетов процессов можно использовать top и htop.
В Linux управление процессами, а точнее их уровнем nice, происходит с помощью команд:
- nice — настраивает приоритет процесса перед его запуском;
- renice — позволяет изменить приоритет уже запущенного процесса.
При этом:
- обычный пользователь может лишь уменьшить nice своего процесса;
- root может уменьшить или увеличить nice своего или чужого процесса.
Команда nice
Вы можете легко проверить значение nice для своего терминала (или оболочки), выполнив команду nice без каких-либо аргументов:
alex@deb-11:~$ nice 0
То есть любые команды запущенные из этой оболочки будут иметь значение nice=0.
А чтобы запустить команду с изменённым значением nice, её нужно запускать таким образом: nice <приоритет> <выполняемая команда>. Например, запустим две программы с разным приоритетом (что-бы запустить их две в одном терминале, я использую выполнение на заднем фоне):
alex@deb-11:~$ nice -2 md5sum /dev/urandom ^Z [1]+ Остановлен nice -2 md5sum /dev/urandom alex@deb-11:~$ bg [1]+ nice -2 md5sum /dev/urandom & alex@deb-11:~$ nice -8 md5sum /dev/urandom ^Z [2]+ Остановлен nice -8 md5sum /dev/urandom alex@deb-11:~$ bg [2]+ nice -8 md5sum /dev/urandom & alex@deb-11:~$ jobs [1]- Запущен nice -2 md5sum /dev/urandom & [2]+ Запущен nice -8 md5sum /dev/urandom &
Теперь, с помощью htop, посмотрим на приоритеты процессов для нашего пользователя:
alex@deb-11:~$ htop -u alex
Как видите команды md5sum работают параллельно. Но у одной команды приоритет стал 22, а у другой 28. При этом более приоритетный процесс получает больше процессорного времени, это видно в колонке TIME+.
А если мы хотим указать отрицательное значение для nise. Другими словами, уменьшить nice и уменьшить priority, тем самым увеличив приоритет. То должны использовать двойное тире и sudo (так как обычный пользователь не имеет право запускать процессы с повышенным приоритетом):
alex@deb-11:~$ sudo nice --2 md5sum /dev/urandom [sudo] пароль для alex: ^Z [3]+ Остановлен sudo nice --2 md5sum /dev/urandom alex@deb-11:~$ bg [3]+ sudo nice --2 md5sum /dev/urandom & alex@deb-11:~$ jobs [1] Запущен nice -2 md5sum /dev/urandom & [2]- Запущен nice -8 md5sum /dev/urandom & [3]+ Запущен sudo nice --2 md5sum /dev/urandom &
Кстати, этот экземпляр md5sum, будет работать от пользователя root. Вот так можно посмотреть на все процессы md5sum с помощью ps:
alex@deb-11:~$ ps -C md5sum -o pid,user,comm,nice,priority,time,%cpu PID USER COMMAND NI PRI TIME %CPU 5791 alex md5sum 2 22 00:13:35 90.6 5792 alex md5sum 8 28 00:09:31 64.3 5802 root md5sum -2 18 00:06:34 98.4
Выше видно, что сильнее всего нагружает процессор процесс с уровнем nice равным -2.
Команда renice
Чтобы изменить приоритет уже работающего процесса нужно использовать команду renice -n <значение> -p <pid>. И этой командой может пользоваться только root.
Вернём всем процессам значение nice=0:
alex@deb-11:~$ sudo renice -n 0 -p 5791 5791 (process ID) old priority 2, new priority 0 alex@deb-11:~$ sudo renice -n 0 -p 5792 5792 (process ID) old priority 8, new priority 0 alex@deb-11:~$ sudo renice -n 0 -p 5802 5802 (process ID) old priority -2, new priority 0 alex@deb-11:~$ ps -C md5sum -o pid,user,comm,nice,priority,time,%cpu PID USER COMMAND NI PRI TIME %CPU 5791 alex md5sum 0 20 00:17:29 87.5 5792 alex md5sum 0 20 00:10:40 53.9 5802 root md5sum 0 20 00:11:28 98.3
А ещё вы можете изменить приоритет всех процессов определённого пользователя таким образом: sudo renice -n <значение nice> -u <имя пользователя>. Например:
alex@deb-11:~$ sudo renice -n 5 -u alex 1000 (user ID) old priority 0, new priority 5 alex@deb-11:~$ ps -C md5sum -o pid,user,comm,nice,priority,time,%cpu PID USER COMMAND NI PRI TIME %CPU 5791 alex md5sum 5 25 00:18:47 85.7 5792 alex md5sum 5 25 00:11:58 55.1 5802 root md5sum 0 20 00:12:46 93.9
Обратите внимание, процессы alex стали с уровнем nice=5, а процесс root так и остался с уровнем nice=0.
Итог
Статья получилось довольно длинной. Я показал как происходит некоторое управление процессами в Linux.
Рассказал про сигналы и как их отправлять процессам с помощью клавиатуры или с помощью команды kill. Также я показал, как можно использовать передний (fg) и задний (bg) фон терминала, и этим реально можно пользоваться. Также я разобрал приоритеты процессов и как их можно менять с помощью команд nice и renice.
В статье много примеров использования команды ps. Думаю, что умение работать с этой утилитой, это очень полезный навык для системного администратора Linux.
Спасибо! Очень полезная информация.