Overview

Last Week: ● How to program UNIX processes (Chapters 7-9) ● fork() and exec() Unix System Programming This Week, and next week: ● UNIX inter- communication mechanisms: signals, Signals » (next week) pipes and FIFOs. ● How to program with UNIX signals (Chapter 10) » http://en.wikipedia.org/wiki/Unix_signal ● Non-local jumps (Chapter 7) ● Focus on the sigaction() function

Maria Hybinette, UGA 1 Maria Hybinette, UGA 2

Outline What is a Signal?

● A is an asynchronous event which is ● What is a UNIX signal? delivered to a process (instantiated by a small message) ● Signal types ● Asynchronous means that the event can occur ● Generating signals at any time (e.g., posting at a bulletin board ) ● Responding to a signal » may be unrelated to the execution of the process ● Common uses of a signal – e.g., user types Ctrl-C, or the modem hangs (SIGINT) ● Implementing a read() time-out – e.g,, user types Ctrl-Z (SIGTSTP) ● Sent from kernel (e.g. detects divide by zero ● Non-local jumps setjmp()/longjmp() (SIGFPE) or could be at the request of another ● POSIX signals process to send to another) ● Interrupted system calls ● Only information that a signal carries is its ● System calls inside handlers unique ID and that it arrived

Maria Hybinette, UGA 3 Maria Hybinette, UGA 4

Signal Types (31 in POSIX) Signal Sources

terminal memory ID Name Description Default Action driver management shell command 2 2 SIGINT Interrupt from keyboard (^C) terminate Ctr-C SIGINT SIGHUP 3 3 SIGQUIT Quit from keyboard (^\) terminate & core SIGSEGV 9 9 SIGKILL -9 terminate SIGKILL,SIGCONT kernelsig 11 SIGSEGV Invalid memory reference terminate & core SIGPIPE

14 SIGALRM alarm() clock rings terminate SIGWINCH SIGALRM 17 SIGCHLD Child stopped or terminated! ignore Terminal, window 16 SIGUSR1 user-defined signal type terminate manager a process SIGUSR1 ● /usr/include/sys/iso/signal_iso.h on atlas (solaris) ● /usr/src/kernels/2.6.32-431.29.*/include/linux/signal.h SIGFPE other user ● processes /usrMaria Hybinette,/include/ UGA signal.h (user space)! 5 RunningMaria Hybinette, a.out UGA process (division by 0, floating point exception) 6 Generating a Signal kill()

● Use the UNIX command: {saffron} ./fork_example {saffron} kill -KILL 6676 Terminating Parent, PID = 6675 #include » sends a SIGKILL signal to Running Child, PID = 6676 int kill( pid_t pid, int signo ); {saffron} ps processor ID (pid) 6676 PID TTY TIME CMD 6585 ttyp9 00:00:00 tcsh ● Send a signal to a process (or group of processes). » check pid via (and also to 6676 ttyp9 00:00:06 fork_example ● make sure it died) {saffron} kill –s 9 6676 Return 0 if ok, -1 on error. {saffron} ps {saffron} ps -l PID TTY TIME CMD 6585 ttyp9 00:00:00 tcsh ● Meaning pid 6678 ttyp9 00:00:00 ps > 0 send signal to process pid ● kill is not a good name; == 0 send signal to all processes send_signal might be whose ID better. equals the senders pgid. e.g. parent kills all children ● How do we do this in a program?

Maria Hybinette, UGA 7 Maria Hybinette, UGA 8

Responding to a Signal signal()

#include ● After receiving a signal a process can:

1. Ignore/Discard/Block out the signal (not possible with void (*signal( int signo, void (*func)(int) ))(int); SIGKILL or SIGSTOP) 2. Catch the signal; execute a signal handler function, and typedef void Sigfunc( int ); /* Plauger 1992 definition */ then possibly resume execution or terminate Sigfunc *signal( int signo, Sigfunc *handler ); 3. Carry out the default action for that signal

