Короткий ответ на ваш вопрос - да. Они работают, даже несмотря на то, что процесс, к которому они подключены, занят вычислениями или ожиданием / ожиданием какого-то рода. Единственным ограничением является то, что процесс не меняется и играет с SIGALRM
, что может вызвать проблемы. Когда поступает сигнал, нормальное выполнение процесса / потока приостанавливается и вызывается обработчик сигнала. В этом вся идея обработки сигналов и почему она называется asynchronous
.
Более длинный ответ на ваш вопрос - нет. Вы не захотите реализовывать отчет о ходе выполнения с помощью механизма обработчика сигналов, поскольку API, который вы можете использовать внутри обработчика сигналов, очень ограничен. Если быть точным, пример, который вы указали неверно, поскольку он использует fprintf(3)
. Как было сказано в одном из ответов, сигналы являются асинхронными. Это означает, что если сигнал приходит в середине основного кода, вызывающего, скажем, malloc(3)
, а ваш код также вызывает malloc(3)
(вы никогда не узнаете, printf(3)
может вызвать malloc(3)
для буферизации и других нужд) тогда вы испортите mallocs собственные внутренние структуры данных и вызовете сбой программы. У вас могут возникнуть проблемы с вызовом собственных функций, которые не являются асинхронными. У вас есть список безопасных функций, которые вы можете вызывать в обработчике сигналов, и вы можете найти их в man 7 signal
в Async-signal-safe functions
. Так что да, технически вы можете реализовать отчеты о прогрессе через alarm(3)
, если вы готовы жить с этим сокращенным API, и поэтому я не буду этого делать, если программа не является однопоточной по дизайну и если на самом деле нет никакого способа в котором я вижу код сообщения о прогрессе как подлежащий будущим улучшениям, которые затруднят запись в обработчике сигналов.
Другая проблема, о которой говорилось в вашем примере, заключается в том, что alarm(2)
не принимает аргументы длиной более секунды, и в приведенном выше примере должна была произойти ошибка компиляции или, по крайней мере, были показаны некоторые предупреждения об этом факте.
Для разрешения в микросекундах вы можете использовать setitimer(2)
с ITIMER_REAL
, как было указано.
Для разрешения наносекунд в Linux вы можете использовать timer_create(2)
, CLOCK_REALTIME
, SIGEV_SIGNAL
и timer_settime(2)
, которые имеют гораздо больше функций.
Вот пример кода. Обратите внимание, что здесь используются мои собственные макросы обработки ошибок. Вы можете увидеть его в скомпилированном состоянии в этом проекте demos-linux
#include <signal.h> // for signal(2), SIG_ERR
#include <unistd.h> // for alarm(2), write(2)
#include <stdlib.h> // for EXIT_SUCCESS
#include <err_utils.h> // for CHECK_NOT_M1(), CHECK_NOT_SIGT()
#include <stdio.h> // for snprintf(3), STDERR_FILENO
/*
* This is an example of doing progress reports via the SIGALRM signal every second.
* The main code does a tight calculation loop and the progress reporting to stderr
* (file descriptor 2) is done via the alarm signal handler.
*/
/*
* The variables are global to allow the signal handler to access them easily
* You DONT need to initialize them to 0 since that is the default.
* The 'volatile' on i is *critical* since it will be accessed asynchronously
* and the compiler needs to know not to put it in a register since that
* will mean that we cannot report it's value correctly from the signal
* handler.
*/
volatile unsigned long i;
/*
* Remember that this is a signal handler and calls to fprintf(3) or the like
* are forbidden so we are forced to use async-safe function (see man 7 signal).
* That is the reason for the cumbersome code. Hopefully snprintf(3) is safe enough
* to use.
*/
static void handler(int sig) {
// we have to reschedule the SIGALRM every time since the alarm(2)
// is a one time deal.
CHECK_NOT_SIGT(signal(SIGALRM, handler), SIG_ERR);
// no error code from alarm(2)
alarm(1);
char buf[100];
int len=snprintf(buf, sizeof(buf), "did [%ld] units of work...\n", i);
CHECK_NOT_M1(write(STDERR_FILENO, buf, len));
}
int main(int argc, char** argv, char** envp) {
CHECK_NOT_SIGT(signal(SIGALRM, handler), SIG_ERR);
// no error code from alarm(2)
alarm(1);
// a very long calculation
while(true) {
/* Do some real work here */
i++;
}
return EXIT_SUCCESS;
}