ОП.05Процессы: PID, состояния и сигналызанятие 7Материалы

Часть 5 из 7

Сигналы: kill, остановка и продолжение

В этой части разберём, что такое сигнал, какие сигналы нужны администратору, как посылать их командой kill и почему процесс может перехватить TERM, но не KILL.

  1. 5.1 Что такое сигнал
  2. 5.2 Команда kill
  3. 5.3 Остановка и продолжение
  4. 5.4 Процесс перехватывает TERM

5.1 Что такое сигнал

Сигнал — короткое уведомление, которое ядро доставляет процессу. У сигнала нет содержимого, только номер и имя. Получив сигнал, процесс выполняет действие по умолчанию для этого сигнала: завершается, останавливается или продолжает работу. Программа может заранее назначить свой обработчик — тогда вместо действия по умолчанию выполняется её код. Исключение — сигналы KILL и STOP: их перехватить нельзя, ядро выполняет их само.

Сигналы похожи на сигналы светофора и регулировщика для водителя. Жёлтый свет (TERM) — просьба остановиться: водитель сам решает, как плавно затормозить, и может доехать до перекрёстка. Перекрытая шлагбаумом дорога (KILL) — выбора нет. Жест «стой» и «проезжай» (STOP и CONT) — пауза и продолжение движения без съезда с дороги.

СигналНомерКто посылаетДействие по умолчанию
HUP1терминал при закрытии окназавершить; службы часто перечитывают по нему настройки
INT2Ctrl+Cпрервать процесс
KILL9kill -9завершить немедленно; перехватить нельзя
TERM15kill без ключазавершить; программа может сначала сохранить данные
CONT18kill -CONT, fg, bgпродолжить остановленный процесс
STOP19kill -STOPостановить; перехватить нельзя
TSTP20Ctrl+Zостановить; программа может перехватить

Номера сигналов STOP, CONT и TSTP на разных процессорах разные, поэтому в командах сигналы пишут по имени: kill -STOP.

5.2 Команда kill

Команда kill PID посылает процессу сигнал TERM. С ключом посылается другой сигнал: kill -9 PID или kill -KILL PID, kill -STOP PID. Вместо PID можно указать номер задания: kill %1. Команда kill -l выводит список всех сигналов с номерами.

Посылать сигналы можно только своим процессам; процессам других пользователей — только root. Поэтому kill 1 от имени ubuntu завершается ошибкой Operation not permitted.

Порядок завершения программы такой: сначала TERM, несколько секунд ожидания, и только если процесс не завершился — KILL. Программа, получившая TERM, успевает записать данные на диск, закрыть соединения и удалить временные файлы. KILL снимает процесс сразу: незаписанные данные теряются, временные файлы остаются.

В терминалеTERM, KILL и чужой процесс
  1. kill -l | head -3# первые 15 сигналов
    что делает

    Что делает команда. Выводит первые три строки списка сигналов: сигналы 1–15 с номерами.

    По частям:

    kill
    послать процессу сигнал
    -l
    вывести список сигналов с номерами
    |
    передать вывод левой команды на вход следующей
    head
    оставить только первые строки
    -3
    сколько первых строк оставить: 3
  2. sleep 900 &
    что делает

    Что делает команда. Запускает в фоне процесс для опыта с TERM.

    По частям:

    sleep
    ничего не делать указанное число секунд
    900
    сколько секунд
    &
    запустить команду слева в фоне и сразу продолжить
  3. kill $!; sleep 0.5# TERM
    что делает

    Что делает команда. Посылает последнему фоновому процессу TERM и полсекунды ждёт, чтобы оболочка успела сообщить о завершении.

    По частям:

    kill
    послать процессу сигнал
    $!
    какому процессу, PID последнего процесса, запущенного в фоне
    ;
    разделитель: выполнить следующую команду после этой
    sleep
    ничего не делать указанное число секунд
    0.5
    сколько секунд
  4. sleep 901 &
    что делает

    Что делает команда. Запускает в фоне процесс для опыта с KILL.

    По частям:

    sleep
    ничего не делать указанное число секунд
    901
    сколько секунд
    &
    запустить команду слева в фоне и сразу продолжить
  5. kill -9 $!; sleep 0.5# KILL
    что делает

    Что делает команда. Посылает последнему фоновому процессу KILL и полсекунды ждёт.

    По частям:

    kill
    послать процессу сигнал
    -9
    послать сигнал 9 — KILL: завершить немедленно
    $!
    какому процессу, PID последнего процесса, запущенного в фоне
    ;
    разделитель: выполнить следующую команду после этой
    sleep
    ничего не делать указанное число секунд
    0.5
    сколько секунд
  6. kill 1# процесс root
    что делает

    Что делает команда. Пытается послать TERM процессу с PID 1, который принадлежит root.

    По частям:

    kill
    послать процессу сигнал
    1
    какому процессу, PID 1 — первый процесс системы, systemd
