# Управление заданиями


## Введение

<!-- Активная и фоновая группа процессов, передний и задний план -->
Пользователь держит на компьютере несколько запущенных программ, работая с ними попеременно.
Принимать ввод от пользователя может только одна программа.
О ней говорят, что она *активная* и *расположена на переднем плане*.
Остальные программы *фоновые* и *расположены на заднем плане*.

<!-- Потребность в многозадачности -->
До настоящего момента, из командной оболочки мы запускали только одну терминальную программу.
Следующую программу запускали после завершения предыдущей.
Потребность в работе с несколькими программами не возникала, но на практике такое бывает часто.
Например, небольшую программу пишут в текстовом редакторе, а компиляцию запускают из командной строки.
Длительные по времени задания, которые не требуют данных от пользователя, как архивация и сборка программы, лучше запускать на заднем плане.

<!-- Что такое задание -->
<!-- Warning: не понимают, что такое процесс. -->
Запуск программы или исполнение команды порождает процессы.
Процессов может быть один или несколько, например при запуске конвейера или программы с параллельными вычислениями.
Пользователь в такой группе процессов видит одну целостно управляемую сущность, которую называют *заданием*.
Управляющие воздействия на задание, как завершение, приостановка, переключение из переднего плана на задний и наоборот, будут применены ко всей группе процессов.


## Управление заданиями

<!-- Останов программы `Ctrl-C` -->
Интерактивные терминальные приложения предлагают разные способы завершения работы.
Пейджер `less` закрывается по команде `q`, текстовый редактор `nano` по `Ctrl-X`, `vim` по `:q!`.
Остальные (неинтерактивные) программы, если в них не заложено завершение по просьбе, могут быть принудительно завершены системой сочетанием клавиш `Ctrl-C`.

<!-- Запуск программы на заднем плане -->
На заднем плане одновременно могут размещаться несколько заданий.
Если в конец команды добавить символ `&`, то команда запустится на заднем плане.

```console
user@sppo:~/projects/$ find ~ -name lab1.txt &
[1] 33533
```

Дополнительно на экране выведется номер задания ([1]) и идентификатор процесса (33533).
В последующем они понадобятся, чтобы сослаться на задание.

```{note}
Если задание состоит из нескольких процессов, то будет выведет идентификатор процесса, являющегося *лидером группы*.
<!-- Подробнее об этом будет рассказано в разделе [Управление процессами](process.md). -->
```

Фоновое задание будет выполнять работу до своего завершения или пока ему не понадобится ввод от пользователя.
В последнем случае оно приостановит свое выполнение до переключения в активное состояние.
Примером программы, постоянно нуждающемся в пользовательских данных, является `vim`.

```console
user@sppo:~/projects$ vim &
[1] 34226
user@sppo:~/projects$ 

[1]+  Stopped                 vim
user@sppo:~/projects$ 
```

Фоновые программы выводят сообщения на экране, что может быть неожиданным.
Чтобы такого не происходило, используют перенаправление потока вывода в файл или системную корзину: `./program > /dev/null &`

<!-- Перевод задания с переднего плана на задний (`Ctrl-Z` + `bg`) -->
Сочетание клавиш `Ctrl-Z` приостанавливает активное задание.
После приостановки на передний план выходит командная оболочка.
Встроенная команда `bg` (сокращение от "background" в переводе "задний план") запускает приостановленное задание и перемещает его на задний план.
Таким образом комбинации `Ctrl-Z` и `bg` перемещают задание из переднего плана на задний.

<!-- Перечисление процессов на заднем плане -->
На заднем плане могут размещаться несколько заданий.
Их список на экран выводит встроенная команда `jobs`.

```console
user@sppo:~/projects$ jobs
[1]+  Stopped                 vim
[2]   Running                 bzip2 -k9 bigfile.bin &
[3]-  Running                 sleep 2h &
```

Она выводит список заданий с номером, состоянием и командной строкой запуска.
Знаком `+` отмечено текущее задание, а знаком `-` -- предыдущее.

<!-- Перевод задания с заднего плана на передний -->
Перенос задания на передний план выполняет команда `fg` (сокращение от "foreground", что в переводе "передний план").
Если аргумент не указан, то команда перенесет текущее задание (то, что отмечено знаком `+`).
Чтобы сослаться на конкретное задание, используют символ `%` и номер задания: `fg %2`.


## Сигналы

