Документация по ОС FreeBSD Пятница, 19.04.2024, 02:03
Приветствую Вас Гость | RSS
Меню сайта

Категории каталога
Shell [40]

Главная » Статьи » Программирование » Shell

Обработка сигналов в сценариях оболочки [2010]

Сигналы — это числовые сообщения, отправляемые запущенным приложениям операционной системой, другими приложениями или пользователем. Сигналы, как правило, ожидают от приложения какой-то определённой реакции, например «корректно заверши работу», «приостановись, чтобы я мог тебя перевести в фоновый режим» или «умри!».

Обычно для отправки сигналов приложениям используется программа kill, но некоторые сигналы можно отправлять и при помощи клавиатурных комбинаций, например таких, как знакомые многим  Ctrl+C или Ctrl+Z.

Сигналы обрабатываются «каскадно». То есть, сигнал отправляется приложению и, если приложение не обработало поступивший сигнал, то он возвращается обратно оболочке или операционной системе. Некоторые типы сигналов в принципе не могут обрабатываться приложениями. Например, сигнал SIGKILL вообще не доставляется приложению, а перехватывается операционной системой, которая немедленно завершает работу приложения, которому сигнал был адресован.

Давайте начнём изучение сигналов с того, что узнаем, какие они бывают. Для этого можно воспользоваться командой kill -l:

$ kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL
5) SIGTRAP 6) SIGABRT 7) SIGEMT 8) SIGFPE
9) SIGKILL 10) SIGBUS 11) SIGSEGV 12) SIGSYS
13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGURG
17) SIGSTOP 18) SIGTSTP 19) SIGCONT 20) SIGCHLD
21) SIGTTIN 22) SIGTTOU 23) SIGIO 24) SIGXCPU
25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH
29) SIGINFO 30) SIGUSR1 31) SIGUSR2

Большинство перечисленных сигналов нам неинтересны и используются редко. Среди часто используемых я бы отметил следующие. SIGHUP — обычно отправляется в момент выхода пользователя из системы; SIGINT (именно он посылается приложению при нажатии Ctrl+C) - запрос прервать работу; SIGKILL — немедленное завершение работы процесса операционной системой; SIGTSTP — отправляется комбинацией Ctrl+Z; SIGCONT — это тот сигнал, который отправляется приложению из оболочки командами fg и bg после приостановки его работы сигналом SIGTSTP; SIGWINCH используется в оконных системах для отправки приложению уведомления об изменении размеров окна; сигналы SIGUSR1 и SIGUSR2 используются для организации межпроцессного взаимодействия.

Давайте, наконец, попробуем написать немного кода. В сценариях оболочки сигналы перехватываются при помощи встроенной команды trap. Ниже на примере показано, как обычно это делается:

trap 'echo "Ctrl-C Ignored" ' INT

Как это использовать в сценарии? Очень просто!

#!/bin/bash
 
trap 'echo " - Ctrl-C ignored" ' INT
while /usr/bin/true ; do
 sleep 30
done
 
exit 0

Видите здесь бесконечный цикл? Он практически не использует ресурсов, поскольку практически всё время «спит» и будет выполняться вечно или до тех пор, пока вы не остановите его.

Давайте рассмотрим более гибкий способ работы с сигналами в сценариях оболочки при помощи функций:

sigquit()
{
 echo "signal QUIT received"
}
 
sigint()
{
 echo "signal INT received, script ending"
 exit 0
}
 
trap 'sigquit' QUIT
trap 'sigint' INT
trap ':' HUP # ignore the specified signals
 
echo "test script started. My PID is $$"
while /usr/bin/true ; do
 sleep 30
done

Запустите приведённый выше код и из другого терминала попробуйте отправить ему несколько разных сигналов:

$ kill -HUP 25309
$ kill -QUIT 25309
$ kill -INT 25309

Ниже показано, как скрипт будет реагировать на отправленные ему сигналы:

$ ./test.sh
test script started. My PID is 25309
signal QUIT received
signal INT received, script ending

Вооружившись этим примером, давайте посмотрим, как обрабатывать более сложные сигналы, например отправляемые по нажатию Ctrl+Z.

Создавая сложные скрипты, вы обнаружите, что попадается масса моментов, когда необходимо игнорировать поступивший сигнал TSTP (он же SIGTSTP, Ctrl+Z или сигнал номер 18), а иногда — перезапустить работу сценария.

