Signals are available on all POSIX operating systems (including Windows, Linux, Mac OS X), and include:
Signals can also be used to indicate that I/O is ready (SIGIO,
enabled using ``fcntl''), that a timer has expired (SIGALRM, SIGPROF,
or SIGVPROF, enabled using ``setitimer''), that the operating system
wants you to shut down (SIGTERM, SIGQUIT, SIGKILL, all UNIX-specific),
that various events have happened on the terminal (SIGHUP, SIGWINCH,
SIGPIPE, SIGTTIN, SIGTTOU, all UNIX-specific), or for
application-defined purposes (SIGUSR1/SIGUSR2, which must be sent
explicitly). See signal.h for the full list of signals.
Signals, exactly like interrupts, are hence a generic ``catch-all'' notification mechanism, used for a variety of unrelated tasks.
Overall signal delivery looks like this:
#include <signal.h>
void myHandler(int i)
{
printf("Sorry dude--you just hit signal %d\n",i);
exit(1);
}
int foo(void) {
int *badPointer=(int *)0;
printf("Installing signal handler\n");
signal(SIGSEGV,myHandler); /* <------------- */
printf("Signal handler installed. Segfaulting...\n");
(*badPointer)++;
printf("Back from segfault?!\n");
return 0;
}
(Executable NetRun Link)
Which on my machine prints out:
Installing signal handlerTypically you do *not* want to just return from a signal handler without actually handling the problem.
Signal handler installed. Segfaulting...
Sorry dude--you just hit signal 11
#include <signal.h>
#ifdef SOLARIS /* needed with at least Solaris 8 */
#include <siginfo.h>
#endif
void segfaultHandler(int cause, siginfo_t *HowCome, void *ucontext_void) {
void *badptr=HowCome->si_addr;
std::cout<<"Bad pointer access at address "<<badptr<<"\n";
exit(1); // the easy way out: just exit.
}
int foo(void)
{
/* Install our SIGSEGV signal handler */
struct sigaction sa;
sa.sa_sigaction = segfaultHandler;
sigemptyset( &sa.sa_mask );
sa.sa_flags = SA_SIGINFO; /* we want a siginfo_t */
if (sigaction (SIGSEGV, &sa, 0)) { perror("sigaction failed"); exit(1); }
/* Do something that crashes */
int *ptr=(int *)0x123456;
std::cout<<"About to access pointer "<<ptr<<"\n";
*ptr=3;
std::cout<<"Wait, that worked?!\n";
return 0;
}
You can actually use the signal handler to fix the root cause of the
error. In this case, the problem was there's no memory at pointer
0x123456. But using mmap, we can *make* there be memory at that
address!
#include <sys/mman.h> /* for mmap */Segfault-fix-continue is a surprisingly powerful technique. It's used by the operating system to implement virtual memory, used by dynamic translators to lazily demand-translate binary code, used by parallel programmers to fake shared memory across the network, and lots of other interesting strange things!
#include <signal.h>
#ifdef SOLARIS /* needed with at least Solaris 8 */
#include <siginfo.h>
#endif
void segfaultHandler(int cause, siginfo_t *HowCome, void *ucontext_void) {
void *badptr=HowCome->si_addr;
std::cout<<"Fixing bad pointer access at address "<<badptr<<"\n";
long start=(long)badptr, end=16+(long)badptr;
start&=~0xfff; end+=0xfff; end&=~0xfff; /* round to page boundaries */
void *ret=mmap((void *)start,end-start,PROT_READ|PROT_WRITE|PROT_EXEC,
MAP_ANONYMOUS|MAP_SHARED|MAP_FIXED,-1,0);
if (ret!=(void *)start) {
std::cout<<"Failed to map region (got "<<ret<<")\n";
exit(1);
}
/* else we've fixed the bad pointer, so return and keep running! */
}