CSCE 313 Lecture 16

From Notes
Jump to navigation Jump to search

« 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

  1. Ignore the signal: works for most signals (hardware signals, SIGKILL, and SIGSTOP cannot be ignored)
  2. Catch the signal: Tell kernel to invoke a given function whenever a signal occurs. (e.g. catch SIGTERM and cleanup)
  3. 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
    1. it is unblocked
    2. 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
}