Основные моменты при взаимодействии с процессами в Linux

Основные моменты при взаимодействии с процессами в Linux

ЛЕТОПИСЕЦ КИТЕЖ-ГРАДА

Введение: Как я уже упоминал ранее, в перерыве между написанием статей по основополагающим DevOps-инструментам, периодически будут всплывать заметки на различные темы, с которыми приходится сталкиваться в повседневной жизни. Темы, затрагиваемые в таких заметках, не столь масштабны, как Docker или Jenkins, но это то, с чем нам постоянно приходится сталкиваться. Одна из таких тем – процессы в Linux. И, хотя данная тема достаточно обширна и глубока по своей природе, мы сфокусируемся лишь на базовых моментах. Я бы даже сказал, что это именно то, с чем нам всем чаще всего приходится сталкиваться и это то, что чаще всего спрашивают на собеседованиях. Поэтому на полноту знаний данная статья нисколько не претендует.

Proccess Linux: Прежде всего необходимо знать и понимать, что процесс – это экземпляр запущенной вами программы. Больше в качестве ответа ничего и не надо. Однако если углубиться на самую малость, то по своей сути процесс представляет собой некое общее определение механизма, который управляет памятью, временем работы ЦП, а также ресурсами ввода-вывода. Таким образом, что процесс, что программа в Linux, представляют собой одно и тоже. Когда мы вводим имя программы в терминале и нажимаем Enter, мы тем самым её запускаем, а это значит, что вместе с ней образуется и процесс исполняемой программы. Ещё стоит понимать, что пока программа не запущена, это просто программный файл, который отличается от любого другого файла только наличием бита X (возможность исполнения). Но как только такой файл был запущен, он становится процессом под управлением ядра Linux. Так, например, всякий раз, когда в терминале выполняется какая-нибудь команда (например, команда ls), система создает/запускает новый процесс:

При этом в системе Linux существует три основных типа процессов:

Интерактивный процесс: Данный тип процесса инициализируется и управляется с помощью терминала. Это значит, что необходимым условием для запуска таких процессов является наличие пользователя, подключенного к системе. Такой тип процессов не запускается автоматически как часть системных функций/служб. Когда команда/процесс выполняется, они полностью занимают запустивший их терминал. Другие команды не получится запустить, поскольку оболочка будет недоступна из-за ранее запущенной программы, которая в свою очередь выполняется на переднем плане.
Фоновый процесс: Данный тип представляет собой процесс, который не был запущен в терминале. То бишь, он не ожидает пользовательского ввода данных. Таким образом, остальные процессы могут выполняться параллельно с тем процессом, который был запущен в фоновом режиме, поскольку им не нужно ждать его завершения.
Демоны: Данный тип представляет собой особый тип фоновых процессов, которые запускаются при старте системы и продолжают работать в виде службы. Они не умирают. Такие процессы запускаются как системные задачи (службы). Однако при этом они могут управляться пользователем через init-процесс. Например, к демонам относится sshd (служба, принимающая от клиентов запросы на соединения по протоколу SSH). За исключением процесса init и некоторых других, процессы демонов обычно имеют окончание d в своем имени.

Поскольку Linux является многопользовательской системой, каждый из пользователей может запустить свой экземпляр программы, при этом каждый запущенный экземпляр программы должен быть однозначно идентифицирован ядром. Процессы запущенной программы имеют уникальный пятизначный номер, известный как PID (то бишь, Process Identificator), а также PPID (то бишь, Parent Process Identificator). В связи с этим процессы дополнительно разделяют на две группы:

Parrent Proccess: Это процессы, которые во время своего выполнения создают другие процессы.
Child Proccess: Это процессы, создаваемые другими процессами во время своего выполнения.

Прародителем всех процессов в системе является процесс init – первая программа, которая выполняется при загрузке Linux и управляет всеми другими процессами в системе. Init запускается самим ядром и всегда имеет PID равный 1, поэтому у него в принципе не может быть родительского процесса. Когда процесс передает ядру запрос, который не может быть исполнен сразу же, то процесс погружается в сон/ожидание и пробуждается, когда запрос может быть удовлетворен. В связи с этим, в зависимости от текущей ситуации, процесс, во время своего выполнения, может переходить из одного состояния в другое. Рассмотрим основные состояния процесса:

Running: Данный процесс либо запущен (текущий процесс в системе), либо готов к запуску (ожидает передачи на выполнение процессору).
Sleeping: Данный процесс ожидает наступления некоторого события (пользовательского ввода, сигнала от другого процесса и т.п.) или выделения системных ресурсов. Кроме того, ядро также различает два типа ожидающих процессов: прерываемые ожидающие процессы, которые могут быть прерваны сигналами и не прерываемые ожидающие процессы, которые ожидают непосредственно на аппаратном уровне и не могут быть прерваны каким-либо событием или сигналом.
Stopped: Данный процесс будет остановлен, как правило, путем получения сигнала о штатном завершении работы exit().
Zombie: Состояние, которое возникает, когда родительский процесс убивается до завершения дочернего процесса, при этом дочерние процессы становятся как бы осиротевшими, при этом в качестве нового родителя (с соответствующим изменением PPID) им назначается процесс init. Убитые процессы, но при этом всё еще отображающиеся в таблице процессов, называются процессами зомби (они мертвы и не используются). Иными словами, зомби – это завершившийся процесс, но память о нём всё ещё хранится в ядре. Более того, это второе состояние, в котором процесс может игнорировать сигнал SIGKILL, ведь что мертво не может умереть ещё раз.

Не менее важную роль в работе процессов играют сигналы в Linux. При этом сигналы представляют собой программные прерывания, посылаемые программе, чтобы указать, что произошло важное событие. События могут варьироваться от пользовательских запросов до недопустимых ошибок доступа к памяти. Некоторые сигналы, такие как сигнал прерывания, указывают, что пользователь попросил программу сделать что-то, что не соответствует обычному потоку управления. Рассмотрим некоторые сигналы:

Сигнал SIGHUP: Данный сигнал имеет порядковый номер 1. Изначально был предназначен для того, чтобы информировать программу о потере связи с управляющим терминалом (терминалы часто подключались к системе с помощью модемов). Этот сигнал посылается приложению также и в том случае, если процесс-лидер сессии завершил свою работу. Многие программы-демоны, у которых нет лидера сессии, так же обрабатывают этот сигнал. В ответ на получение SIGHUP демон обычно перезапускается или повторно считывает файл конфигурации. По умолчанию программа, получившая этот сигнал, завершается.
Сигнал SIGINT: Данный сигнал имеет порядковый номер 2. Это сигнал, который обычно используется для прерывания запущенного процесса. В общем это тот сигнал, который отправляется, когда пользователь выполняет Ctrl + C в терминале.
Сигнал SIGABRT: Данный сигнал имеет порядковый номер 6. Он посылается программе в результате вызова функции abort. В результате чего программа завершается с сохранением на диске образа памяти.
Сигнал SIGKILL: Данный сигнал имеет порядковый номер 9. Он используется для принудительной остановки процесса независимо от того, можно ли его остановить корректно или нет. Этот сигнал нельзя игнорировать, за исключением процесса инициализации (или systemd в последних дистрибутивах).
Сигнал SIGTERM: Данный сигнал имеет порядковый номер 15. Он вызывает вежливое завершение программы. Получив этот сигнал, программа может выполнить необходимые перед завершением операции (например, высвободить занятые ресурсы). Получение SIGTERM свидетельствует не об ошибке в программе, а о желании ОС или пользователя завершить ее.
SIGQUIT: Данный сигнал имеет порядковый номер 3. Это специальный сигнал, отправляемый, когда пользователь хочет выйти из текущего процесса. Его можно вызвать, нажав Ctrl + D, и он часто используется в терминальных оболочках или в сеансах SSH.
SIGUSR1, SIGUSR2: Данные сигналы имеют порядковые номера 10 и 12. Эти сигналы используются исключительно для целей связи, и их можно использовать в программах для реализации пользовательских обработчиков.
SIGSTOP: Данный сигнал имеет порядковый номер 19. Этот сигнал указывает процессу остановить его выполнение без завершения процесса. Затем процесс ожидает последующего продолжения работы или его полной остановки.
SIGCONT: Данный сигнал имеет порядковый номер 18. Если процесс помечен как остановленный, данный сигнал указывает этому процессу снова начать выполнение.

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

firefox

Однако, как только консоль будет закрыта или будет применено сочетание клавиш Ctrl + C, программа закроется, что, в общем-то, эквивалентно закрытию консоли. Это как-раз таки интерактивный процесс, о котором шла речь ранее. Запуск же процесса в фоновом режиме полезен только для программ, которые не нуждаются в пользовательском вводе (через оболочку). Перевод задания в фоновый режим обычно выполняется, когда ожидается, что выполнение задания займет много времени. Кроме этого, в оболочку встроена утилита для управления заданиями, которая позволяет легко управлять несколькими процессами, переключая их между передним планом и фоновым исполнением. Также, с помощью утилиты jobs процессы могут быть сразу запущены в фоновом режиме. Чтобы запустить процесс в фоновом режиме, используйте символ & после имени запускаемой программы. В этом случае процесс не будет принимать пользовательский ввод, пока не переместится на передний план:

google-chrome &
jobs

[1]+ Running                google-chrome &

Для взаимодействия с заданиями есть две следующие команды: bg и fg. Команда bg используется в Linux для отправки процесса в фоновый режим и имеет следующий вид:

bg %<job_id>

Точно так же, чтобы отправить процесс на передний план, можно использовать fg таким же образом:

fg %<job_id>

Теперь, если обратиться к списку заданий, выведя его предварительно в консоли, можно вывести любое задание из списка на передний план, то бишь в текущее окно оболочки. Для этого необходимо использовать вот такую команду:

jobs
fg %2

Введя команду Ctrl + Z процесс можно остановить. Этот же процесс также можно связать с командой bg, чтобы отправить в фоновый режим. К слову, идентификатор любого процесса можно получить с помощью такой команды, как pidof:

pidof firefox
pidof systemd
pidof bash
medoed@kitezh:~> pidof firefox
17566 17465 17332 15131 12549 12219 12110 11515 11267 10933 10884 10788 10751 4204 4067 3281 3244 2760 2729 2576 2528 2516 2444 2417 2325
medoed@kitezh:~> pidof systemd
1848 1
medoed@kitezh:~> pidof bash
2502
medoed@kitezh:~> 

Чтобы вывести информацию о PID и PPID текущей оболочки, необходимо выполнить следующие две команды:

echo $$
echo $PPID

Существует несколько различных инструментов для просмотра/перечисления запущенных в системе процессов. Двумя традиционными и хорошо известными из них являются команды ps и top. Команда ps отображает информацию об активных процессах в системе, как показано в примере ниже:

ps
 PID TTY         TIME CMD
 2502 pts/0   00:00:00 bash
 19766 pts/0   00:00:00 ps

По умолчанию команда ps покажет вам список текущих запущенных процессов, принадлежащих текущему пользователю. В нашем случае запущено только два процесса: интерпретатор bash и сама команда ps, которая была выполнена. Важно понимать, что у каждого процесса есть владелец. В большинстве случаев это пользователь, который запустил данный процесс, хотя бывают и исключения. Чтобы проиллюстрировать это, давайте составим список первых десяти процессов на нашей операционной системе Linux, только с другим форматом отображения:

ps -ef | head -n 10
UID       PID PPID C STIME TTY         TIME CMD
root        1    0 0 Dec23 ?       00:00:01 /usr/lib/systemd/systemd --switched-root --system --deserialize 39
root        2    0 0 Dec23 ?       00:00:00 [kthreadd]
root        3    2 0 Dec23 ?       00:00:00 [rcu_gp]
root        4    2 0 Dec23 ?       00:00:00 [rcu_par_gp]
root        5    2 0 Dec23 ?       00:00:00 [slub_flushwq]
root        6    2 0 Dec23 ?       00:00:00 [netns]
root        8    2 0 Dec23 ?       00:00:00 [kworker/0:0H-events_highpri]
root       10    2 0 Dec23 ?       00:00:00 [mm_percpu_wq]
root       11    2 0 Dec23 ?       00:00:00 [rcu_tasks_kthread]

Столбцы, присутствующие в выводе команды ps, имеют следующие значения:

UID: Идентификатор пользователя, которому принадлежит процесс (тот, от чьего имени происходит выполнение).
PID: Идентификатор самого процесса.
PPID: Идентификатор родительского процесса.
C: Загрузка CPU процессом.
STIME: Время начала выполнения процесса.
TTY: Тип терминала, связанного с процессом.
TIME: Количество процессорного времени, потраченного на выполнение процесса.
CMD: Команда, запустившая этот процесс.

В целом, чтобы ознакомиться со всеми опциями, которые присутствуют в ps, необходимо обратиться к соответствующему руководству, однако некоторые опции всё же стоит здесь упомянуть:

Для получения дополнительной информации о процессах, запущенных текущим пользователем, применяется опция -f.
Для отображения дополнительной информации о процессах по всем пользователям используется опция -a.
Для отображения дополнительной информации о процессе по заданному UID или имени пользователя используется опция -u.
Для отображения расширенной информации используется опция -e.