Начиная решать подобную задачу, сперва создадим функцию, которая не только обрабатывает определённый сигнал, но и затем «отключает» сама себя и не будет срабатывать при последующих вызовах:

sigtstp()
{
 echo "SIGTSTP received"
 trap - TSTP
 echo "SIGTSTP standard handling restored"
}

Вызовом trap — TSTP в любом месте скрипта вы отключаете назначенный ранее обработчик указанного сигнала. Теперь, если у нас в коде есть строка

trap 'sigtstp' TSTP

в том месте, где мы хотим включить игнорирование нажатия Ctrl+Z, то она заставит скрипт не реагировать на Ctrl+Z, но только один раз. В следующий раз, когда вы нажмёте Ctrl+Z, сигнал будет обработан так же, как обычно.

Игнорирование Ctrl+Z часто используется тогда, когда ваш скрипт ещё не готов «правильно» на него реагировать. Просто вставьте этот код, там где требуется такое поведение сценария:

trap : TSTP # ignore Ctrl-Z requests

Затем, когда  ваш скрипт будет готов к обработке Ctrl+Z, используйте следующую конструкцию, которая вернёт обычную реакцию сценария на сигнал останова:

trap - TSTP # allow Ctrl-Z requests

Эксперименты показывают, что имеет место быть какая-то странноватая буферизация вывода терминалом, связанная с сигналом SIGTSTP, так что не удивляйтесь, если, перехватывая этот сигнал, вы вдруг не получите вывод от вашего скрипта вплоть до момента его завершения.

Давайте рассмотрим более практичный пример. Представим, что у вас есть некий административный скрипт, который работает в режиме демона. Иногда у вас возникает необходимость внести какие-либо изменения в его конфигурационный файл, но при этом нужно сделать так, чтобы файл конфигурации перечитывался скриптом в процессе его работы, а не только при запуске. Такая возможность позволяет значительно сократить время неработоспособности демона, поскольку его не нужно останавливать, а затем заново запускать.

При решении этой задачи мы будем использовать сигнал SIGUSR1, поскольку он обычно для этого и используется.

Считывание файла конфигурации может быть реализовано достаточно просто:

. $config

Вспомним, что использование команды «точка» приведёт к тому, что переменные, определённые во внешнем файле, станут доступными в текущей оболочке. Вместо точки можно использовать команду source.

Вот так выглядит наш экспериментальный скрипт:

#!/bin/bash
 
config="our.config.file"
sigusr1()
{
 echo "(SIGUSR1: re-reading config file)"
 . $config
}
 
trap sigusr1 USR1 # catch -USR1 signal
 
echo "Daemon started. Assigned PID is $$"
 
. $config # read it first time
 
while /usr/bin/true; do
 echo "Target number = $number"
 sleep 5
done
 
trap - USR1 # reset to be elegant
 
exit 0

Начнём с того, что определим в нашем конфигурационном файле переменную number со значением 5. Затем, через 10-15 секунд изменим значение переменной на 1. До тех пор, пока мы не пошлём скрипту сигнал USR1, он будет выводить начальное значение переменной:

$ ./test2.sh
Daemon started. Assigned PID is 25843
Target number = 5
Target number = 5
Target number = 5

Тем временем, когда значение переменной в конфигурационном файле уже изменено, из другого терминала пошлём нашему скрипту сигнал:

$ kill -USR1 25843

Теперь посмотрим, что происходит в терминале, где выполняется наш скрипт:

(SIGUSR1: re-reading config file)
Target number = 1
Target number = 1

Классно, не правда ли?

Надеюсь, что сегодняшнее наше исследование работы с сигналами в сценариях оболочки пригодится вам в работе. Лично я многому научился, пока разбирался со всем, о чём здесь написано. Однако, для меня всё ещё остаётся загадкой, каким образом возобновить вывод скрипта после того, как он перехватывает сигнал SIGTSTP и я надеюсь, что читатели подскажут мне, как это сделать.



Источник: http://www.ashep.org/2010/obrabotka-signalov-v-scenariyax-obolochki/
Категория: Shell | Добавил: oleg (25.10.2010) | Автор: ashep
Просмотров: 1369 | Рейтинг: 0.0/0 |
Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Форма входа

Beastie

Друзья сайта

Статистика

Онлайн всего: 2
Гостей: 2
Пользователей: 0
links

Copyright MyCorp © 2024