Представьте, что ваш серверный процесс должен одновременно ожидать ввода-вывода для нескольких дескрипторов, а также должен ожидать доставки сигнала.
Проблема в том, что вы не можете безопасно смешивать select () с сигналами, потому что есть вероятность состояния гонки.
Рассмотрим следующий фрагмент кода:
GOT_SIGNAL = False def handler(signum, frame): global GOT_SIGNAL GOT_SIGNAL = True ... signal.signal(signal.SIGUSR1, handler) # What if the signal arrives at this point? try: reads, writes, excs = select.select(rlist, wlist, elist) except select.error as e: code, msg = e.args if code == errno.EINTR: if GOT_SIGNAL: print 'Got signal' else: raise
Проблема здесь в том, что если SIGUSR1 доставляется после установки обработчика сигнала, но перед вызовом select, то вызов select блокируется, и мы не будем выполнять логику нашего приложения в ответ на событие, таким образом, фактически «пропуская» сигнал (наше приложение Логика в этом случае печатает сообщение: « Получил сигнал» ).
Это пример возможных неприятных гонок. Давайте смоделируем это с selsigrace.py
Запустить программу
$ python selsigrace.py PID: 32324 Sleep for 10 secs
и отправьте сигнал USR1 на PID (он отличается при каждом запуске) в течение 10-секундного интервала, пока процесс все еще спит:
$ kill -USR1 32324
Вы должны увидеть, как программа выводит дополнительную строку вывода «Проснись и блокируй в« select »» и блокируй без выхода , нет сообщения «Got signal»:
$ python selsigrace.py PID: 32324 Sleep for 10 secs Wake up and block in "select"
Если вы отправите еще один сигнал USR1 в этот момент, то выбор будет прерван, и программа завершит работу с сообщением:
$ kill -USR1 32324
$ python selsigrace.py PID: 32324 Sleep for 10 secs Wake up and block in "select" Got signal
Self-Pipe Trick используется, чтобы избежать условий гонки при ожидании сигналов и вызове select для набора дескрипторов.
Следующие шаги описывают, как реализовать это:
- Создайте канал и измените его чтение и запись, чтобы они были неблокирующими
- Добавьте конец канала для чтения в список чтения дескрипторов, данных для выбора.
- Установите обработчик сигнала для сигнала, который нас интересует. Когда сигнал поступает, обработчик сигнала записывает байт данных в канал. Поскольку конец записи канала неблокирует, мы предотвращаем ситуацию, когда сигналы затопляют процесс, канал заполняется, и процесс блокирует себя в обработчике сигналов.
- Когда выбор успешно возвращается, проверьте, находится ли конец чтения канала в списке для чтения, и если это так, то наш сигнал поступил.
- Когда поступает сигнал, прочитайте все байты, которые находятся в канале, и выполните любые действия, которые необходимо выполнить в ответ на доставку сигнала.
Вы можете проверить реализацию на GitHub и попробовать.
Запустите selfpipe.py и отправьте ему сигнал USR1 . Вы должны увидеть вывод сообщения « Получил сигнал» и выход.