Результат

после TERM оболочка сообщает Terminated, после KILL — Killed; сигнал процессу root обычный пользователь послать не может.

Снимок 5.1Ubuntu 24.04
  1. 19 — KILL
  2. 215 — TERM
  3. 3TERM: процесс завершён
  4. 4KILL: процесс снят
  5. 5процессы других пользователей недоступны
Список сигналов, завершение сигналами TERM и KILL, отказ для чужого процесса.Весь снимок ↗

Список сигналов, завершение сигналами TERM и KILL, отказ для чужого процесса.

Что показывает снимок. kill -l выводит сигналы с номерами: 9 — KILL, 15 — TERM; после TERM оболочка сообщила Terminated, после KILL — Killed; процесс root обычному пользователю недоступен.

Где это пригодится. Зависшую программу завершают сначала командой kill без ключа и только если она не реагирует — kill -9.

5.3 Остановка и продолжение

kill -STOP останавливает процесс так же, как Ctrl+Z, но работает для любого своего процесса, в том числе запущенного в фоне или из другого терминала. kill -CONT продолжает его. Так приостанавливают программу, которая заняла процессор, чтобы разобраться, что с ней, и не потерять её работу.

Столбец %CPU в ps показывает среднюю загрузку за всё время жизни процесса: процессорное время, делённое на время работы. Поэтому у только что остановленного процесса %CPU уменьшается постепенно.

В терминалеSTOP и CONT
  1. yes > /dev/null &# процесс, занимающий процессор
    что делает

    Что делает команда. Запускает в фоне процесс, который бесконечно пишет строку y в устройство-«корзину»; он всё время занят.

    По частям:

    yes
    бесконечно печатать строку y
    >
    записать вывод в файл
    /dev/null
    устройство, которое выбрасывает всё записанное
    &
    запустить команду слева в фоне и сразу продолжить
  2. ps -o pid,stat,%cpu,cmd -p $!
    что делает

    Что делает команда. Выводит номер, состояние, загрузку процессора и команду последнего фонового процесса.

    По частям:

    ps
    вывести сведения о процессах
    -o pid,stat,%cpu,cmd
    выводить столбцы pid,stat,%cpu,cmd
    -p $!
    выбрать процесс с PID $!
  3. kill -STOP $!# остановить
    что делает

    Что делает команда. Останавливает последний фоновый процесс.

    По частям:

    kill
    послать процессу сигнал
    -STOP
    послать сигнал STOP: остановить процесс
    $!
    какому процессу, PID последнего процесса, запущенного в фоне
  4. ps -o pid,stat,%cpu,cmd -p $!
    что делает

    Что делает команда. Выводит номер, состояние, загрузку процессора и команду последнего фонового процесса.

    По частям:

    ps
    вывести сведения о процессах
    -o pid,stat,%cpu,cmd
    выводить столбцы pid,stat,%cpu,cmd
    -p $!
    выбрать процесс с PID $!
  5. kill -CONT $!# продолжить
    что делает

    Что делает команда. Продолжает остановленный процесс.

    По частям:

    kill
    послать процессу сигнал
    -CONT
    послать сигнал CONT: продолжить остановленный процесс
    $!
    какому процессу, PID последнего процесса, запущенного в фоне
  6. ps -o pid,stat,%cpu,cmd -p $!
    что делает

    Что делает команда. Выводит номер, состояние, загрузку процессора и команду последнего фонового процесса.

    По частям:

    ps
    вывести сведения о процессах
    -o pid,stat,%cpu,cmd
    выводить столбцы pid,stat,%cpu,cmd
    -p $!
    выбрать процесс с PID $!
  7. kill $!; sleep 0.5# завершить
    что делает

    Что делает команда. Посылает последнему фоновому процессу TERM и полсекунды ждёт, чтобы оболочка успела сообщить о завершении.

    По частям:

    kill
    послать процессу сигнал
    $!
    какому процессу, PID последнего процесса, запущенного в фоне
    ;
    разделитель: выполнить следующую команду после этой
    sleep
    ничего не делать указанное число секунд
    0.5
    сколько секунд
  8. pgrep -a yes# процесса нет
    что делает

    Что делает команда. Ищет процессы yes; пустой вывод — процессов нет.

    По частям:

    pgrep
    найти процессы и вывести их PID
    -a
    выводить вместе с PID командную строку
    yes
    образец имени
Результат

после STOP состояние T, после CONT снова R; %CPU меняется постепенно, потому что считается за всё время жизни процесса.

Снимок 5.2Ubuntu 24.04
  1. 1R: yes занимает процессор
  2. 2оболочка заметила остановку своего задания
  3. 3T: остановлен; %CPU — среднее за жизнь, падает постепенно
  4. 4R: продолжил работу после CONT
  5. 5TERM завершил процесс
Процесс yes в состояниях R, T и снова R.Весь снимок ↗