● Signal returns a pointer to a function that returns an int (i.e. it ● The choice is called the processsignal returns a pointer to Sigfunc) disposition ● Specify a signal handler function to deal with a signal type. ● How is a process disposition set? ● Returns previous signal disposition if OK, SIG_ERR on error.

Maria Hybinette, UGA 9 Maria Hybinette, UGA 10

5. signal returns a pointer 4. The handler Sketch on how to program with to a function. 2. The signal to be function The return type is the sameActual caught Prototype or ignored Receives a single signals as the function that is passed is given as argument integer in, signo argument and i.e., a function that takes an returns void ● The actual prototype, listed in the man page is a bit int main() int and returns a void { perplexing but is an expansion of the Sigfunc type: signal( SIGINT, foo ); :

void (*signal( int signo, void (*handler)(int)))(int); /* do usual things until SIGINT */

return 0; ● signal returns a pointer to the previous signal handler }

3. The function to be void foo( int signo ) { #include called when the 6. The returned : /* deal with SIGINT signal */ typedef1. signal void takes Sigfunc(int); two specified /* Plauger signal is1992 */ function Sigfuncarguments: *signal( int signo,received Sigfunc is given *handler as ); takes a integer signo and handler parameter. return; /* return to program */ a pointer to the } function handler

Maria Hybinette, UGA 11 Maria Hybinette, UGA 12 External Signal Example: signal_example.c

#include int main( void ) {saffron:ingrid:54} signal_example 0: Received SIGUSR1 #include { 1: Received SIGUSR1 #include int i = 0; 2: Received SIGUSR2 [1] + Stopped (signal) if( signal( SIGUSR1, sig_usr ) == SIG_ERR ) signal_example static void sig_usr( int signo ) perror( "Cannot catch SIGUSR1\n" ); {saffron:ingrid:26} fg { if( signal( SIGUSR2, sig_usr ) == SIG_ERR ) signal_example 3: Received SIGUSR1 if( signo == SIGUSR1 ) perror( "Cannot catch SIGUSR2\n" ); Quit printf( "Received SIGUSR1\n" );

else if( signo == SIGUSR2 ) while( 1 ) printf( "Received SIGUSR2\n" ); { else printf( "%d: ", i ); {saffron:ingrid:55} kill -l= {saffron:ingrid:56} ps -l { pause(); /* until signal handler fprintf( stderr, "ERROR: received signal %d\n", signo ); has processed signal */ {saffron:ingrid:23} kill -USR1 1255 exit(1); i++; {saffron:ingrid:24} kill -USR1 1255 } } {saffron:ingrid:25} kill -USR2 1255 return; return 0; {saffron:ingrid:26} kill -STOP 1255 {saffron:ingrid:27} kill -CONT 1255 } ps –u } {saffron:ingrid:28} kill -USR1 1255

{saffron:ingrid:29} kill QUIT 1255 kill -SIGUSR2 21084

Maria Hybinette, UGA 13 Maria Hybinette, UGA 14

Internal Signal Example: signal_example2.c Special Sigfunc * Values

#include int main( void ) #include { #include int i = 0; ● Value Meaning if( signal( SIGALRM, handler ) == SIG_ERR ) int beeps = 0; perror( "Cannot catch SIGALRM\n" ); alarm(1); SIG_IGN Ignore / discard the signal. static void handler( int signo ) while( 1 ) { { printf( "BEEP\n" ); printf( "%d: ", i ); SIG_DFL Use default action to handle signal. fflush(stdout); pause(); i++; } SIG_ERR Returned by signal() as an error. if( ++beeps < 5 ) return 0; alarm(1); } else { {cinnamon} signal_example2 printf("BOOM!\n"); 0: BEEP exit(0); 1: BEEP } 2: BEEP return; 3: BEEP } 4: BEEP BOOM!

Maria Hybinette, UGA 15 Maria Hybinette, UGA 16

