Процессы в Linux


Основы Linux

Компьютеры выполняют программы для решения различных задач. В современных вычислительных системах программное обеспечение можно "условно" разделить на два уровня:

  • ядра операционных систем и коммпоненты\модули ядра, о них Вы узнаете значительно позже (обязательно посвящу им курс);
  • процессы или утилиты уровня пользователя, которые мы рассмотрим в данном модуле.

При запуске Linux создаёт процесс init (сокращение от initиализатор), который, в свою очередь, запускает ряд других процессов, те запускают следующие — и так продолжается до тех пор, пока Вы не окажетесь перед командной оболочкой, которая тоже является процессом! Оболочка, в свою очередь, запускает процессы в ответ на вводимые Вами команды.

В данном модуле Вы научитесь просматривать процессы и взаимодействовать с ними самыми разнообразными способами!

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

В зависимости от источника, аббревиатура ps расшифровывается либо как «process snapshot» («снимок процессов»), либо как «process status» («состояние процессов») — в любом случае, команда выводит список процессов. По умолчанию ps отображает только те процессы, которые выполняются в Вашем терминале, что само по себе не очень информативно:

hacker@proc:~$ ps
    PID TTY          TIME CMD
    329 pts/0    00:00:00 bash
    349 pts/0    00:00:00 ps
hacker@proc:~$

В приведённом примере видны оболочка (bash) и сам процесс ps — больше в данном терминале ничего не выполняется. Обратите внимание: каждому процессу присвоен числовой идентификатор (идентификатор процесса, или PID) — уникальный номер, однозначно идентифицирующий каждый запущенный процесс в среде Linux. Также отображается терминал, в котором выполняются команды (в данном случае — pts/0), и суммарное время работы процессора, затраченное данным процессом (поскольку эти процессы крайне нетребовательны к ресурсам, они ещё не успели потребить даже одной секунды!).

В большинстве случаев именно это Вы и увидите при вызове ps без аргументов. Чтобы команда стала по-настоящему полезной, необходимо передать ей несколько аргументов.

Поскольку ps — очень старая утилита, синтаксис её использования несколько запутан. Существует два способа передачи аргументов.

«Стандартный» синтаксис: в этом режиме флаг -e выводит список «всех» (every) процессов, а -f обеспечивает «полный» (full) формат вывода, включая аргументы командной строки. Их можно объединить в единый аргумент -ef.

«BSD»-синтаксис: в этом режиме флаг a выводит процессы всех пользователей, x — процессы, не связанные ни с одним терминалом, а u формирует вывод, удобный для чтения пользователем (user-readable). Все три флага можно объединить в единый аргумент aux.

Оба варианта — ps -ef и ps aux — дают несколько различающийся, но взаимно узнаваемый результат.

Попробуем это на практике:

hacker@proc:~$ ps -ef
UID          PID    PPID  C STIME TTY          TIME CMD
hacker         1       0  0 05:34 ?        00:00:00 /sbin/docker-init -- /bin/sleep 1h
hacker         7       1  0 05:34 ?        00:00:00 /bin/sleep 1h
hacker       571     554  0 05:35 pts/0    00:00:00 /usr/bin/bash --init-file /usr/lib/code-server/lib/vscode/out/vs
hacker       695     571  0 05:35 pts/0    00:00:00 ps -ef
hacker@proc:~$

Здесь видны процессы инициализации среды задания (docker-init), таймер автозавершения для освобождения вычислительных ресурсов (sleep 1h — завершение через 1 час), оболочка (bash) и сам вызов ps -ef.

Примерно то же самое можно увидеть с помощью ps aux:

hacker@proc:~$ ps aux
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
hacker         1  0.0  0.0   1128     4 ?        Ss   05:34   0:00 /sbin/docker-init -- /bin/sleep 6h
hacker         7  0.0  0.0   2736   580 ?        S    05:34   0:00 /bin/sleep 6h
hacker       571  0.0  0.0   4600  4032 pts/0    Ss   05:35   0:00 /usr/bin/bash --init-file /usr/lib/code-server/li
hacker      1172  0.0  0.0   5892  2924 pts/0    R+   05:38   0:00 ps aux
hacker@proc:~$

Между ps -ef и ps aux много общего: оба варианта отображают имя пользователя (столбец USER), PID, терминал (TTY), время запуска процесса (STIME/START), суммарное использованное процессорное время (TIME) и команду (CMD/COMMAND).

Команда ps -ef дополнительно выводит идентификатор родительского процесса (PPID) — PID процесса, из которого был запущен данный, тогда как ps aux показывает долю от общего времени ЦПУ и объёма оперативной памяти системы, используемых процессом. Кроме того, в выводе присутствует ряд других полей, которые мы пока не будем рассматривать.

ПРИМЕЧАНИЕ: Обе команды — ps -ef и ps aux — усекают вывод командной строки до ширины Вашего терминала (именно поэтому в примерах выше правый край выглядит таким ровным). Если Вам не удаётся прочитать полный путь к процессу, попробуйте увеличить окно терминала (или перенаправить вывод в файл, чтобы избежать усечения). Кроме того, можно дважды передать опцию w (например, ps -efww или ps auxww), чтобы полностью отключить усечение.


Приступим к практике!

В данном задании файл /challenge/run снова переименован в случайное имя, причём на этот раз команда ls в каталоге /challenge недоступна! Однако процесс уже запущен, так что Вы можете найти его в списке работающих процессов, узнать имя файла и запустить его напрямую, чтобы получить флаг. Удачи!

Connect with SSH

Link your SSH key, then connect with: ssh hacker@dojo.pwn.college

Вы научились запускать процессы и просматривать их список — теперь пришло время освоить их завершение!

В Linux для приостановки процесса используется команда с говорящим названием kill. При вызове с параметрами по умолчанию (именно они рассматриваются в данном задании) kill завершает процесс, предоставляя ему возможность корректно завершить свои дела перед тем, как прекратить существование.

Предположим, у Вас есть надоедливый процесс sleep (sleep — это программа, которая просто «висит» указанное в командной строке количество секунд; в данном случае — 1337 секунд), запущенный в другом терминале:

hacker@proc:~$ sleep 1337

Как избавиться от sleep? Для этого нужно вызвать kill, передав идентификатор процесса (значение PID из вывода ps) в качестве аргумента:

hacker@proc:~$ ps -e | grep sleep
 342 pts/0    00:00:00 sleep
hacker@proc:~$ kill 342
hacker@proc:~$ ps -e | grep sleep
hacker@proc:~$

Все очень просто!


Что ж, пора завершить Ваш первый процесс!

В данном задании /challenge/run отказывается запускаться, пока работает /challenge/dont_run! Вам необходимо найти процесс dont_run и завершить его с помощью kill. Удачи.

Connect with SSH

Link your SSH key, then connect with: ssh hacker@dojo.pwn.college

Вы узнали, как завершать другие процессы с помощью команды kill, однако порой требуется просто избавиться от процесса, занявшего Ваш терминал! К счастью, в терминалах для этого предусмотрено специальное сочетание клавиш: Ctrl-C (то есть удерживание клавиши Ctrl при одновременном нажатии C) отправляет сигнал «прерывание» любому приложению, ожидающему ввода с терминала, и, как правило, это приводит к корректному завершению приложения.

ПРИМЕЧАНИЕ:

Если Вас интересуют подробности, обратитесь к этой статье о терминалах и «управляющих кодах» (таких как Ctrl-C).


Попробуйте это прямо сейчас!

Команда /challenge/run не выдаст Вам флаг до тех пор, пока Вы её не прервёте. Удачи!

Connect with SSH

Link your SSH key, then connect with: ssh hacker@dojo.pwn.college

