Ответ 1
В дополнение к alk answer:
Вы можете использовать указатель функции для потока, чтобы выбрать, какая функция будет выполняться при доставке определенного сигнала по потоку.
Примечание. Сигналы доставляются в поток any, который явно не блокирует его доставку. Это не меняет этого. Вам все равно нужно использовать pthread_kill()
или аналогичные механизмы для направления сигнала на конкретный поток; сигналы, которые поднимаются или отправляются процессу (вместо конкретного потока), по-прежнему будут обрабатываться случайным потоком (среди тех, которые его не блокируют).
Я не могу придумать какой-либо вариант использования, когда я лично предпочел бы такой подход; до сих пор всегда был какой-то другой способ, что-то еще легче и лучше. Итак, если вы планируете внедрить что-то подобное для реального приложения, пожалуйста, отстранитесь и пересмотрите свою логику приложений.
Но, поскольку метод возможен, вот как я могу его реализовать:
#include <signal.h>
/* Per-thread signal handler function pointer.
* Always use set_thread_SIG_handler() to change this.
*/
static __thread void (*thread_SIG_handler)(int, siginfo_t *, void *) = (void *)0;
/* Process-wide signal handler.
*/
static void process_SIG_handler(int signum, siginfo_t *info, void *context)
{
void (*func)(int, siginfo_t *, void *);
#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)
func = __atomic_load_n(&thread_SIG_handler, __ATOMIC_SEQ_CST);
#else
func = __sync_fetch_and_add(&thread_SIG_handler, (void *)0);
#endif
if (func)
func(signum, info, context);
}
/* Helper function to set new per-thread signal handler
*/
static void set_thread_SIG_handler(void (*func)(int, siginfo_t *, void *))
{
#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)
__atomic_store_n(&thread_SIG_handler, func, __ATOMIC_SEQ_CST);
#else
void (*oldfunc)(int, siginfo_t *, void *);
do {
oldfunc = thread_SIG_handler;
} while (!__sync_bool_compare_and_swap(&thread_SIG_handler, oldfunc, func));
#endif
}
/* Install the process-wide signal handler.
*/
int install_SIG_handlers(const int signum)
{
struct sigaction act;
sigemptyset(&act.sa_mask);
act.sa_sigaction = process_SIG_handler;
act.sa_flags = SA_SIGACTION;
if (sigaction(signum, &act, NULL))
return errno;
return 0;
}
Мне нравится выше, потому что он не требует pthreads, и он очень надежный и надежный. Помимо визуального беспорядка из-за того, что логика препроцессора выбирает, какой стиль атомных встроенных модулей используется, это очень просто, если вы внимательно посмотрите на него.
GCC 4.7 и более поздние версии предоставляют С++ 11-подобную __ атомные встроенные функции, более старые версии GCC и другие компиляторы (ICC, Pathscale, Portland Group) предоставляют __ sync legacy встроенные функции. __thread
ключевое слово для локального хранилища потоков также должно быть доступно во всех существующих системах POSIX-y.
Если у вас есть архаичная система или настаивайте на соблюдении стандартов, следующий код должен иметь примерно эквивалентное поведение:
#include <pthread.h>
#include <signal.h>
#include <errno.h>
static pthread_key_t thread_SIG_handler_key;
static void process_SIG_handler(int signum, siginfo_t *info, void *context)
{
void (*func)(int, siginfo_t *, void *);
*((void **)&func) = pthread_getspecific(thread_SIG_handler_key);
if (func)
func(signum, info, context);
}
static int set_thread_SIG_handler(void (*func)(int, siginfo_t *, void *))
{
sigset_t block, old;
int result;
sigemptyset(&block);
sigaddset(&block, SIG); /* Use signal number instead of SIG! */
result = pthread_sigmask(SIG_BLOCK, &block, &old);
if (result)
return errno = result;
result = pthread_setspecific(thread_SIG_handler_key, (void *)func);
if (result) {
pthread_sigmask(SIG_SETMASK, &old, NULL);
return errno = result;
}
result = pthread_sigmask(SIG_SETMASK, &old, NULL);
if (result)
return errno = result;
return 0;
}
int install_SIG_handlers(const int signum)
{
struct sigaction act;
int result;
result = pthread_key_create(&thread_SIG_handler_key, NULL);
if (result)
return errno = result;
sigemptyset(&act.sa_mask);
act.sa_sigaction = process_SIG_handler;
act.sa_flags = SA_SIGACTION;
if (sigaction(signum, &act, NULL))
return errno;
return 0;
}
Я думаю, что самый близкий реальный эквивалент кода, подобный этому, который я когда-либо использовал, - это тот, где я использовал один сигнал в реальном времени (SIGRTMIN+0
), заблокированный во всех, кроме одного потока, в качестве отражателя: он отправил другой сигнал реального времени (SIGRTMIN+1
) для ряда рабочих потоков, чтобы прервать блокировку ввода-вывода. (Это можно сделать с помощью одного сигнала в реальном времени, но модель с двумя сигналами проще реализовать и упростить.)
Такое отражение сигнала или разветвление иногда полезно, и это не так отличается от этого подхода. Разные, чтобы оправдать свой собственный вопрос, если кто-то заинтересован.