Multiple Signals pause()

● If many signals of the same type are waiting to #include be handled (e.g. two SIGINTs), then most UNIXs int pause(void); will only deliver one of them. » the others are thrown away - i.e. pending signals are not queued ● Suspend the calling process until a signal is caught. » for each signal type, just have a single bit indicating whether or not the signal has occured ● Returns -1 with errno assigned EINTR. ● If many signals of different types are waiting to (Linux assigns it ERESTARTNOHAND). be handled (e.g. a SIGINT, SIGSEGV, SIGUSR1), ● pause() only returns after a signal handler they are not delivered in any fixed order. has returned.

Maria Hybinette, UGA 17 Maria Hybinette, UGA 18 The Reset Problem Reset Problem Example

● In Linux (and many other UNIXs), the signal int main() disposition in a process is reset to its default { signal(SIGINT, foo); action immediately after the signal has been : delivered. /* do usual things until SIGINT */ }

void foo( int signo ) { ● Must call signal() again to reinstall the signal(SIGINT, foo); /* reinstall */ signal handler function. : return; }

Maria Hybinette, UGA 19 Maria Hybinette, UGA 20

To keep catching the signal with this function, must call Reset Problem the signal system Re-installation may be too slow! call again.

: void ouch( int sig ) ● There is a (very) small time period in foo() when { a new SIGINT signal will cause the default action printf( "OUCH! - I got signal %d\n", sig ); to be carried out -- process termination. (void) signal( SIGINT, ouch ); } Problem: from the time int main() that the interrupt function ● With there is no answer to this starts to just before the signal() { signal handler is re- problem. (void) signal( SIGINT, ouch ); established » POSIX signal functions solve it (and some other later while(1) the signal will not be UNIXs) { handled. printf( "Hello World!\n"); sleep(1); } If another SIGINT signal is } received during this time, default behavior will be done, i.e., program will terminate. Maria Hybinette, UGA 21 Maria Hybinette, UGA 22

Common Uses of Signals Ignore a Signal

: ● Ignore a signal int main() { ● Clean up and terminate signal( SIGINT, SIG_IGN ); signal( SIGQUIT, SIG_IGN ); ● Dynamic reconfiguration : /* do work without interruptions */ ● Report status }

● Turn debugging on/off ● Cannot ignore/handle SIGKILL or SIGSTOP ● Restore a previous handler ● Should check for SIG_ERR

Maria Hybinette, UGA 23 Maria Hybinette, UGA 24 Clean up and Terminate Checking the Disposition

● If a program is run in the new disposition : old disposition /* global variables */ background then the interrupt int my_children_pids; and quit signals (SIGINT, : : SIGQUIT) are automatically if( signal( SIGINT, SIG_IGN ) != SIG_IGN ) void clean_up( int signo ); ignored. signal( SIGINT, clean_up ); int main() ● Your code should not override if( signal( SIGQUIT, SIG_IGN ) != SIG_IGN ) { these changes: signal( SIGQUIT, clean_up); signal( SIGINT, clean_up ); : : » check if the signal dispositions } are SIG_IGN ● Note: cannot check the signal disposition without void clean_up( int signo ) changing it (sigaction that we will look at later, is { unlink( /tmp/work-file ); different) kill( my_children_pids, SIGTERM ); wait((int *)0); fprintf( stderr, terminated\n); exit(1); } Maria Hybinette, UGA 25 Maria Hybinette, UGA 26

Dynamic Reconfiguration Report Status