Иногда некорректно работающие процессы мешают Вашей работе. В таких случаях их необходимо завершить...

В данном задании существует процесс-приманка, захвативший критически важный ресурс — именованный канал (FIFO) по пути /tmp/flag_fifo, в который (как в задании про FIFO из модуля Управление потоками) /challenge/run хочет записать Ваш флаг. Этот процесс необходимо завершить с помощью kill.

Общий порядок действий:

  1. Проверьте список запущенных процессов.
  2. Найдите /challenge/decoy в списке и определите его идентификатор процесса.
  3. Завершите его с помощью kill.
  4. Запустите /challenge/run, чтобы получить флаг, не будучи заваленным приманками (перенаправлять вывод не нужно — процесс самостоятельно запишет данные в FIFO).

Удачи!

ПРИМЕЧАНИЕ:

После завершения процесса-приманки Вы можете ещё увидеть несколько ложных флагов!

Это происходит потому, что каналы Linux буферизованы: данные как бы проходят через некий «буфер», и вполне возможно, что Вы завершите процесс-приманку в тот момент, когда часть данных уже находится в канале. Эти данные, попав в канал, всё равно дойдут до другого его конца (до Вашего cat). Подождите секунду — приманки прекратятся, после чего Вы сможете запустить /challenge/run и победить!

Connect with SSH

Link your SSH key, then connect with: ssh hacker@dojo.pwn.college

Вы научились прерывать процессы с помощью Ctrl-C, однако существуют и менее радикальные способы вернуть себе управление терминалом!

Процессы можно приостановить и отправить в фоновый режим нажатием Ctrl-Z. В данном задании мы разберёмся, как это работает, а в следующем научимся возобновлять приостановленные процессы!

