Вы находитесь на странице: 1из 13

Overview

Last Week:
! How to program UNIX processes (Chapters 7-9)
! fork() and exec()
Unix System Programming
This Week:
Signals ! UNIX inter-process communication mechanism:
signals, pipes and FIFOs.
! How to program with UNIX signals (Chapter 10)
! Non-local jumps (Chapter 7)
! Focus on the sigaction() function

Maria Hybinette, UGA 1 Maria Hybinette, UGA 2

Outline What is a Signal?

! What is a UNIX signal? ! A signal is an asynchronous event which is


! Signal types delivered to a process (instantiated by a small
message)
! Generating signals
! Asynchronous means that the event can occur
! Responding to a signal at any time
! Common uses of a signal » may be unrelated to the execution of the process
! Implementing a read() time-out » e.g. user types Ctrl-C, or the modem hangs
! Non-local jumps setjmp()/longjmp() ! Sent from kernel (e.g. detects divide by zero
(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
2 SIGINT Interrupt from keyboard (^C) terminate
shell command
SIGINT SIGHUP
3 SIGQUIT Quit from keyboard (^\) terminate & core
SIGQUIT
9 SIGKILL kill -9 terminate SIGKILL
SIGPIPE
kernel
11 SIGSEGV Invalid memory reference terminate & core

14 SIGALRM alarm() clock ‘rings’ terminate


SIGWINCH SIGALRM
17 SIGCHLD Child stopped or terminated ignore

16 SIGUSR1 user-defined signal type terminate


window
manager a process
SIGUSR1
! /usr/include/sys/iso/signal_iso.h on atlas
other user
Maria Hybinette, UGA 5 Maria Hybinette, UGA
processes 6
Generating a Signal kill()

! Use the UNIX command: {saffron} ./fork_example


#include <signal.h>
{saffron} kill -KILL 6676 Terminating Parent, PID = 6675
Running Child, PID = 6676 int kill( pid_t pid, int signo );
» sends a SIGKILL signal to {saffron} ps
PID TTY TIME CMD
processor ID (pid) 6676 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
{saffron} kill –s 9 6676 ! Return 0 if ok, -1 on error.
make sure it died) {saffron} ps
PID TTY TIME CMD
{saffron} ps -l 6585 ttyp9 00:00:00 tcsh ! pid Meaning
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 process group ID
better. equals the sender’s pgid.
e.g. parent kills all children

Maria Hybinette, UGA 7 Maria Hybinette, UGA 8

Responding to a Signal signal()

#include <signal.h>
! 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 process’ signal disposition returns a pointer to Sigfunc)
! Specify a signal handler function to deal with a signal type.
! Returns previous signal disposition if OK, SIG_ERR on error.

Maria Hybinette, UGA 9 Maria Hybinette, UGA 10

5. signal returns a pointer