: ● Reset problem void print_status( int signo ) void read_config( int signo ); : { ● Handler interruption void print_status(int signo); signal( SIGUSR1, print_status ); int main() printf( %d blocks copied\n, count ); » what is the effect of a { return; read_config(0); SIGHUP in the middle of int count; /* global * } /* dummy argument */ read_config() s execution? int main() while (1) ● Can only affect global { /* work forever */ signal( SIGUSR1, print_status ); } variables. ● Reset problem : ● value not always void read_config( int signo ) for( count=0; count < BIG_NUM; count++ ) count { { defined. int fd; signal( SIGHUP, read_config ); /* read block from tape */ ● Must use global variables fd = open( config_file, O_RDONLY ); /* write block to disk */ for status information /* read file and set global vars */ } close(fd); ... return; } }

Maria Hybinette, UGA 27 Maria Hybinette, UGA 28

Turn Debugging On/Off Restore Previous Handler

void toggle_debug(int signo) : { signal(SIGUSR2, toggle_debug); Sigfunc *old_hand;

: debug = ((debug == 1) ? 0 : 1); /* set action for SIGTERM; void toggle_debug(int signo); save old handler */ return; old_hand = signal( SIGTERM, foobar ); /* initialize here */ } int debug = 0; /* do work */

int main() { /* restore old handler */ signal( SIGUSR2, toggle_debug ); signal( SIGTERM, old_hand ); : /* do work */ if (debug == 1) printf(...); ... }

Maria Hybinette, UGA 29 Maria Hybinette, UGA 30 Implementing a read() timeout alarm()

● Put an upper limit on an operation that might #include block forever long alarm( long secs );

» e.g. read() ● Set an alarm timer that will ring after a specified ● alarm() number of seconds ● Implementing various timeouts » a SIGALRM signal is generated » Bad read() timeout » setjmp() and longjmp() ● Returns 0 or number of seconds until previously » Better read() timeout set alarm would have rung.

Maria Hybinette, UGA 31 Maria Hybinette, UGA 32

Some Tricky Aspects Bad read() Timeout

#include if( signal( SIGALRM, sig_alrm ) == SIG_ERR ) #include { ● A process can have at most one alarm timer #include printf(signal(SIGALRM) error\n); exit(1); running at once. #define MAXLINE 512 } alarm(10); ● If alarm() is called when there is an existing void sig_alrm( int signo ); n = read( STDIN_FILENO, line, MAXLINE ); alarm set then it returns the number of int main() alarm(0); seconds remaining for the old alarm, and sets { if( n < 0 ) /* read error */ int n; fprintf( stderr, \nread error\n ); the timer to the new alarm value. char line[MAXLINE]; else » What do we do with the old alarm value? : write( STDOUT_FILENO, line, n ); return 0; ● An alarm(0) call causes the previous alarm } to be cancelled. void sig_alrm( int signo ) /* do nothing, just handle signal */ { return; }

Maria Hybinette, UGA 33 Maria Hybinette, UGA 34

[sig]setjmp() and Problems [sig]longjmp()

● The code assumes that the read() call terminates with an error after being interrupted ( talk about this later). ● In C we cannot use goto to jump to a label in another function ● Race Condition: The kernel may take longer than 10 » use [sig]setjmp() and [sig]longjmp() for those seconds to start the read() after the alarm() call. long jumps » the alarm may ring before the read() starts » then the read() is not being timed out; may block forever » Two ways two solve this one uses and the other setjmp ● Only uses which are good style: uses sigprocmask and sigsuspend » error handling which requires a deeply nested function to recover to a higher level (e.g. back to main()) » coding timeouts with signals

Maria Hybinette, UGA 35 Maria Hybinette, UGA 36 Nonlocal Jumps: [sig]setjmp() & [sig]longjmp() Prototypes

#include ● Powerful (but dangerous) user-level mechanism for int setjmp( jmp_buf env ); transferring control to an arbitrary location void longjmp( jmp_buf env, int val ); » controlled way to break the procedure call/return discipline ● Returns 0 if called directly, non-zero if returning from a call to » Useful for error recovery and signal recover longjmp(). ● setjmp( jmp_buf j ) ● In the setjmp() call, env is initialized to information about the » called before longjmp() current state of the stack. » identified return site for subsequent longjmp() ● The longjmp() call causes the stack to be reset to its jmp_buf » Called once, returns one or more times env value (so it never returns) ● Implementation: ● Execution restarts after the call, but this time » remember where you are by storing the current register setjmp() setjmp() context, stack pointer and PC value in jmp_buf returns val (so in way val is a way to send a message to the » returns 0 setjmp -- and consequently facilitates multiple longjmp s per setjmp)

Maria Hybinette, UGA 37 Maria Hybinette, UGA 38

Restart when ctrl-cd: setlongjmp.c Stack Frames at setjmp()

#include while( 1 ) #include { top of stack #include sleep(1); main() sigjmp_buf buf; printf("processing...\n"); void handler(int sig) stack frame } { } siglongjmp( buf, 1 ); setjmp(env) } returns 0; int main() {cinnamon:ingrid:34} setlongjmp env records stack direction of { starting frames info signal(SIGINT, handler); processing... stack growth if( !sigsetjmp( buf, 1 ) ) ^Crestarting Ctrl-C printf("starting\n"); restarting else processing... printf("restarting\n"); Terminated kill -9 {cinnamon:ingrid:35}

Maria Hybinette, UGA 39 Maria Hybinette, UGA 40

Stack frames at longjmp() Implementing sleep1()

top of stack ● Using alarm() and pause() we can

main() implement our own sleep() function (a sleep stack frame function puts a process to sleep for a specified amount of time. process_line() stack frame ● Idea: Use pause() that waits for a specific direction of amount of time until we get a signal. stack growth : ● Set the amount of time we want to sleep via : alarm(). longjmp( env, 1 ) cmd_add() causes stack frames stack frame to be reset

Maria Hybinette, UGA 41 Maria Hybinette, UGA 42 Implementing: sleep1() sleep2(): Avoids the race condition

#include unsigned int sleep1( unsigned int nsecs ) #include unsigned int sleep2( unsigned int nsecs ) #include { #include { if( signal( SIGALRM, sig_alrm ) == SIG_ERR ) #include if( signal( SIGALRM, sig_alrm ) == SIG_ERR ) void sig_alrm( int signo ) return (nsecs); return ( nsecs ); alarm( nsecs ); /* starts timer */ static void jmp_buf env_alrm; if( setjmp( env_alrm ) == 0 ) { pause(); /* caught signal wakes */ { return; return( alarm( 0 ) ); /* turn off timer return void sig_alrm( int signo ) alarm( nsecs ); /* starts timer */ /* return to wake up pause */ un-slept time */ { pause(); } } longjmp( env_alrm, 1 ); } } return( alarm( 0 ) ); ● Alarm erases old set alarms } » Look at return value from the previous alarm() call – If less than new alarm() - wait until old alarm() expires ● sleep2() fixes race condition. Even if the pause is never – If larger than new alarm()- reset old alarm() with executed. remaining seconds when done with new alarm() » A SIGALRM causes sleep2() to return ● Lose old disposition of SIGALRM » Avoids entering pause() via longjmp() » Save old disposition and restore when done ● Race condition ● There is one more problem » between first call to alarm and the call to pause ⇒ never get out of » SIGALRM could interrupt some other signal handler and pause (fix via setjmp/longjmp or sigprocmask/sigsuspend) subsequently abort it by executing the longjmp() Maria Hybinette, UGA 43 Maria Hybinette, UGA 44

Problem Interrupted Handler

int main( void ) {saffron} a.out { ^C unsigned int unslept; sig_int starting sleep2 returned: 0 ● If the program has several signal handlers if( signal( SIGINT, sig_int ) == SIG_ERR ) then: perror( signal(SIGINT) error ); unslept = sleep2( 5 ); printf( sleep2 returned: %u\n, unslept ); » execution might be inside another one when an alarm exit(0) ● rings } Here: longjmp() aborts the sig_int signal handler even if it did not complete (the for » the longjmp() call will jump to the setjmp() void sig_int( int signo ) loop) location, and abort the other signal handler -- might { int i; ● We will see ways around these lose / corrupt data int j; printf( sig_int starting\n ); problems soon. for( i = 0; i < 2000000; i++ ) j += i * i ; printf( sig_int finished\n ); return; }

Maria Hybinette, UGA 45 Maria Hybinette, UGA 46

Status of Variables after longjmp? Better read() Timeout

int main( void ) void sig_alrm(int signo) { { ● The POSIX standard says: int n; longjmp( env_alrm, 1 ); char line[MAXLINE]; }

» global and static variable values will be left alone by the if( signal( SIGALRM, sig_alrm ) == SIG_ERR ) longjmp()call { printf( signal(SIGALRM) error\n ); exit(1); ● Solves earlier Race Conditions: ● Nothing is specified about local variables, are } if( setjmp( env_alrm ) != 0 ) » Now if alarm occurs before it they rolled back to their original values (at the { gets to read it jumps to fprintf( stderr, \nread() too slow\n); setjmp at exits instead of setjmp call) as the stack? exit( 2 ); doing nothing and blocks } forever in the read » It depends: they may be restored to their values at the alarm(10); » and if the system call is re- n = read( 0, line, MAXLINE ); first setjmp(), but maybe not started the return of the signal alarm(0); – Most implementations do not roll back their values handler still have an effect if( n < 0 ) /* read error */ » still have same problem with fprintf( stderr, \nread error\n ); other signal handlers… else write( 1, line, n ); return 0; Maria Hybinette, UGA 47 } Maria Hybinette, UGA 48 Caveat: Non-local Jumps POSIX Signal Functions

● The POSIX signal functions can control signals From the UNIX man pages: in more ways:

» can block signals for a while, and deliver them later WARNINGS (good for coding critical sections) If longjmp() or siglongjmp() are called even though env was

never primed by a call to setjmp() or sigsetjmp(), or when the last such call was in a function that has since » can switch off the resetting of the signal disposition returned, absolute chaos is guaranteed. when a handler is called (no reset problem)

» can queue pending signals

Maria Hybinette, UGA 49 Maria Hybinette, UGA 50

Signal Sets

● The POSIX signal system, uses signal sets, to deal with ● The signal set stores collections of signal pending signals that might otherwise be missed while a types. signal is being processed ● Sets are used by signal functions to define which signal types are to be processed.

● POSIX contains several functions for creating, changing and examining signal sets.

Maria Hybinette, UGA 51 Maria Hybinette, UGA 52

POSIX.1 Prototypes sigprocmask()

#include

int sigemptyset( sigset_t *set ); ● A process uses a signal set to create a mask which int sigfillset( sigset_t *set ); defines the signals it is blocking from delivery. – good for critical sections where you want to block certain int sigaddset( sigset_t *set, int signo ); signals. int sigdelset( sigset_t *set, int signo );

#include int sigismember( const sigset_t *set, int signo ); int sigprocmask( int how, const sigset_t *set, sigset_t *oldset); ● sigemptyset - initializes signal set pointed by set so that all signals are excluded ● how – indicates how mask is modified (later) ● sigfillset - all signals are included ● oldset - current signal mask ● sigaddset - add a single signal (signo) to set ● sigdelset - remove signo from set Maria Hybinette, UGA 53 Maria Hybinette, UGA 54 how Meanings Example: A Critical Code Region

: ● Value Meaning sigset_t newmask, oldmask;

sigemptyset( &newmask ); signals are added to mask SIG_BLOCK set sigaddset( &newmask, SIGINT );

SIG_UNBLOCK set signals are removed from mask /* block SIGINT; save old mask */ sigprocmask( SIG_BLOCK, &newmask, &oldmask ); SIG_SETMASK set becomes new mask /* critical region of code */

/* reset mask which unblocks SIGINT */ sigprocmask( SIG_SETMASK, &oldmask, NULL ); :

Maria Hybinette, UGA 55 Maria Hybinette, UGA 56

sigaction() sigaction() Structure

struct sigaction { #include void (*sa_handler)( int ); /* the action or SIG_IGN, SIG_DFL */ int sigaction( int signo, const struct sigaction *act, sigset_t sa_mask; /* additional signal to be blocked */ struct sigaction *oldact ); int sa_flags; /* modifies action of the signal */ void (*sa_sigaction)( int, siginfo_t *, void * ); } ● Supercedes (more powerful than) signal()

» sigaction() can be used to code a non-resetting signal() ● sa_flags – modifies the behaviour of signo ● signo is the signal you want to perform an action on » SIG_DFL reset handler to default upon return ● is the action act » SA_SIGINFO denotes extra information is passed to handler (.i.e. ● oact is the old action (can be set to NULL, if uninteresting) specifies the use of the second handler in the structure. ● Cannot handle SIGSTOP and SIGKILL

Maria Hybinette, UGA 57 Maria Hybinette, UGA 58

sigaction() Behavior Signal Raisingstruct sigaction { void (*) (int) sa_handler {cinnamon:ingrid:8} sigact #include sigset_t sa_mask #include Hello World! int sa_flags ● A signal causes the signal void ouch( int signo ) Hello World! signo sa_handler { printf( OUCH! signo = %d\n, signo ); } } Hello World! handler to be called. int main() Hello World! { struct sigaction act; OUCH! signo = 2 ● While sa_handler executes, the signals in HelloNo World!flags are needed here." act.sa_handler = ouch; Possible flags include:" sa_mask are blocked. Any more signo signals Hello World! Hello World! are also blocked. sigemptyset( &(act.sa_mask) ); HelloSA_NOCLDSTOP World! SA_RESETHAND Set the signal handler to" act.sa_flags = 0; TerminatedSA_RESTART be the function SA_NODEFERouch" ● sa_handler remains installed until it is sigaction( SIGINT, &act, NULL ); " changed by another sigaction() call. No while( 1 ) { reset problem. We can manipulate" printf("Hello World!\n"); This call sets the signal " sets of signals sleep(1);.." handler for the SIGINT" } ( ) signal" } Ctrl-c Maria Hybinette, UGA 59 Maria Hybinette, UGA 60

Signal Raising sigexPOS.c

/* sigexPOS.c - demonstrate sigaction() */ ● This function will continually capture the /* include files as before */ Ctrl-c (SIGINT) signal. int main(void) { ● Default behavior is not restored after signal is caught. /* struct to deal with action on signal set */ static struct sigaction act;

● To terminate the program, must type ctrl-\, void catchint( int ); /* user signal handler */ the SIGQUIT signal (or sent a TERM signal via kill) /* set up action to take on receipt of SIGINT */ act.sa_handler = catchint;

Maria Hybinette, UGA 61 Maria Hybinette, UGA 62

Signals - Ignoring signals

/* create full set of signals */ sigfillset(&(act.sa_mask)); ● Other than SIGKILL and SIGSTOP, signals can be ignored: /* before sigaction call, SIGINT will terminate * process */ ● Instead of in the previous program: /* now, SIGINT will cause catchint to be executed */ sigaction( SIGINT, &act, NULL ); act.sa_handler = catchint /* or whatever */ sigaction( SIGQUIT, &act, NULL ); We use: printf("sleep call #1\n"); act.sa_handler = SIG_IGN; sleep(1); Then the ^C key will be ignored

/* rest of program as before */

Maria Hybinette, UGA 63 Maria Hybinette, UGA 64

Restoring previous action A BetterReliable signal()

#include

● The third parameter to sigaction, oact, can Sigfunc *signal( int signo, Sigfunc *func ) be used: { struct sigaction act, oact;

act.sa_handler = func; /* save old action */ sigemptyset( &act.sa_mask ); sigaction( SIGTERM, NULL, &oact ); act.sa_flags = 0;

/* set new action */ act.sa_flags |= SA_INTERRUPT; act.sa_handler = SIG_IGN; if( signo != SIGALRM ) act.sa_flags |= SA_RESTART; sigaction( SIGTERM, &act, NULL ); /* any system call interrupted by a signal * other than alarm is restarted */

if( sigaction( signo, &act, &oact ) < 0 ) /* restore old action */ return(SIG_ERR); sigaction( SIGTERM, &oact, NULL ); return( oact.sa_handler ); } Maria Hybinette, UGA 65 Maria Hybinette, UGA 66 Other POSIX Functions [sig]longjmp & [sig]setjmp

● sigpending() examine blocked signals NOTES (longjmp, sigjmp) POSIX does not specify whether longjmp will restore ● sigsetjmp() the signal context. If you want to save and restore siglongjmp() jump functions for use signal masks, use siglongjmp. in signal handlers which handle masks correctly NOTES (setjmp, sigjmp) POSIX does not specify whether setjmp will save the ● sigsuspend() atomically reset mask signal context. (In SYSV it will not. In BSD4.3 it will, and sleep and there is a function _setjmp that will not.) If you want to save signal masks, use sigsetjmp.

Maria Hybinette, UGA 67 Maria Hybinette, UGA 68

Example Interrupted System Calls

#include … #include while(1) #include { ● When a system call (e.g. read()) is sleep(5); sigjmp_buf buf; printf( waiting...\n"); interrupted by a signal, a signal handler is } called, returns, and then what? void handler(int sig) } { siglongjmp(buf, 1); > a.out ● On many UNIXs, slow system function calls } starting waiting... do not resume. Instead they return an error main() waiting... Control-c and is assigned . { restarting errno EINTR signal(SIGINT, handler); waiting... » true of Linux, but can be altered with (Linux- waiting... specific) siginterrupt() if( sigsetjmp(buf, 1) == 0 ) waiting... printf("starting\n"); restarting Control-c else waiting... printf("restarting restarting Control-c \n"); waiting...

Maria… Hybinette, UGA waiting... 69 Maria Hybinette, UGA 70

Slow System Functions Non-slow System Functions

● Slow system functions carry out I/O on things ● Most system functions are non-slow, that can possibly block the caller forever: including ones that do disk I/O » pipes, terminal drivers, networks » e.g. read() of a disk file » some IPC functions » read() is sometimes a slow function, sometimes not » pause(), some uses of ioctl()

● Some UNIXs resume non-slow system ● Can use signals on slow system functions to functions after the handler has finished. code up timeouts (e.g. did earlier ) ● Some UNIXs only call the handler after the non-slow system function call has finished.

Maria Hybinette, UGA 71 Maria Hybinette, UGA 72 System Calls inside Handlers Non-reentrant functions

● If a system function is called inside a signal ● A functions may be non-reentrant (only handler then it may interact with an interrupted one call to it at once) for a number of call to the same function in the main code. reasons: » e.g. malloc() » it uses a static data structure » it manipulates the heap: malloc(), free(), etc. ● This is not a problem if the function is reentrant » it uses the standard I/O library » a process can contain multiple calls to these functions – e,g, scanf(), printf() at the same time – the library uses global data structures in a non-reentrant way » e.g. read(), write(), fork(), many more

Maria Hybinette, UGA 73 Maria Hybinette, UGA 74

errno problem Limitations of Nonlocal Jumps

● Works within stack discipline » Can only long jump to environment of function that has been called ● errno is usually represented by a global but not yet completed variable. jmp_buf env; P1

P1() env P2 ● Its value in the program can be changed { P2(); P3(); At setjmp" suddenly by a signal handler which produces } a new system function error. P1 P2() { if( setjmp( env ) ) env X" P2 /* long jump to here */ } P2 returns" P3() { P1 longjmp( env, 1 ); env X" } P3

At longjmp"

Maria Hybinette, UGA 75 Maria Hybinette, UGA 76