Сочетания клавиш `Ctrl-C` и `Ctrl-Z` соответственно завершают и приостанавливают активный процесс.
Технически, это выглядит как отправка терминалом группе процессов сигнала о завершении (`-SIGINT`) и приостановке (`-SIGTSTP`).
Те же самые сигналы можно отправить произвольному процессу, воспользовавшись утилитой `kill`.
Утилита требует идентификатор процесса (или номер задания, предваренного символом `%`) и по умолчанию отправляет сигнал `-SIGKILL`, который завершает принудительно процесс.

```console
bob@pc:~/projects/sppo$ dd if=/dev/zero of=/dev/null &
[1] 12512
bob@pc:~/projects/sppo$ kill %1
bob@pc:~/projects/sppo$ 
[1]+  Завершено      dd if=/dev/zero of=/dev/null
bob@pc:~/projects/sppo$ 
```

```{note}
<!-- Различие сигналов `-SIGINT` и `-SIGKILL` -->
Сигнал `-SIGINT` "просит" процесс завершиться.
Если программа не переопределяла обработчик событий на него, то она выполнит это действие.
Правильно переопределенный обработчик сигнала сохраняет данные и завершает после этого процесс.
В отличие от него сигнал `-SIGKILL` завершает процесс немедленно, без каких-либо предварительных действий.
Если `-SIGINT` можно проигнорировать, установив пустой обработчик сигнала, то `-SIGKILL` -- нет.
```

<!-- `Ctrl-\` (`-SIGQUIT`) -- принудительное завершение процесса с сохранением дампа памяти -->
Полезное сочетание клавиш `Ctrl-\` принудительно завершает активное задание сигналом `-SIGQUIT`.
Но в отличие от `-SIGKILL` в текущем каталоге сохранится содержимое оперативной памяти процесса (дамп памяти).
В последующем дамп памяти может быть использован для отладки программы.

<!-- Управление выводом на экран: `Ctrl-S` и `Ctrl-Q` -->


## Заключение

<!-- Работа внутри одного приложения -->
Работа разработчика состоит в изучении и редактировании исходного кода, отладке, в ожидании сборки и результатов тестирования.
Вся эта деятельность происходит внутри одного приложения, называемого интегрированной средой разработки.
Это позволяет отказаться от переключения между разнородными программами.
Редактор `vim` позволяет настроить себя под полноценную среду разработки.

<!-- Разбиение экрана на части -->
Альтернативный способ работы с несколькими приложениями -- это разделение экрана терминала на несколько вложенных терминалов с неперекрывающимися видами.
Подробнее об этом будет рассказано в разделе [Терминальный мультиплексор](tmux.md).


## Использованные источники

1. Раздел "34.7. Управление заданиями" книги М.Керриск "Linux API. Исчерпывающее руководство." -- СПб.: Питер, 2021. -- 1248 с.: ил. -- (Серия "Библиотека программиста").
1. [Job Control Basics](https://www.gnu.org/software/bash/manual/html_node/Job-Control-Basics.html)
1. [Need a Linux command that takes much time](https://superuser.com/questions/131502/need-a-linux-command-that-takes-much-time)


## Вопросы для самоконтроля

1. Расскажите про активные и фоновые программы.
   Чем они отличаются?
   Где они могут понадобиться?
1. В чем отличия между программой, командой, заданием и процессом?
1. Как завершить запущенную программу?
1. Запустите команду `sort` и завершите запущенную программу.
1. Запустите на заднем плане перечисленные ниже команды и объясните, что они делают:
   * `vim main.c`
   * `sleep 1h`
   * `dd if=/dev/zero of=/dev/null`
1. Что произойдет с фоновым заданием, которому вдруг понадобятся пользовательские данные?
1. Что произойдет, если фоновому заданию понадобится вывести данные на экран?
1. Как перевести программу из переднего плана на задний?
1. Как получить список заданий на заднем плане и их состояние.
1. Как перевести задание из заднего плана на передний?
1. Какая связь между сочетаниями клавиш `Ctrl-C`, `Ctrl-Z` и `Ctrl-\` и сигналами?
1. Что делает утилита `kill`?
   Запустите на заднем плане задание `sleep 1h` и завершите его поочередно отправкой сигналов `-SIGINT`, `-SIGKILL`, `-SIGQUIT`.
   Чем отличаются сообщения командной оболочки на выходе? <!-- Прерывание, Убито, Выход (образ памяти сброшен на диск) -->
1. Создайте файл размером 1 Гб командой `dd if=/dev/urandom of=bigfile.bin bs=10MiB count=100`.
   Сожмите файл утилитой `bzip2` с опцией `-9` (максимальный уровень сжатия) и сохранив оригинал (опция `--keep`).
   Запустите последнюю команду на заднем плане.
   Проконтролируйте, как долго выполнится задание.

