Do signal() and alarm() work even when the process where they are run is busy? Or should I run it on another dedicated process?
I am currently implementing a Producer/Consumers problem program. I have one parent and several child processes. Everything is working but now I need to make my program output each k
milliseconds the progress of the task my program is doing.
At first I thought that maybe it'd be just about using the signal()
and alarm()
functions, but from some preliminary testing I've been making it doesn't seem enough. I have watched over several log files and it seems onAlarm()
is not being called. I guess it has to do with the fact that as that both parent and children are "busy" they don't receive the events? Or even if they are busy, they should be able to receive calls on onAlarm()? The only workaround I see for this is to create yet another process, that has as single responsability dealing with this.
This is my "events" code:
void onAlarm() {
signal(SIGALRM, onAlarm);
alarm(0.01);
开发者_JS百科fprintf(outputFile, "ALAAAAAAAAAAAAAARMMMMMMMMMMM: %d\n", numberOfBytesRead);
}
int main() {
signal(SIGALRM, onAlarm);
alarm(0.01);
...
}
Your main problem is that alarm()
takes an integer number of seconds - and alarm(0)
is used to cancel any outstanding alarms.
The natural follow-up question is:
How can I do sub-second waiting?
I'm not sure what the approved way is. On some systems, there is a micro-sleep (usleep()
) call, but that is not part of POSIX 2008. The direct analogue of usleep()
in POSIX appears to be nanosleep()
.
There is a sigtimewait()
which could probably be used to achieve the effect. (You might be able to use setitimer()
and getitimer()
in lieu of usleep()
.)
The difficulty with all of these is that you are either synchronous (you can't get on with work while waiting for a signal to arrive) or not sent a signal. I don't immediately see a POSIX sub-second alarm mechanism, which would allow you to continue work while waiting for the timeout.
I'm not sure whether or not this is your problem, but it's not safe to fprintf
inside a signal handler. For example, the signal could be raised inside of fprintf
itself, which might lead to unexpected behavior. Or perhaps fprintf
is allocating some memory, and the signal was caught while malloc
was running. This kind of thing can produce seemingly random deadlocks and violent crashes.
The safe way to do complex computations inside a signal handler is for your signal handler to alter the state of an already-running event loop, or something like this. Or, for your specific problem, as others suggest, avoid using signals for this and use a more straightforward sleep call.
The short answer to your question is yes. They work even though the process they attached to is busy doing calculations or io/waiting of some sort. The only limitation is that the process is not changing and playing with SIGALRM
itself which would cause problems. When a signal arrives the normal execution of a process/thread is suspended and the signal handler is called. That is the whole idea of signal handling and why it is called asynchronous
.
The longer answer to your question is no. You wouldn't want to implement progress report via the signal handler mechanism since the API that you can use inside a signal handler is very limited. To be exact the example that you referred to wrong since it uses fprintf(3)
. As has been stated in one of the replies signals are asynchronous. This means that if the signal arrives in the middle of the main code calling say, malloc(3)
, and your code calls malloc(3)
as well (you can never know, printf(3)
may call malloc(3)
for buffering and other needs) then you will corrupts mallocs own internal data structures and cause the program to fault. You may even have problems calling your own functions which are not async safe. You have a list of safe functions that you can call inside a signal handler and you can find those in man 7 signal
under Async-signal-safe functions
. So yes, technically you can implement progress reporting via alarm(3)
as long as you are willing to live with this reduced API and that is the reason I would not do it unless the program is single threaded by design and unless there is really no way in which I see the progress reporting code as being subject to future enhancements that will make it hard to write inside a signal handler.
Another problem which has been stated about your example is that alarm(2)
does not accept sub-second arguments and the example above should have failed compilation completely or at least showed some warnings about that fact.
For microsecond resolution you can use setitimer(2)
with ITIMER_REAL
as has been stated.
For nanosecond resolution on Linux you can use timer_create(2)
, CLOCK_REALTIME
, SIGEV_SIGNAL
and timer_settime(2)
that have many more features.
Here is some example code. Note that this uses my own error handling macros. You can see it in a compilable state in this project 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;
}
If you need sub-second resolution on a timer, you can use a second posix thread and a usleep
For a sub-second timer sending a signal, you need to use the POSIX setitimer(2) function.
精彩评论