Процесс yes в состояниях R, T и снова R.

Что показывает снимок. yes в состоянии R занимал 99 % процессора, после STOP стал T, после CONT снова R; %CPU снижается постепенно, потому что считается за всё время жизни.

Где это пригодится. STOP приостанавливает тяжёлую задачу, например резервное копирование в рабочее время, а CONT продолжает её без потери сделанного.

5.4 Процесс перехватывает TERM

Обработчик сигнала в bash задаёт встроенная команда trap: trap "команды" TERM выполняет команды при получении TERM вместо завершения. В опыте процесс на TERM печатает сообщение и продолжает работать. Против KILL обработчик не помогает: этот сигнал до процесса не доходит, ядро снимает процесс само.

Так устроены службы: на TERM веб-сервер дорабатывает текущие запросы и только потом выходит, база данных записывает данные на диск. Если служба зависла и на TERM не реагирует, администратор через несколько секунд посылает KILL.

В терминалеtrap и KILL
  1. bash -c 'trap "echo got TERM, keep working" TERM; while true; do sleep 1; done' &# перехватывает TERM
    что делает

    Что делает команда. Запускает в фоне оболочку с обработчиком TERM: на TERM она печатает строку и работает дальше.

    По частям:

    bash -c '…'
    запустить bash и выполнить команды из строки
    trap "echo got TERM, keep working" TERM
    обработчик: при сигнале TERM напечатать строку и продолжить работу
    while true; do sleep 1; done
    бесконечный цикл: спать по секунде
    &
    запустить в фоне
  2. kill $!; sleep 1.5# TERM
    что делает

    Что делает команда. Посылает TERM и ждёт полторы секунды, чтобы обработчик успел сработать.

    По частям:

    kill
    послать процессу сигнал
    $!
    какому процессу, PID последнего процесса, запущенного в фоне
    ;
    разделитель: выполнить следующую команду после этой
    sleep
    ничего не делать указанное число секунд
    1.5
    сколько секунд
  3. ps -o pid,stat,cmd -p $!# процесс жив
    что делает

    Что делает команда. Выводит номер, состояние и команду последнего фонового процесса, если он ещё есть.

    По частям:

    ps
    вывести сведения о процессах
    -o pid,stat,cmd
    выводить столбцы pid,stat,cmd
    -p $!
    выбрать процесс с PID $!
  4. kill -9 $!; sleep 0.5# KILL
    что делает

    Что делает команда. Посылает последнему фоновому процессу KILL и полсекунды ждёт.

    По частям:

    kill
    послать процессу сигнал
    -9
    послать сигнал 9 — KILL: завершить немедленно
    $!
    какому процессу, PID последнего процесса, запущенного в фоне
    ;
    разделитель: выполнить следующую команду после этой
    sleep
    ничего не делать указанное число секунд
    0.5
    сколько секунд
  5. ps -o pid,stat,cmd -p $!# процесса нет
    что делает

    Что делает команда. Выводит номер, состояние и команду последнего фонового процесса, если он ещё есть.

    По частям:

    ps
    вывести сведения о процессах
    -o pid,stat,cmd
    выводить столбцы pid,stat,cmd
    -p $!
    выбрать процесс с PID $!
Результат

на TERM процесс печатает got TERM, keep working и продолжает работать; KILL завершает его.

Снимок 5.3Ubuntu 24.04
  1. 1обработчик TERM выполнен, процесс работает дальше
  2. 2процесс жив после TERM
  3. 3KILL перехватить нельзя
  4. 4процесса больше нет
Процесс перехватывает TERM, но не KILL.Весь снимок ↗

Процесс перехватывает TERM, но не KILL.

Что показывает снимок. На TERM процесс выполнил обработчик, напечатал строку и остался в состоянии S; KILL снял его, и ps больше ничего не находит.

Где это пригодится. Так ведут себя службы: на TERM аккуратно закрывают соединения и файлы. Если служба зависла и TERM не помог, администратор посылает KILL.

Для поиска процессов по имени есть пара команд: pgrep находит PID, pkill посылает сигнал всем найденным процессам. Ключ -f ищет образец во всей командной строке процесса, включая аргументы: pkill -f backup.sh завершит процесс bash backup.sh.

Итоги части 5

  • Сигнал — уведомление процессу; на него процесс завершается, останавливается или выполняет свой обработчик.
  • kill PID посылает TERM, kill -9 PID — KILL, kill -STOP и kill -CONT — паузу и продолжение.
  • TERM программа может перехватить и завершиться аккуратно; KILL и STOP перехватить нельзя.
  • Порядок завершения: TERM, ожидание, и только потом KILL.
  • Своим процессам сигналы посылает пользователь, чужим — только root.

В отчётСнимок STOP и CONT и снимок с trap: почему процесс пережил TERM и не пережил KILL.

Открыть в отдельной вкладке ↗