Overview
Last Week: ● How to program UNIX processes (Chapters 7-9) ● fork() and exec() Unix System Programming This Week, and next week: ● UNIX inter-process 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 signal 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 kill -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
Maria Hybinette, UGA 7 Maria Hybinette, UGA 8
Responding to a Signal signal()
#include
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 process signal 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
Maria Hybinette, UGA 11 Maria Hybinette, UGA 12 External Signal Example: signal_example.c
#include
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
Maria Hybinette, UGA 15 Maria Hybinette, UGA 16
Multiple Signals pause()
● If many signals of the same type are waiting to #include
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
» 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
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
Maria Hybinette, UGA 37 Maria Hybinette, UGA 38
Restart when ctrl-c d: setlongjmp.c Stack Frames at setjmp()
#include
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
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
: ● 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
» 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
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 Better Reliable 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
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