CSCE 313 Lecture 16
« previous | Tuesday, March 20, 2012 | next »
Asynchronous Events: Signals
- Ask kernel: "Did event X happen since I last checked?"
- Tell kernel: "If and when event X happens, do the following..." (Set and forget)
- terminal-generated signals
- C-c / ^C sends SIGINT
- hardware-exception-generated signals
- Hardware detects condition and notifies kernel
- SIGFPE when div by 0, SIGSEGV for invalid memory access
- software-condition-generated signals
- software event
- kill command/code in command line or in code sends signal to process:
- SIGURG by ount-of-band data on network connection, SIGPIPE for proken pipe, SIGALRM for timer
When a signal is received, the program can
- Ignore the signal: works for most signals (hardware signals, SIGKILL, and SIGSTOP cannot be ignored)
- Catch the signal: Tell kernel to invoke a given function whenever a signal occurs. (e.g. catch SIGTERM and cleanup)
- Default action All signals have a default action (see below)
Signals and their default actions
(table output from man signal)
No. | Name | Default Action | Description |
---|---|---|---|
1 | SIGHUP | terminate process | terminal line hangup |
2 | SIGINT | terminate process | interrupt program |
3 | SIGQUIT | create core image | quit program |
4 | SIGILL | create core image | illegal instruction |
5 | SIGTRAP | create core image | trace trap |
6 | SIGABRT | create core image | abort program (formerly SIGIOT) |
7 | SIGEMT | create core image | emulate instruction executed |
8 | SIGFPE | create core image | floating-point exception |
9 | SIGKILL | terminate process | kill program |
10 | SIGBUS | create core image | bus error |
11 | SIGSEGV | create core image | segmentation violation |
12 | SIGSYS | create core image | non-existent system call invoked |
13 | SIGPIPE | terminate process | write on a pipe with no reader |
14 | SIGALRM | terminate process | real-time timer expired |
15 | SIGTERM | terminate process | software termination signal |
16 | SIGURG | discard signal | urgent condition present on socket |
17 | SIGSTOP | stop process | stop (cannot be caught or ignored) |
18 | SIGTSTP | stop process | stop signal generated from keyboard |
19 | SIGCONT | discard signal | continue after stop |
20 | SIGCHLD | discard signal | child status has changed |
21 | SIGTTIN | stop process | background read attempted from control terminal |
22 | SIGTTOU | stop process | background write attempted to control terminal |
23 | SIGIO | discard signal | I/O is possible on a descriptor (see fcntl(2)) |
24 | SIGXCPU | terminate process | cpu time limit exceeded (see setrlimit(2)) |
25 | SIGXFSZ | terminate process | file size limit exceeded (see setrlimit(2)) |
26 | SIGVTALRM | terminate process | virtual time alarm (see setitimer(2)) |
27 | SIGPROF | terminate process | profiling timer alarm (see setitimer(2)) |
28 | SIGWINCH | discard signal | Window size change |
29 | SIGINFO | discard signal | status request from keyboard |
30 | SIGUSR1 | terminate process | User defined signal 1 |
31 | SIGUSR2 | terminate process | User defined signal 2 |
#include <signal.h>
// send signal to process pid
int kill(pid_t pid, int sig);
// send signal to self
int raise(int sig);
// traditional signal handlers:
static void sig_usr(int signo {
// do something here
}
int main(void) {
if (signal(SIGUSR1, sig_usr) == SIG_ERR)
perr("cannot catch signal SIGUSR1");
}
// modern signal handling:
int sigaction (int signo, const struct sigaction *act, struct sigaction *old);
// installs a new signal handler form 'act', return old handler in 'old'.
struct sigaction {
void (*sa_handler)(int); // SIG_DFL, SIG_IGN, or pointer to function
sigset_t sa_mask; // signals to block
int sa_flags; // flags and options
void (*sa_sigaction)(int, siginfo_t*, void*); // real-time handler
}
// usage
struct sigaction new_act;
new_act.sa_handler = mysighandler;
new_act.sa_flags = 0;
if ((sigemptyset(&new_act.sa_mask) == 1) || (sigaction(SIGINT, &new_act, NULL) == -1))
perror("Failed to install SIGINT signal handler.");
Terminology
- A signal is generated when an event that causes the signal occurs
- A signal is delivered when an action for a signal is taken
- Between generation and delivery, the signal is pending.
- A process has the option of blocking the delivery of a signal. It will remain blocked until
- it is unblocked
- action is changed to ignore the signal
- When multiple signals are delivered, they can be queued (not done in most UNIX systems)
- In programs, a signal mask is used to control set of signals that are blocked from delivery
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *old);
// `how` can be one of:
// SIG_BLOCK (add collection of signals to those already blocked)
// SIG_UNBLOCK (remove signals from mask)
// SIG_SETMASK (set mask equal to what's passed in)
Waiting for Signals
To wait for a signal (any signal), use the following
#include <signal.h>
int pause(void);</code>
<source lang="c">// first attempt (!!)
stattic volatile sig_atomic_t quitflag = 0;
while (quitflag == 0) pause();
What if the signal is generated after the while statement and before the first call to pause
sigsuspend (old way)
#include <signal.h>
int sigsuspend(const sigset_t *sigmask);
// usage
sigset_t newmask, oldmask, zeromask;
signal(SIGINT, sig_int);
sigemptyset(&zeromask);
sigemptyset(&newmask);
sigaddset(&newmask, SIGINT);
sigprocmask(SIG_BLOCK, &newmask, &oldmask);
critical_section();
// wait for any signal and pause
sigsuspend(&zeromask);
// reset signal mask
sigprocmask(SIG_SETMASK, &oldmask, NULL);
sigwait (better way)
much cleaner, avoids races and errors
#include <signal.h>
int sigwait(const sigset_t *restrict sigmask, int *restrict sig)
int signo = SIGUSR1
if ((sigemptyset(&sigset) == -1) ||
(sigaddset(&sigset, signo) == -1) ||
(sigprocmask(SIG_BLOCK, &sigset, NULL))
while (true) {
if (sigwait(&sigset, &signo) == -1) {
perror("failed to wait using sigwait");
exit(1)
}
signalcount++;
fprintf(stderr, "Number of signals so far: %d\n", signalcount);
}
Signal Handling in Threads
Signals can be delivered
- Synchronously (to thread that caused it)
- Asynchronously (to thread that has it unblocked)
- Directed (to specific thread)
... TOO FAST! (to which he responds "read textbook")
Time and Timers
Current time represented as seconds after midnight of January 1, 1970
if time_t is long, then overflow happens in 2038. if time_t is unsigned long, then overflow happens in 2100-ish. if time_t is long long, then overflow happens in ... a long long time from now
Old Library
#include <time.h>
time_t time(time_t *tloc); // returns time in seconds
// convert to readable format
struct tm *localtime(const time_t *timer);
char *asctime(const struct tm* timeptr);
char *ctime(const time_t *clock);
New Library
#include <sys/time.h>
struct timeval {
time_t tv_sec; // seconds since epoch
time_t tv_usec; // microseconds
}
int gettimeofday(struct timeval *tp, NULL);
// stores time since epoch in tp
Sample Timer
#include <stdio.h>
#include <sys/time.h>
void activity_to_time(void);
int main(void) {
long timedif;
struct timeval tpstart, tpend
gettimeofday(&tpstart, NULL);
activity_to_time(void);
gettimeofday(&tpend, NULL);
timedif = 1000000L * (tpend.tv_sec - tpstart.tv_sec) + (tpend.tv_usec - tpstart.tv_usec);
printf("The function took %ld usec.\n", timedif);
}
Sleeping and waiting
sleep(unsigned seconds);
usleep(usecond_t microseconds);
nanosleep(...); // almost never used
None of the above functions are very accurate. (unless it's running on Mac OS X ;), based on his variance graph)
Timer Interrupt Handling
#include <sys/time.h>
int setitimer(int which, const struct itimerval *value, struct itimerval *old);
// where which is
// ITIMER_REAL (uses system time as a timer; generates SIGALRM)
// ITIMER_VIRTUAL (uses timer local to program's user space; generates SIGVTALRM)
// ITIMER_PROF (uses timer local to program's kernel space)
struct itimerval {
struct timeval it_value; // time until next expiration
struct timeval it_interval; // value to reload into timer after expiration
}