Если вы хотите вывести вообще всю информацию по всем процессам системы, то используйте команду ps вот в таком виде:

ps -aux

Команда ps также поддерживает функцию сортировки процессов по соответствующим столбцам. Например, чтобы отсортировать список процессов по потреблению ресурсов процессора (в порядке возрастания), введите следующую команду:

ps -aux --sort=%cpu

Еще один очень популярный пример использования команды ps – это объединение её и команды grep для поиска заданного процесса по его имени:

ps -aux | grep bash
medoed  21568 0.0 0.1  9484 6160 pts/0   Ss  19:57  0:00 bash
medoed  23446 0.0 0.0  6556 2080 pts/0   S+  20:16  0:00 grep --color=auto bash

Теперь в том же ключе рассмотрим команду top. Сама команда отображает информацию о запущенных процессах в режиме реального времени:

top

Рассмотрим более детально каждый компонент, показанный на рисунке ниже:

PID: Идентификатор процесса.
USER: Пользователь, которому принадлежит процесс.
PR: Приоритет процесса на уровне ядра.
NI: Приоритет выполнения процесса от -20 до 19.
VIRT: Общий объем (в килобайтах) виртуальной памяти (физическая память самого процесса; загруженные с диска файлы библиотек; память, совместно используемая с другими процессами и т.п.), используемой задачей в данный момент.
RES: Текущий объем (в килобайтах) физической памяти процесса.
SHR: Объем совместно используемой с другими процессами памяти.
S (сокр. от «STATUS»): Состояние процесса, где S означает прерываемое ожидание; где I означает, что процесс бездействует; где R означает, что процесс выполняется; где Z означает, что это процесс зомби.-процесс.
%CPU: Процент используемых ресурсов процессора.
%MEM: Процент используемой памяти.
TIME+: Количество процессорного времени, затраченного на выполнение процесса.
COMMAND: Имя процесса (команды).

Теперь немного поговорим о том, как управлять процессами в Linux. Мы уже говорили о том, что сигналы представляют собой форму межпроцессного взаимодействия (также называемого IPC), которое создает и отправляет асинхронные уведомления запущенным процессам о возникновении определенного события. Сигналы часто используются для того, чтобы послать процессу команду уничтожения или завершения, чтобы завершить его работу (также называется сигналом уничтожения). Чтобы послать сигнал процессу, необходимо использовать команду kill:

kill -<номер сигнала> <pid>|<имя_процесса>

Например, чтобы принудительно завершить процесс HTTPD (PID = 123) (без полного завершения работы), необходимо использовать следующую команду:

kill -9 123

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

kill -l

Чтобы отправить процессу нужный сигнал, используйте команды kill, pkill или pgrep. Однако стоит помнить, что программы могут реагировать на сигналы только в том случае, если они запрограммированы на распознавание этих сигналов. Большинство сигналов предназначены для внутреннего использования системой или для программистов, когда они пишут код. Ниже приведены примеры команды kill для уничтожения приложения firefox с помощью PID, после его зависания:

pgrep -u diego firefox

2275 # Ответ терминала.

kill 9 2275 # Результат одного и того же.
kill -KILL 2275 # Результат одного и того же.
kill -SIGKILL 2275 # Результат одного и того же.

Чтобы убить приложение, используя его имя, применяются команды pkill или killall:

pkill firefox
killall firefox

В Linux не все процессы имеют одинаковый приоритет, когда речь идет о процессорном времени. Некоторым процессам, например очень важным процессам, запускаемым пользователем root, присваивается более высокий приоритет, чтобы операционная система могла работать над задачами, действительно важными для системы. Шкала приоритетов от -20 до 19. Чем ниже вы идете по этой шкале, тем выше будет приоритет. Точно так же, чем выше вы находитесь по шкале, тем ниже будет ваш приоритет:

Чтобы запустить определенную программу или процесс с заданным хорошим уровнем, выполните следующую команду:

nice -n <уровень> <команда>

Например, чтобы запустить команду tar с пользовательским уровнем tar, необходимо выполнить следующую команду:

nice -n 19 tar -cvf test.tar file

Точно так же вы можете использовать команду renice, чтобы установить уровень nice запущенного процесса на заданное значение.

renice -n <приоритет> <pid>

Например, если у нас есть запущенный процесс с PID 123, можно использовать команду renice, чтобы установить его приоритет на заданное значение:

renice -n 18 123

В общем-то на этом всё. Эту статью можно и нужно расценивать сугубо как шпаргалку по основным командам, которые используются для работы с процессами в Linux.

Report Page