to a function. 2. The signal to be
4. The handler
function
Sketch on how to program with
The return type is the same Actual Prototype
caught 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
int and prototype,
returns a void listed in the “man”
page is a bit int main()
{
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 void foo( int signo )


{
#include <signal.h> be called when the 6. The returned : /* deal with SIGINT signal */
1. signal
typedef voidtakes two
Sigfunc(int); /* Plauger function
specified signal is1992 */
arguments:
Sigfunc *signal( int signo, Sigfunc
received *handler
is given 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 <stdio.h> int main( void ) {saffron:ingrid:54} signal_example
0: Received SIGUSR1
#include <signal.h> { 1: Received SIGUSR1
#include <unistd.h> 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 */
exit(1); i++; {saffron:ingrid:23} kill -USR1 1255
{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
} } {saffron:ingrid:28} kill -USR1 1255

{saffron:ingrid:29} kill QUIT 1255

Maria Hybinette, UGA 13 Maria Hybinette, UGA 14

Internal Signal Example:


signal_example2.c Special Sigfunc * Values
#include <stdio.h> int main( void )
#include <signal.h> {
#include <unistd.h> int i = 0;
if( signal( SIGALRM, handler ) == SIG_ERR )
! Value Meaning
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 <unistd.h>


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
» for each signal type, just have a single bit indicating
caught.
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
Reset Problem function, must call
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 );
(void) signal( SIGINT, ouch );
to be carried out -- process termination.
} Problem: from the time
int main() that the interrupt function ! With signal() there is no answer to this
starts to just before the
{
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
void clean_up( int signo );
if( signal( SIGINT, SIG_IGN ) != SIG_IGN )
ignored. signal( SIGINT, clean_up );
int main() ! Your code should not override if( signal( SIGQUIT, SIG_IGN ) != SIG_IGN )
{ signal( SIGQUIT, clean_up);
these changes:
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 signal( SIGUSR1, print_status );
void print_status(int signo);
int main()
{
» what is the effect of a printf( “%d blocks copied\n”, count );
return;
read_config(0); SIGHUP in the middle of int count; /* global * }
/* dummy argument */ read_config()’s
execution? int main()
while (1)
/* work forever */
! Can only affect global {

} variables. signal( SIGUSR1, print_status ); ! Reset problem


:
void read_config( int signo ) for( count=0; count < BIG_NUM; count++ )
! count value not always
{ { 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 <unistd.h>


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 <stdio.h> if( signal( SIGALRM, sig_alrm ) == SIG_ERR )
#include <unistd.h> {
! A process can have at most one alarm timer #include <signal.h> printf(“signal(SIGALRM) error\n”);
running at once. #define MAXLINE 512
exit(1);
}

! If alarm() is called when there is an existing void sig_alrm( int signo );


alarm(10);
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 {


int n;
if( n < 0 ) /* read error */
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

Problems setjmp() and 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 setjmp() and longjmp() for those ‘long jumps’
seconds to start the read() after the alarm() call.
» the alarm may ‘ring’ before the read() starts
» then the read() is not being timed out; may block forever
! Only uses which are good style:
» Two ways two solve this one uses setjmp and the other
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: setjmp() &
longjmp() Prototypes
#include <setjmp.h>
! 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
» Useful for error recovery and signal recover
! Returns 0 if called directly, non-zero if returning from a
call to longjmp().
! setjmp( jmp_buf j)
» called before longjmp() ! In the setjmp() call, env is initialized to information
» identified return site for subsequent longjmp() about the current state of the stack.
» Called once, returns one or more times ! The longjmp() call causes the stack to be reset to its
! Implementation: jmp_buf env value (never returns)
» remember where you are by storing the current register ! Execution restarts after the setjmp() call, but this
context, stack pointer and PC value in jmp_buf
» returns 0
time setjmp() returns val.

Maria Hybinette, UGA 37 Maria Hybinette, UGA 38

Restart when ctrl-c’d:


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

Maria Hybinette, UGA 39 Maria Hybinette, UGA 40

Another use for


Stack frames at longjmp() setjmp/longjmp(): sleep1()
#include <signal.h> unsigned int sleep1( unsigned int nsecs )
#include <unistd.h> {
if( signal( SIGALRM, sig_alrm ) == SIG_ERR )
top of stack void sig_alrm( int signo ) return (nsecs);
alarm( nsecs ); /* starts timer */
{
main() pause(); /* caught signal wakes */
return;
stack frame return( alarm( 0 ) ); /* turn off timer return
/* return to wake up pause */
un-slept time */
} }
process_line()
stack frame ! Alarm erases “old” set alarms
direction of » Look at return value from the previous alarm() call
stack growth : – If less than new alarm() - wait until old alarm() expires
: – If larger than new alarm()- reset old alarm() with
remaining seconds when done with new alarm()
longjmp( env, 1 ) ! Lose old disposition of SIGALRM
cmd_add() causes stack frames » Save old disposition and restore when done
stack frame to be reset ! Race condition
» between first call to alarm and the call to pause ! never get out of
pause (fix via setjmp/longjmp or sigprocmask/sigsuspend)
Maria Hybinette, UGA 41 Maria Hybinette, UGA 42
sleep2(): Avoids the race condition Status of Variables?
#include <signal.h> unsigned int sleep2( unsigned int nsecs )
#include <unistd.h> {
#include <setjmp.jmp> if( signal( SIGALRM, sig_alrm ) == SIG_ERR )
return ( nsecs ); ! The POSIX standard says:
static void jmp_buf env_alrm; if( setjmp( env_alrm ) == 0 )
{ » global and static variable values will not be changed by
void sig_alrm( int signo ) alarm( nsecs ); /* starts timer */ the longjmp() call
{ pause();
longjmp( env_alrm, 1 ); }
} return( alarm( 0 ) );
}
! Nothing is specified about local variables, are
they “rolled back” to their original values (at the
! sleep2() fixes race condition. Even if the pause is never setjmp call) as the stack”?
executed. » they may be restored to their values at the first
» A SIGALRM causes sleep2() to return setjmp(), but maybe not
» Avoids entering pause() via longjmp() – Most implementations do not roll back their values
! There is one more problem
» SIGALRM could interrupt some other signal handler and
subsequently abort it by executing the longjmp
Maria Hybinette, UGA 43 Maria Hybinette, UGA 44

Better read() Timeout Caveat: Non-local Jumps


int main( void ) void sig_alrm(int signo)
{ {
int n;
char line[MAXLINE];
longjmp( env_alrm, 1 ); From the UNIX man pages:
}

if( signal( SIGALRM, sig_alrm ) == SIG_ERR )


{
printf( “signal(SIGALRM) error\n” ); WARNINGS
exit(1);
} ! Solves earlier Race Conditions: If longjmp() or siglongjmp() are called even though env was
if( setjmp( env_alrm ) != 0 ) » Now if alarm occurs “before” it never primed by a call to setjmp() or sigsetjmp(), or when
{ gets to “read” it jumps to the last such call was in a function that has since
fprintf( stderr, “\nread() too slow\n”); setjmp at exits instead of doing
exit( 2 ); returned, absolute chaos is guaranteed.
nothing and blocks forever in
} the read
alarm(10);
n = read( 0, line, MAXLINE ); » and if the system call is re-
alarm(0); started the return of the signal
handler still have an effect
if( n < 0 ) /* read error */
fprintf( stderr, “\nread error\n” );
else
write( 1, line, n );
return 0;
} Maria Hybinette, UGA 45 Maria Hybinette, UGA 46

A Problem Remains! Interrupted Handler


int main( void ) {saffron} a.out
{ ^C
unsigned int unslept; sig_int starting

Recall: If the program has several signal


sleep2 returned: 0
! if( signal( SIGINT, sig_int ) == SIG_ERR )

handlers then: perror( “signal(SIGINT) error” );


unslept = sleep2( 5 );
printf( “sleep2 returned: %u\n”, unslept );
» execution might be inside one when an alarm ‘rings’ exit(0)
} ! Here: longjmp() aborts the
sig_int signal handler
» the longjmp() call will jump to the setjmp() even if it did not complete
location, and abort the other signal handler -- might void sig_int( int signo )
(the for loop)
{
lose / corrupt data int i;
int j;
printf( “sig_int starting\n” );
for( i = 0; i < 2000000; i++ )
j += i * i ;
printf( “sig_int finished\n” );
return;
}

Maria Hybinette, UGA 47 Maria Hybinette, UGA 48


POSIX Signal Functions

! The POSIX signal functions can control signals


in more ways: ! The POSIX signal system, uses signal sets, to deal with
pending signals that might otherwise be missed while a
» can block signals for a while, and deliver them later signal is being processed
(good for coding critical sections)

» can switch off the resetting of the signal disposition


when a handler is called (no reset problem)

» can queue pending signals

Maria Hybinette, UGA 49 Maria Hybinette, UGA 50

Signal Sets POSIX.1 Prototypes


#include <signal.h>

! The signal set stores collections of signal int sigemptyset( sigset_t *set );
int sigfillset( sigset_t *set );
types.
int sigaddset( sigset_t *set, int signo );
! Sets are used by signal functions to define int sigdelset( sigset_t *set, int signo );

which signal types are to be processed. int sigismember( const sigset_t *set, int signo );

! POSIX contains several functions for creating, ! sigemptyset - initializes signal set pointed by set
changing and examining signal sets. so that all signals are excluded
! sigfillset - all signals are included
! sigaddset - add a single signal (signo) to set
! sigdelset - remove signo from set
Maria Hybinette, UGA 51 Maria Hybinette, UGA 52

sigprocmask() how Meanings

! A process uses a signal set to create a mask ! Value Meaning


which defines the signals it is blocking from
SIG_BLOCK set signals are added to mask
delivery. – good for critical sections where you
want to block certain signals. SIG_UNBLOCK set signals are removed from mask

SIG_SETMASK set becomes new mask


#include <signal.h>
int sigprocmask( int how,
const sigset_t *set, sigset_t *oldset);

! how – indicates how mask is modified (later)

Maria Hybinette, UGA 53 Maria Hybinette, UGA 54


Example: A Critical Code Region sigaction()
:
sigset_t newmask, oldmask; #include <signal.h>
int sigaction( int signo, const struct sigaction *act,
sigemptyset( &newmask ); struct sigaction *oldact );
sigaddset( &newmask, SIGINT );

/* block SIGINT; save old mask */ ! Supercedes (more powerful than) signal()
sigprocmask( SIG_BLOCK, &newmask, &oldmask ); » sigaction() can be used to code a non-resetting signal()

/* critical region of code */ ! signo is the signal you want to perform an action on
! act is the action
/* reset mask which unblocks SIGINT */
sigprocmask( SIG_SETMASK, &oldmask, NULL ); ! oact is the old action (can be set to NULL, if uninteresting)
: ! Cannot handle SIGSTOP and SIGKILL

Maria Hybinette, UGA 55 Maria Hybinette, UGA 56

sigaction() Structure sigaction() Behavior


struct sigaction
{
void (*sa_handler)( int ); /* the action or SIG_IGN, SIG_DFL */ ! A signo signal causes the sa_handler signal
sigset_t sa_mask; /* additional signal to be blocked */
int sa_flags; /* modifies action of the signal */
handler to be called.
void (*sa_sigaction)( int, siginfo_t *, void * );
} ! While sa_handler executes, the signals in
sa_mask are blocked. Any more signo signals
! sa_flags – modifies the behaviour of signo
are also blocked.
» SIG_DFL reset handler to default upon return
» SA_SIGINFO denotes extra information is passed to handler (.i.e.
specifies the use of the “second” handler in the structure. ! sa_handler remains installed until it is
changed by another sigaction() call. No
reset problem.

Maria Hybinette, UGA 57 Maria Hybinette, UGA 58

Signal Raising
struct sigaction Signal Raising
{
void (*) (int) sa_handler
#include <signal.h> {cinnamon:ingrid:8} sigact
sigset_t sa_mask
#include <stdio.h> Hello World!
int sa_flags
void ouch( int signo )
{ printf( “OUCH! signo = %d\n, signo ); }
Hello World!
}
! This function will continually capture the
Hello World! Ctrl-c (SIGINT) signal.
int main() Hello World!
{
struct sigaction act; OUCH! signo = 2
No World!
Hello flags are needed here. ! Default behavior is not restored after signal
act.sa_handler = ouch;
Possible
Hello World! flags
Helloinclude:
World!
is caught.
sigemptyset( &(act.sa_mask) ); SA_NOCLDSTOP
Hello World!
SA_RESETHAND
act.sa_flags = 0; SetTerminated
the signal handler to
SA_RESTART ! To terminate the program, must type ctrl-\,
function ouch
be theSA_NODEFER
sigaction( SIGINT, &act, NULL ); the SIGQUIT signal (or sent a TERM signal via
kill)
while( 1 )
{
We can manipulate
printf("Hello World!\n"); This call sets the signal
sets of signals..
sleep(1); handler for the SIGINT
}
(Ctrl-c) signal
}
Maria Hybinette, UGA 59 Maria Hybinette, UGA 60
sigexPOS.c

/* sigexPOS.c - demonstrate sigaction() */ /* create full set of signals */


/* include files as before */ sigfillset(&(act.sa_mask));

int main(void) /* before sigaction call, SIGINT will terminate


{ * process */
/* struct to deal with action on signal set */
static struct sigaction act; /* now, SIGINT will cause catchint to be executed */
sigaction( SIGINT, &act, NULL );
void catchint( int ); /* user signal handler */ sigaction( SIGQUIT, &act, NULL );

/* set up action to take on receipt of SIGINT */ printf("sleep call #1\n");


act.sa_handler = catchint; sleep(1);

/* rest of program as before */

Maria Hybinette, UGA 61 Maria Hybinette, UGA 62

Signals - Ignoring signals Restoring previous action

! Other than SIGKILL and SIGSTOP, signals ! The third parameter to sigaction, oact, can
can be ignored: be used:

/* save old action */


! Instead of in the previous program: sigaction( SIGTERM, NULL, &oact );

act.sa_handler = catchint /* or whatever */ /* set new action */


We use: act.sa_handler = SIG_IGN;

act.sa_handler = SIG_IGN;
sigaction( SIGTERM, &act, NULL );
Then the ^C key will be ignored

/* restore old action */


sigaction( SIGTERM, &oact, NULL );

Maria Hybinette, UGA 63 Maria Hybinette, UGA 64

A “Better”Reliable signal() Other POSIX Functions


#include <signal.h>

Sigfunc *signal( int signo, Sigfunc *func ) ! sigpending() examine blocked signals
{
struct sigaction act, oact;
! sigsetjmp()
act.sa_handler = func; siglongjmp() jump functions for use
sigemptyset( &act.sa_mask ); in signal handlers which
act.sa_flags = 0; handle masks correctly
act.sa_flags |= SA_INTERRUPT;
if( signo != SIGALRM )
! sigsuspend() atomically reset mask
act.sa_flags |= SA_RESTART;
and sleep
/* any system call interrupted by a signal
* other than alarm is restarted */
if( sigaction( signo, &act, &oact ) < 0 )
return(SIG_ERR);
return( oact.sa_handler );
}
Maria Hybinette, UGA 65 Maria Hybinette, UGA 66
[sig]longjmp & [sig]setjmp Example
#include <stdio.h> …
#include <signal.h> while(1)
NOTES (longjmp, sigjmp) #include <setjmp.h> {
POSIX does not specify whether longjmp will restore sleep(5);
sigjmp_buf buf; printf(“ waiting...\n");
the signal context. If you want to save and restore }
signal masks, use siglongjmp. void handler(int sig) }
{
siglongjmp(buf, 1); > a.out
NOTES (setjmp, sigjmp) } starting
POSIX does not specify whether setjmp will save the waiting...
signal context. (In SYSV it will not. In BSD4.3 it will, main() waiting...
Control-c
and there is a function _setjmp that will not.) If you { restarting
signal(SIGINT, handler); waiting...
want to save signal masks, use sigsetjmp.
waiting...
if( sigsetjmp(buf, 1) == 0 ) waiting...
printf("starting\n"); restarting Control-c
else waiting...
restarting Control-c
printf("restarting\n"); waiting...
Maria Hybinette, UGA 67 … Hybinette, UGA
Maria waiting... 68

Interrupted System Calls Slow System Functions

! When a system call (e.g. read()) is ! Slow system functions carry out I/O on things
interrupted by a signal, a signal handler is that can possibly block the caller forever:
called, returns, and then what? » pipes, terminal drivers, networks
» some IPC functions
! On many UNIXs, slow system function calls » pause(), some uses of ioctl()
do not resume. Instead they return an error
and errno is assigned EINTR. ! Can use signals on slow system functions to
» true of Linux, but can be altered with (Linux- code up timeouts (e.g. did earlier )
specific) siginterrupt()

Maria Hybinette, UGA 69 Maria Hybinette, UGA 70

Non-slow System Functions System Calls inside Handlers

! Most system functions are non-slow, ! If a system function is called inside a signal
including ones that do disk I/O handler then it may interact with an interrupted
» e.g. read() of a disk file call to the same function in the main code.
» read() is sometimes a slow function, sometimes » e.g. malloc()
not

! This is not a problem if the function is reentrant


! Some UNIXs resume non-slow system
functions after the handler has finished. » a process can contain multiple calls to these functions
at the same time
» e.g. read(), write(), fork(), many more
! Some UNIXs only call the handler after the
non-slow system function call has finished.

Maria Hybinette, UGA 71 Maria Hybinette, UGA 72


Non-reentrant functions errno problem

! A functions may be non-reentrant (only ! errno is usually represented by a global


one call to it at once) for a number of variable.
reasons:
» it uses a static data structure ! Its value in the program can be changed
» it manipulates the heap: malloc(), free(), etc. suddenly by a signal handler which produces
» it uses the standard I/O library a new system function error.
– e,g, scanf(), printf()
– the library uses global data structures in a
non-reentrant way

Maria Hybinette, UGA 73 Maria Hybinette, UGA 74

Limitations of Nonlocal Jumps


! Works within stack discipline
» Can only long jump to environment of function that has been called
but not yet completed

jmp_buf env; P1
P1() env P2
{
P2(); P3(); At setjmp
}
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

Вам также может понравиться