У цьому розділі ми обговоримо наступні теми:

  • Наявні сигнали.
  • Використання сигналів.
  • Використання виразу trap.
  • Як запобігти припиненню роботи програми іншими користувачами.

Сигнали

Вступ

Сторінка посібника сигналів

У вашій системі повинна бути сторінка посібника, де перелічено всі доступні системні сигнали. На більшості систем Linux це man 7 signal. Якщо ви маєте труднощі з пошуком сторінки або розділу, виконайте

man -k signal | grep list

або

apropos signal | grep list

Назви сигналів можна знайти, використавши kill -l.

Доступні оболонці Bash сигнали

Якщо не поставлено жодної пастки (що це — описується нижче), інтерактивна оболонка нехтує сигнали SIGTERM та SIGQUIT. SIGINT перехоплюється й обробляється. Якщо в дії керування завданнями, SIGTTIN, SIGTTOU та SIGTSTP також нехтуються. Команди, запущені в результаті заміни команд, також ігнорують ці сигнали, якщо останні спричинено клавіатурною комбінацією.

Типово сигнал SIGHUP завершує роботу оболонки. Інтерактивна оболонка надішле SIGHUP всім своїм запущеним або призупиненим завданням (подивіться документацію щодо вбудованої команди disown, якщо хочете вимкнути цю поведінку для певних процесів). Для завершення процесів по отриманні сигналу SIGHUP використовуйте ключ huponexit вбудованої команди shopt.

Посилання сигналів за допомогою оболонки

Оболонка може надсилати наступні сигнали:

[Таблиця 12-1. Сигнали керування в Bash.] | Стандартна комбінація клавіш | Що це означає | | :--------------------------- | :---------------------------------------------------------- | | Ctrl+C | Сигнал переривання, посилає SIGINT завданню, що виконується на передньому плані | Ctrl+Y | Знак відкладеного призупинення. Спричиняє до зупинки запущеного процесу, якщо той намагається прочитати ввід з терміналу. Контроль передається оболонці. Користувач може зробити процес пріоритетним, фоновим або знищити його. Призупинення може використовуватись лише на системах, які підтримують цю властивість. | Ctrl+Z | Сигнал призупинення, посилає SIGTSTP запущеній програмі, зупиняючи її та передаючи контроль оболонці.

Підказка: Налаштунки термінала

Перевірте за допомогою stty ваші налаштунки. Зависання та відновлення, як правило, унеможливлені за використання «сучасних» емуляторів термінала. В стандартному ?xterm підтримка Ctrl+S та Ctrl+Q є усталеною.

Використання сигналів із kill командою.

Більшість сучасних оболонок мають вбудовану функцію kill. Не є винятком і Bash. Її ключами можуть бути як номери, так і назви сигналів, а аргументами можуть бути ідентифікатори завдань або процесів. Про стан виходу може бути повідомлено, якщо додати ключ -l (нуль, якщо щонайменше один сигнал було успішно послано; ненульове значення, якщо сталася помилка).

Використовуючи команду kill із каталога /usr/bin (/bin), ви можете скористатись із додаткових можливостей, наприклад, знищувати процеси, що не належать вам, або вказувати назви процесів як, скажімо, у pgrep або pkill.

Обидві команди kill посилають сигнал TERM, якщо інше не вказано.

Ось список найпоширеніших сигналів:

[ Таблиця 12-2. Поширені kill сигнали.] | Назва сигналу | Числове значення | Ефект | | :-------------------------- | :-------------------------------- | :----------------------------------------------- | | SIGHUP | 1 | Зависання | | SIGINT | 2 | Переривання з клавіатури | | SIGKILL | 9 | Сигнал знищення | | SIGTERM | 15 | Сигнал завершення | | SIGSTOP | 17,19,23 | Призупинення процесу |

Зауваження: SIGKILL та SIGSTOP

SIGKILL та SIGSTOP не можна перехопити, заблокувати чи знехтувати.

Вбиваючи процес чи ряд процесів, є сенс спершу робити це з допомогою найбезпечнішого сигналу — SIGTERM. Тоді програми, котрі турбуються про своє правильне завершення, отримують нагоду виконати процедури, котрі передбачено на випадок отримання SIGTERM — як-от прибрати за собою й закрити відкриті файли. Посилаючи ж процесові SIGKILL, ви не даєте йому жодної можливості завершитись акуратно, а це може мати неприємні наслідки.

Бувають, одначе, випадки, коли чисте завершення роботи не спрацьовує, й у нас залишається лише один вихід — використати сигнали INT або KILL. Наприклад, якщо процес не гине від клавіатурної комбінації Ctrl+C, найкраще застосувати до ідентифікатора цього процесу команду kill -9:

maud: ~> **ps -ef | grep stuck_process**
maud    5607   2214  0 20:05 pts/5    00:00:02 stuck_process

maud: ~> kill -9 5607

maud: ~> **ps -ef | grep stuck_process**
maud    5614    2214 0 20:15 pts/5    00:00:00 grep stuck_process
[1]+ Killed             stuck_process

Коли процес має декілька власних копій, можна застосувати killall. Команда killall cприймає ті самі ключі, що й kill, але застосовує їх на всі копії заданого процесу. Перевірте killall як слід, перш ніж використовувати її в робочому середовищі.

Traps (капкани)

Вступ

Буває, що ви не хочете, аби користувачі вашого сценарію могли припинити його роботу, використавши якусь комбінацію клавіш. Так, наприклад, це буває потрібно, коли необхідно отримати ввід чи провести чистку перед завершенням. Використання виразу trap дозволяє вловлювати ці комбінації клавіш і може бути запрограмований на виконання певного списку команд при отриманні сигналу припинення роботи.

Синтаксис виразу trap є досить очевидним:

trap [КОМАНДИ] [СИГНАЛИ]

Це вказує команді trap перехопити перелічені СИГНАЛИ, які можуть бути у формі назв сигналів (префікс SIG- можна опустити) або їх номерів. Якщо сигналом є 0 або EXIT, то КОМАНДИ виконуються під час виходу з оболонки. Якщо одним із сигналів є DEBUG, то список КОМАНД виконується після кожної звичайної команди. Як назву сигналу можна вказати ERR — тоді КОМАНДИ виконуються щоразу, коли звичайні команди завершуються з ненульовим станом завершення. Зауважте, що це не стосується ненульового статусу виходу з виразу if, або циклів while та until, а також логічних AND (&&) чи OR (||), або ж коли стан команд інвертовано оператором !.

Сам по собі статус завершення trap команди буде нульовим, хіба що подано недійсний вказівник сигналу. Команда trap приймає декілька ключів, що описані у сторінках info Bash.

Ось доволі простий приклад перехоплення користувацького Ctrl+C, після чого виводиться повідомлення. Якщо ви намагатиметесь припинити роботу цього сценарію без вказування сигналу KILL, нічого не станеться:

#!/bin/bash
# traptest.sh

trap "echo Booh!" SIGINT SIGTERM
echo "pid is $$"

while :                # Те саме, що й "while true".
do
       sleep 60        # Цей сценарій насправді нічого не робить.
done

Як Bash тлумачить trap

Отримавши сигнал, для якого заздалегідь заготовлено пастку, Bash не виконуватиме trap до того, як звичайна команда завершить свою дію. Якщо Bash очікує виконання асинхронної команди за допомогою вбудованої функції wait, отримання сигналу, на який приготовано пастку, викличе негайне завершення wait зі статусом, більшим за 128; після цього відразу ж виконається trap.

Ще приклади

Виявлення використання змінної

При відлагодженні довших сценаріїв, можливе надання змінній trace атрибуту і таким чином вловлювати DEBUG повідомлення щодо неї. Як правило, ви надаєте значення змінній таким чином: ЗМІННА=значення. Заміна цього виразу тим що наведено нижче дозволяє отримати цінну інформацію про те що саме відбувається у вашому сценарію:

declare -t VARIABLE=value

trap "echo VARIABLE is being used here." DEBUG

# решта сценарію

Очищення від мотлоху під час виходу

Команда whatis залежить від бази даних, яка постійно поновлюється завдяки сценарію cron makewhatis.cron:

#!/bin/bash

LOCKFILE=/var/lock/makewhatis.lock

# Попередній makewhatis повинен бути завершеним успішно:

[ -f $LOCKFILE ] && exit 0

# При виході видалити lockfile.

trap "{ rm -f $LOCKFILE ; exit 255; }" EXIT

touch $LOCKFILE
makewhatis -u -w
exit 0

Підсумок

За допомогою клавіатурних комбінацій та команди kill вашим програмам можна надсилати сигнали. Ці сигнали можуть бути перехоплені; після цього виконується дія, приписана виразом trap.

Деякі програми не зважають на сигнали. Єдиним сигналом, котрий не може знехтувати жодна програма, є KILL.

Вправи

Декілька практичних прикладів:

  1. Створіть сценарій, який записує завантажувальний образ на дискету командою dd. Якщо користувач намагатиметься припинити виконання сценарію, з'являтиметься повідомлення про те, що ця дія зробить дискету непридатною до використання.
  2. Напишіть сценарій, який автоматизуватиме встановлення якогось пакету — на ваш вибір. Пакет повинен завантажуватися з Інтернету. Його необхідно розпакувати та скомпілювати, якщо ці дії необхідні. З усіх цих дій перервати можна лише власне встановлення.