Команда run в этом задании ожидает, что одновременно с ней будет запущена ещё одна её копия — `в том же терминале. Как это сделать?

Запустите /challenge/run из терминала, затем приостановите, а после запустите вторую копию, пока первая находится в приостановленном состоянии!

Connect with SSH

Link your SSH key, then connect with: ssh hacker@dojo.pwn.college

Как правило, после приостановки процесса рано или поздно потребуется его возобновить. Иначе зачем вообще его приостанавливать — проще было бы сразу завершить?

Для возобновления процессов оболочка предоставляет команду fg — встроенную функцию, которая берёт приостановленный процесс, возобновляет его и возвращает на передний план терминала. Попробуйте это на практике!

Команда run в данном задании требует, чтобы Вы её сначала приостановили, а затем возобновили. Удачи!

Connect with SSH

Link your SSH key, then connect with: ssh hacker@dojo.pwn.college

Вы уже умеете переводить процессы на передний план с помощью команды fg. Кроме того, процессы можно возобновлять в фоновом режиме с помощью команды bg!

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

Команда run в данном задании ожидает, что одновременно с ней будет запущена ещё одна её копия — НЕ ПРИОСТАНОВЛЕННАЯ — в том же терминале.

Как это сделать?

  1. Запустите /challenge/run из терминала;
  2. Затем приостановите;
  3. Переведите /challenge/run в фоновый режим с помощью bg;
  4. Запустите вторую копию, пока первая работает в фоне;
  5. Вы великолепны!

ДОПОЛНИТЕЛЬНО:

Есть существенное различие между приостановленным и фоновым состояниями процесса!

Рассмотрим это на примере. Сначала приостановим процесс sleep:

hacker@proc:~$ sleep 1337
^Z
[1]+  Stopped                 sleep 1337
hacker@proc:~$

Процесс sleep теперь приостановлен в фоне. Это можно увидеть с помощью ps, включив отображение столбца stat через опцию -o:

hacker@proc:~$ ps -o user,pid,stat,cmd
USER         PID STAT CMD
hacker       702 Ss   bash
hacker       762 T    sleep 1337
hacker       782 R+   ps -o user,pid,stat,cmd
hacker@proc:~$ 

Видите букву T?

Она означает, что процесс приостановлен нажатием Ctrl-Z. Буква S в столбце STAT для bash означает, что оболочка находится в режиме ожидания ввода. Буква R в столбце ps означает активное выполнение, а знак + — что процесс находится на переднем плане!

Посмотрим, что произойдёт, когда мы возобновим sleep в фоновом режиме:

hacker@proc:~$ bg
[1]+ sleep 1337 &
hacker@proc:~$ ps -o user,pid,stat,cmd
USER         PID STAT CMD
hacker       702 Ss   bash
hacker       762 S    sleep 1337
hacker      1224 R+   ps -o user,pid,stat,cmd
hacker@proc:~$

Отлично!

Теперь sleep отображается с буквой S. Процесс находится в режиме ожидания (собственно, он и занимается ожиданием), но уже не приостановлен! Он выполняется в фоновом режиме, на что указывает отсутствие знака +. Подробное описание всех состояний STAT можно найти на Википедии, но их изучение мы оставляем на Ваше усмотрение.

Connect with SSH

Link your SSH key, then connect with: ssh hacker@dojo.pwn.college

Представьте, что у Вас есть процесс, выполняющийся в фоновом режиме, и Вы хотите продолжить с ним работу.Что делать?

Вернуть фоновый процесс на передний план можно с помощью команды fg — точно так же, как Вы переводите на передний план приостановленный процесс. Данное задание поможет попрактиковаться в этом, Вам нужно:

  1. Запустить /challenge/run;
  2. Приостановите работу программы;
  3. Возобновите приостановленный процесс в фоновом режиме;
  4. Затем переведите /challenge/run на передний план, не приостанавливая повторно.

Удачи!

Connect with SSH

Link your SSH key, then connect with: ssh hacker@dojo.pwn.college

Разумеется, переводить процессы в фоновый режим через приостановку необязательно: можно сразу запустить их в фоне.

Это очень просто — достаточно добавить символ & в конец команды:

hacker@proc:~$ sleep 1337 &
[1] 1771
hacker@proc:~$ ps -o user,pid,stat,cmd
USER         PID STAT CMD
hacker      1709 Ss   bash
hacker      1771 S    sleep 1337
hacker      1782 R+   ps -o user,pid,stat,cmd
hacker@proc:~$ 

Здесь sleep активно выполняется в фоновом режиме — не в приостановленном состоянии.


Теперь Ваша очередь попрактиковаться! Запустите /challenge/run в фоновом режиме, чтобы получить флаг. Удачи.

Connect with SSH

Link your SSH key, then connect with: ssh hacker@dojo.pwn.college

Каждая команда оболочки — будь то программа или встроенная функция — после завершения своей работы возвращает код завершения.

По этому коду оболочка или пользователь (то есть Вы!) могут определить, успешно ли выполнил процесс свою задачу (что считать успехом, разумеется, зависит от того, что именно данный процесс должен делать). Обратиться к коду завершения последней выполненной команды можно с помощью специальной переменной ? (так как это переменная, не забудьте добавить перед ней $, чтобы прочитать её значение!):

hacker@proc:~$ touch test-file
hacker@proc:~$ echo $?
0
hacker@proc:~$ touch /test-file
touch: cannot touch '/test-file': Permission denied
hacker@proc:~$ echo $?
1
hacker@proc:~$

Принято, что успешно завершившиеся команды возвращают 0, а завершившиеся с ошибкой — ненулевое значение: чаще всего 1, хотя иногда это специфический код ошибки, указывающий на конкретный тип сбоя.


В данном задании Вам необходимо получить код завершения, возвращённый /challenge/get-code, а затем запустить /challenge/submit-code с этим кодом в качестве аргумента. Удачи!

Connect with SSH

Link your SSH key, then connect with: ssh hacker@dojo.pwn.college