Как использовать сигналы Linux в сценариях Bash
Ядро Linux посылает процессам сигналы о событиях, на которые им нужно реагировать. Хорошо себя зарекомендовавшие скрипты элегантно и надежно обрабатывают сигналы и могут очистить себя, даже если вы нажмете Ctrl+C. Вот как.
Сигналы и процессы
Сигналы — это короткие, быстрые односторонние сообщения, отправляемые таким процессам, как сценарии, программы и демоны. Они сообщают процессу о том, что произошло. Возможно, пользователь нажал Ctrl+C, или приложение могло попытаться записать в память, к которой у него нет доступа.
Программы для Windows, мобильные приложения, игры - ВСЁ БЕСПЛАТНО, в нашем закрытом телеграмм канале - Подписывайтесь:)
Если автор процесса ожидал, что ему может быть отправлен определенный сигнал, он может написать в программе или сценарии подпрограмму для обработки этого сигнала. Такой режим называется обработчик сигналов. Он ловит или перехватывает сигнал и выполняет какое-то действие в ответ на него.
Как мы увидим, Linux использует множество сигналов, но с точки зрения сценариев существует лишь небольшое подмножество сигналов, которые могут вас заинтересовать. В частности, в нетривиальных сценариях сигналы, сообщающие сценарий для завершения работы должен быть перехвачен (где это возможно) и выполнено корректное завершение работы.
Например, сценариям, которые создают временные файлы или открывают порты брандмауэра, можно дать возможность удалить временные файлы или закрыть порты до их закрытия. Если сценарий просто умирает в момент получения сигнала, ваш компьютер может остаться в непредсказуемом состоянии.
Вот как вы можете обрабатывать сигналы в своих сценариях.
Знакомьтесь с сигналами
Некоторые команды Linux имеют загадочные имена. Не так команда, которая перехватывает сигналы. Это называется ловушка. Мы также можем использовать trap с параметром -l (список), чтобы показать нам весь список сигналов, которые использует Linux.
ловушка -л
Хотя наш пронумерованный список заканчивается на 64, на самом деле сигналов 62. Сигналы 32 и 33 отсутствуют. Они не реализованы в Linux. Они были заменены функциями компилятора gcc для обработки потоков в реальном времени. Все, от сигнала 34, SIGRTMIN, до сигнала 64, SIGRTMAX, является сигналом реального времени.
Вы увидите разные списки в разных Unix-подобных операционных системах. Например, на OpenIndiana присутствуют сигналы 32 и 33, а также множество дополнительных сигналов, в результате чего общее количество сигналов достигает 73.
На сигналы можно ссылаться по имени, номеру или сокращенному имени. Их сокращенное имя — это просто их имя с удаленным начальным «SIG».
Сигналы поднимаются по разным причинам. Если вы можете их расшифровать, их назначение содержится в их названии. Воздействие сигнала относится к одной из нескольких категорий:
- Завершить: процесс завершен.
- Игнорировать: сигнал не влияет на процесс. Это только информационный сигнал.
- Ядро: создается файл дампа ядра. Обычно это делается из-за того, что процесс каким-то образом нарушился, например, из-за нарушения памяти.
- Стоп: процесс остановлен. То есть это приостановленоне прекращено.
- Продолжить: говорит остановленному процессу продолжить выполнение.
Это сигналы, с которыми вы будете сталкиваться чаще всего.
- SIGHUP: Сигнал 1. Соединение с удаленным хостом, например сервером SSH, неожиданно прервано или пользователь вышел из системы. Сценарий, получивший этот сигнал, может корректно завершить работу или попытаться повторно подключиться к удаленному хосту.
- SIGINT: Сигнал 2. Пользователь нажал комбинацию Ctrl+C, чтобы принудительно закрыть процесс, или команда kill была использована с сигналом 2. Технически это сигнал прерывания, а не сигнал завершения, а прерванный сценарий без обработчик сигнала обычно завершается.
- SIGQUIT: Сигнал 3. Пользователь нажал комбинацию Ctrl+D, чтобы принудительно завершить процесс, или с сигналом 3 была использована команда kill.
- SIGFPE: Сигнал 8. Процесс пытался выполнить недопустимую (невозможную) математическую операцию, такую как деление на ноль.
- SIGKILL: Сигнал 9. Это эквивалент сигнала гильотины. Вы не можете это поймать или проигнорировать, и это происходит мгновенно. Процесс прекращается немедленно.
- SIGTERM: Сигнал 15. Это более продуманная версия SIGKILL. SIGTERM также сообщает процессу о завершении, но он может быть перехвачен, и процесс может запустить свои процессы очистки перед закрытием. Это обеспечивает плавное завершение работы. Это сигнал по умолчанию, выдаваемый командой kill.
Сигналы в командной строке
Одним из способов перехвата сигнала является использование перехвата с номером или именем сигнала и ответом, который вы хотите получить, если сигнал получен. Мы можем продемонстрировать это в окне терминала.
Эта команда перехватывает сигнал SIGINT. Ответом является печать строки текста в окне терминала. Мы используем параметр -e (включить escape-последовательности) с эхом, поэтому мы можем использовать спецификатор формата «\n».
trap ‘echo -e “Обнаружен +c.”‘ SIGINT
Наша строка текста печатается каждый раз, когда мы нажимаем комбинацию Ctrl+C.
Чтобы узнать, установлена ли ловушка для сигнала, используйте параметр -p (вывод ловушки).
ловушка -p СИГНАЛ
Использование trap без опций делает то же самое.
Чтобы сбросить сигнал в нормальное состояние без захвата, используйте дефис «-» и имя захваченного сигнала.
trap – SIGINT trap -p SIGINT
Отсутствие вывода команды trap -p означает, что для этого сигнала не установлено прерывание.
Перехват сигналов в скриптах
Мы можем использовать ту же команду ловушки общего формата внутри скрипта. Этот сценарий перехватывает три разных сигнала: SIGINT, SIGQUIT и SIGTERM.
#!/bin/bash trap “echo Я был прерван SIGINT; выход” SIGINT trap “echo Я был прерван SIGQUIT; выход” SIGQUIT trap “echo Я был прерван SIGTERM; выход” SIGTERM echo $$ counter=0 while true do echo ” Номер цикла: “$((++counter)) sleep 1 выполнено
Три оператора-ловушки находятся в верхней части скрипта. Обратите внимание, что мы включили команду выхода в ответ на каждый из сигналов. Это означает, что скрипт реагирует на сигнал и затем завершает работу.
Скопируйте текст в свой редактор и сохраните его в файле с именем «simple-loop.sh» и сделайте его исполняемым с помощью команды chmod. Вам нужно будет сделать это со всеми сценариями в этой статье, если вы хотите следовать на своем компьютере. Просто используйте имя соответствующего скрипта в каждом случае.
chmod +x простой цикл.sh
Остальная часть скрипта очень проста. Нам нужно знать идентификатор процесса скрипта, поэтому у нас есть скрипт, который повторяет это для нас. Переменная $$ содержит идентификатор процесса сценария.
Мы создаем переменную с именем counter и устанавливаем ее в ноль.
Цикл while будет работать вечно, если его не остановить принудительно. Он увеличивает переменную счетчика, выводит ее на экран и на секунду засыпает.
Запустим скрипт и отправим ему разные сигналы.
./simple-loop.sh
Когда мы нажимаем «Ctrl + C», наше сообщение печатается в окне терминала, и скрипт завершается.
Давайте снова запустим его и отправим сигнал SIGQUIT с помощью команды kill. Нам нужно будет сделать это из другого окна терминала. Вам нужно будет использовать идентификатор процесса, который сообщил ваш собственный скрипт.
./simple-loop.sh убить -SIGQUIT 4575
Как и ожидалось, скрипт сообщает о поступлении сигнала, а затем завершает работу. И, наконец, чтобы доказать это, мы снова проделаем это с сигналом SIGTERM.
./simple-loop.sh убить -SIGTERM 4584
Мы убедились, что можем перехватывать несколько сигналов в сценарии и реагировать на каждый из них независимо. Шаг, который превращает все это из интересного в полезное, — это добавление обработчиков сигналов.
Обработка сигналов в скриптах
Мы можем заменить строку ответа на имя функции в вашем скрипте. Затем команда trap вызывает эту функцию при обнаружении сигнала.
Скопируйте этот текст в редактор и сохраните его как файл с именем «grace.sh» и сделайте его исполняемым с помощью chmod.
#!/bin/bash trap graceful_shutdown SIGINT SIGQUIT SIGTERM graceful_shutdown() { echo -e “\nУдаление временного файла:” $temp_file rm -rf “$temp_file” exit } temp_file=$(mktemp -p /tmp tmp.XXXXXXXXXX) echo “Создан временный файл:” $temp_file counter=0 пока true do echo “Номер цикла:” $((++counter)) sleep 1 done
Сценарий устанавливает ловушку для трех разных сигналов — SIGHUP, SIGINT и SIGTERM — с помощью одного оператора ловушки. Ответ — это имя функции graceful_shutdown(). Функция вызывается всякий раз, когда принимается один из трех захваченных сигналов.
Сценарий создает временный файл в каталоге «/tmp», используя mktemp. Шаблон имени файла — «tmp.XXXXXXXXXX», поэтому имя файла будет «tmp». за которыми следуют десять случайных буквенно-цифровых символов. Имя файла повторяется на экране.
Остальная часть скрипта такая же, как и предыдущая, с переменной-счетчиком и бесконечным циклом while.
./grace.sh
Когда файлу отправляется сигнал, вызывающий его закрытие, вызывается функция graceful_shutdown(). Это удалит наш единственный временный файл. В реальной ситуации он может выполнить любую очистку, которую требует ваш скрипт.
Кроме того, мы объединили все наши захваченные сигналы вместе и обработали их с помощью одной функции. Вы можете перехватывать сигналы по отдельности и отправлять их в собственные выделенные функции обработчика.
Скопируйте этот текст и сохраните его в файле с именем «triple.sh» и сделайте его исполняемым с помощью команды chmod.
#!/bin/bash trap sigint_handler trap SIGINT sigusr1_handler SIGUSR1 trap exit_handler EXIT function sigint_handler() { ((++sigint_count)) echo -e “\nSIGINT получил $sigint_count раз(а).” если [[ “$sigint_count” -eq 3 ]]; затем повторите “Начало закрытия”. loop_flag=1 fi } function sigusr1_handler() { echo “SIGUSR1 отправил и получил $((++sigusr1_count)) раз(а)”. } function exit_handler() { echo “Обработчик выхода: скрипт закрывается…” } echo $$ sigusr1_count=0 sigint_count=0 loop_flag=0 while [[ $loop_flag -eq 0 ]]; убить -SIGUSR1 $$ sleep 1 готово
Мы определяем три ловушки в верхней части скрипта.
- Один перехватывает SIGINT и имеет обработчик sigint_handler().
- Второй перехватывает сигнал с именем SIGUSR1 и использует обработчик с именем sigusr1_handler().
- Ловушка номер три перехватывает сигнал ВЫХОД. Этот сигнал поднимается самим скриптом при его закрытии. Установка обработчика сигнала для EXIT означает, что вы можете установить функцию, которая всегда будет вызываться при завершении сценария (если только он не будет уничтожен сигналом SIGKILL). Наш обработчик называется exit_handler().
SIGUSR1 и SIGUSR2 — это сигналы, предназначенные для того, чтобы вы могли отправлять собственные сигналы в свои скрипты. То, как вы интерпретируете и реагируете на них, полностью зависит от вас.
Оставив пока в стороне обработчики сигналов, тело скрипта должно быть вам знакомо. Он выводит идентификатор процесса в окно терминала и создает некоторые переменные. Переменная sigusr1_count записывает, сколько раз обрабатывался SIGUSR1, а sigint_count записывает, сколько раз обрабатывался SIGINT. Переменная loop_flag устанавливается равной нулю.
Цикл while не является бесконечным циклом. Цикл остановится, если для переменной loop_flag установлено любое ненулевое значение. Каждое вращение цикла while использует kill для отправки сигнала SIGUSR1 этому сценарию, отправляя его идентификатору процесса сценария. Скрипты могут посылать сигналы сами себе!
Функция sigusr1_handler() увеличивает переменную sigusr1_count и отправляет сообщение в окно терминала.
Каждый раз, когда принимается сигнал SIGINT, функция siguint_handler() увеличивает переменную sigint_count и выводит ее значение в окно терминала.
Если переменная sigint_count равна трем, переменная loop_flag устанавливается равной единице, и в окно терминала отправляется сообщение, информирующее пользователя о начале процесса завершения работы.
Поскольку loop_flag больше не равен нулю, цикл while завершается, и сценарий завершается. Но это действие автоматически вызывает сигнал EXIT и вызывается функция exit_handler().
./тройной.ш
После трех нажатий Ctrl+C скрипт завершает работу и автоматически вызывает функцию exit_handler().
Читайте сигналы
Перехватывая сигналы и обрабатывая их в простых функциях-обработчиках, вы можете сделать так, чтобы ваши сценарии Bash убирались за собой, даже если они были неожиданно завершены. Это дает вам более чистую файловую систему. Это также предотвращает нестабильность при следующем запуске сценария и — в зависимости от цели вашего сценария — может даже предотвратить дыры в безопасности.
Программы для Windows, мобильные приложения, игры - ВСЁ БЕСПЛАТНО, в нашем закрытом телеграмм канале - Подписывайтесь:)