Потоки и сигналы POSIX

Я пытался понять тонкости взаимодействия потоков POSIX и POSIX. В частности, меня интересует:

  • Какой лучший способ контролировать, какой поток передается сигнал (при условии, что он не является фатальным в первую очередь)?
  • Каков наилучший способ рассказать другой поток (который действительно может быть занят), что сигнал появился? (Я уже знаю, что это плохая идея использовать переменные условия pthread из обработчика сигнала.)
  • Как я могу безопасно обрабатывать передачу информации о том, что сигнал произошел с другими потоками? Должно ли это происходить в обработчике сигналов? (Я вообще не хочу убивать другие потоки, мне нужен гораздо более тонкий подход.)

Для справки о том, почему я хочу этого, я изучаю, как преобразовать пакет TclX для поддержки потоков или разбить его и, по крайней мере, сделать некоторые полезные части поддержки потоков. Сигналы являются одной из тех частей, которые представляют особый интерес.

Ответ 1

  • Какой лучший способ контролировать, какой поток сигнал доставляется до?

Как указано в @zoli2k, явная нотация одного потока для обработки всех сигналов, которые вы хотите обработать (или набор потоков с определенными сигнальными функциями), является хорошей техникой.

  • Каков наилучший способ рассказать другой поток (который может быть действительно занят) что сигнал достиг? [...]
  • Как я могу безопасно обрабатывать передачу информации о том, что сигнал произошел к другим темам? Должно ли это происходить в обработчике сигналов?

Я не буду говорить "лучше", но здесь моя рекомендация:

Заблокируйте все желаемые сигналы в main, чтобы все потоки наследовали эту сигнальную маску. Затем создайте поток приема специального сигнала в виде сигнального цикла событий, отправляя вновь полученные сигналы как некоторые другие внутрипоточные коммуникации.

Самый простой способ сделать это - принять сигналы потока в цикле, используя sigwaitinfo или sigtimedwait. Поток затем каким-то образом преобразует сигналы, возможно, транслирует pthread_cond_t, пробуждая другие потоки с большим количеством операций ввода-вывода, заставляя команду в конкретной потоке, зависящей от приложения, независимо от того, что.

В качестве альтернативы, специальный поток может разрешать доставку сигналов в обработчик сигналов, разоблачение для доставки только тогда, когда они готовы обрабатывать сигналы. (Передача сигнала через обработчики, как правило, более подвержена ошибкам, чем прием сигнала через семейство sigwait.) В этом случае обработчик сигнала приемника выполняет несколько простых и безопасных асинхронных действий: установка флагов sig_atomic_t вызывая sigaddset(&signals_i_have_seen_recently, latest_sig), write() байт для неблокирующего self-pipe и т.д. Затем вернитесь в свой замаскированный основной цикл, поток связывает прием сигнала с другими потоками, как указано выше.

( ОБНОВЛЕНО @caf справедливо указывает, что подходы sigwait превосходят.)

Ответ 2

В соответствии со стандартом POSIX все потоки должны отображаться с одним и тем же PID в системе и с помощью pthread_sigmask() вы можете определить маску блокировки сигнала для каждого потока.

Так как разрешено определять только один обработчик сигнала на PID, я предпочитаю обрабатывать все сигналы в одном потоке и отправлять pthread_cancel(), если текущий поток необходимо отменить. Это предпочтительный способ против pthread_kill(), поскольку он позволяет определять функции очистки для потоков.

В некоторых старых системах из-за отсутствия надлежащей поддержки ядра текущие потоки могут иметь различный PID из PID родительского потока. См. FAQ для обработки сигналов с помощью linuxThreads на Linux 2.4.

Ответ 3

IMHO, сигналы Unix V и нити posix плохо смешиваются. Unix V - 1970 год. POSIX - 1980;)

Есть точки отмены, и если вы разрешаете сигналы и pthreads в одном приложении, вы, в конечном итоге, будете писать циклы вокруг каждого вызова, что может неожиданно вернуть EINTR.

Итак, то, что я сделал в (нескольких) случаях, когда мне приходилось программировать многопоточность на Linux или QNX, было, чтобы замаскировать все сигналы для всех (но одного) потоков.

Когда поступает сигнал Unix V, процесс переключает стек (это было столько же concurrency в Unix V, сколько вы могли получить в процессе).

Как теперь указывают другие сообщения, возможно, теперь возможно сообщить Системе, какой posix-поток станет жертвой переключения этого стека.

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

Итак, вместо того, чтобы отменять другие потоки или убивать их (или другие странные вещи), вы должны попытаться вывести Сигнал из контекста сигнала в поток обработчика сигнала, а затем использовать механизмы связи шаблона актора для отправки семантически полезных сообщений в тем актерам, которым нужна информация, связанная с сигналом.

Ответ 4

Где я сейчас:

  • Сигналы встречаются в разных основных классах, некоторые из которых обычно должны просто убивать процесс в любом случае (SIGILL), а некоторые из них никогда не нуждаются в чем-либо (SIGIO; проще всего сделать асинхронный ввод-вывод в любом случае). Эти два класса не требуют никаких действий.
  • Некоторые сигналы не требуют немедленного рассмотрения; подобные SIGWINCH могут быть поставлены в очередь до тех пор, пока это не будет удобно (точно так же, как событие из X11).
  • Трудные из них - те, на которые вы хотите реагировать на них, прерывая то, что вы делаете, но не вдаваясь в нисходящий поток. В частности, SIGINT в интерактивном режиме должен оставить отзывчивым.

Мне еще нужно сортировать signal vs sigaction, pselect, sigwait, sigaltstack и целую кучу других битов и частей API POSIX (и не POSIX).