Если ваше программное обеспечение работает под управлением Linux, а ваш процесс - poll()
или select()
, возможно, будет гораздо удобнее использовать signalfd()
.
Я реализовал это в моем классе snap_communicator в это время в строке 2789. Есть копия реализации.
snap_communicator::snap_signal::snap_signal(int posix_signal)
: f_signal(posix_signal)
//, f_socket(-1) -- auto-init
//, f_signal_info() -- auto-init
//, f_unblock(false) -- auto-init
{
int const r(sigismember(&g_signal_handlers, f_signal));
if(r != 0)
{
if(r == 1)
{
// this could be fixed, but probably not worth the trouble...
throw snap_communicator_initialization_error("the same signal cannot be created more than once in your entire process.");
}
// f_signal is not considered valid by this OS
throw snap_communicator_initialization_error("posix_signal (f_signal) is not a valid/recognized signal number.");
}
// create a mask for that signal
//
sigset_t set;
sigemptyset(&set);
sigaddset(&set, f_signal); // ignore error, we already know f_signal is valid
// first we block the signal
//
if(sigprocmask(SIG_BLOCK, &set, nullptr) != 0)
{
throw snap_communicator_runtime_error("sigprocmask() failed to block signal.");
}
// second we create a "socket" for the signal (really it is a file
// descriptor manager by the kernel)
//
f_socket = signalfd(-1, &set, SFD_NONBLOCK | SFD_CLOEXEC);
if(f_socket == -1)
{
int const e(errno);
SNAP_LOG_ERROR("signalfd() failed to create a signal listener for signal ")(f_signal)(" (errno: ")(e)(" -- ")(strerror(e))(")");
throw snap_communicator_runtime_error("signalfd() failed to create a signal listener.");
}
// mark this signal as in use
//
sigaddset(&g_signal_handlers, f_signal); // ignore error, we already know f_signal is valid
}
С этим «сокетом» вы можете сделать poll()
, и он вызовет эквивалент события чтения при поступлении сигнала.
Вы получаете информацию о сигнале следующим образом:
int const r(read(f_socket, &f_signal_info, sizeof(f_signal_info)));
с f_signal_info
, объявленным как:
struct signalfd_siginfo f_signal_info;
Самое замечательное то, что теперь все это происходит в моих классах, и у меня нет никаких «странных» обработчиков сигналов, которые могут заблокировать мой поток, неправильно обрабатывать исключения C ++ и другие потенциальные проблемы. С моей стороны, у меня есть классы C ++ с виртуальными функциями, которые вызываются всякий раз, когда происходит событие, включая сигналы Unix, такие как SIGINT
, SIGPIPE
, SIGCHLD
... Вы также можете реализовать обратные вызовы, используя сигналы повышения, которые дают вам сила использования std::bind()
.
Все это говорит, я все еще использую signal()
для SIGSEGV
, SIGBUS
и т. Д. Те, которые я не собираюсь делать никакой дополнительной работы, когда они происходят. Я пытаюсь записать трассировку стека на тех и все. Поэтому я не использую signalfd()
на них.