Signals - Piazza

advertisement
Signals and Signal Handling
Signals
 A predefined message sent between two processes or from the kernel
to a process, or by a user to a process

A software version of a hardware interrupt

It is a primitive form of interprocess communication
 Signals are asynchronous

A process may receive a signal at any time during its execution

It must be prepared to respond to the signal at that time
 Most are assigned an integer value (starting with 1) and a symbolic
name

SIGKILL

You can see all the defined signals by executing the command
man signal.h
9
/* Kill, unblockable (POSIX). */
Signals
 Users send signals



with the keyboard

^c SIGINT
2
/* Interrupt */

To see the mappings of your keystrokes, enter the command
stty -a
The kill command utility

kill [-signal] pid

The default signal sent is SIGTERM (15)

You may use either the integer value or the symbolic name of
the signal

If after using kill pid, the process doesn't terminate, then use
kill -9 pid
The kill( ) system call
Signals
 Each signal type has associated with it an action which the kernel will
perform on behalf of a process when the process is sent that signal
 Process can

Perform the default action for that signal, such as
 Terminate the process
 Suspend the process but don't terminate it

Ignore the signal
 The process will be unaware that the signal has occured
 Two signals cannot be ignored
SIGKILL - Terminates a process
– SIGSTOP - suspends a process
 Catch and handle the signal
 Execute a pre-arranged signal handler
–
A process can register a special signal-handling function
 It will be called when the signal is received by the process

kill() system call
 kill() system call: send any signal to a process group or process

int kill(pid_t pid, int sig);
 sig is the signal to send (either integer value or symbolic name)
 pid specifies where to send the signal
 pid > 0 :The signal is sent to the process whose PID is pid
 pid = 0 :The signal is sent to all processes whose GID is the same as
the GID of the sender
 pid = -1 :The signal is sent to all processes that the current process may
send signals to
 If superuser: The signal is sent to all processes except init (PID 1)
 sig = 0
 No signal is actually sent, but all error checking is performed
 This can be used to see if process pid is still alive
 RETURN VALUE
 On success (at least one signal was sent), zero is returned
 On error, -1 is returned, and errno is set appropriately
Example


See kill_ex.c on webpage under Code
Output
> ./kill_ex
child 1 is alive
child 2 is alive
child 1 is alive
child 2 is alive
child 1 is alive
child 2 is alive
Parent is about to suspend child 1
child 2 is alive
child 2 is alive
child 2 is alive
Parent is about to resume child 1
child 1 is alive
child 2 is alive
child 1 is alive
child 2 is alive
child 1 is alive
child 2 is alive
Parent is about to terminate both children
alarm()
 alarm( ) system call

Each process has an alarm clock timer associated with it which it can use to
send itself SIGALRM signals

unsigned int alarm(unsigned int seconds);
 E.g. alarm(3) means send the calling process a SIGALRM signal 3
seconds later

If seconds is 0, any currently scheduled alarm is switched off

When alarm( ) is called, any previously scheduled alarms are cancelled
 alarm( ) returns
 The number of seconds remaining until any previously scheduled alarm
was due to be delivered
Or zero if there was no previously scheduled alarm
 Include <unistd.h>

pause()
 pause( ) library function

Suspends the calling process until it receives a signal

The signal must be one that is not currently set to be ignored by the
calling process

If the signal is caught and doesn't cause termination


-1 is returned

errno is set to EINTR
If the signal causes termination of the calling process

pause( ) does not return
Example
alarm(3);
while(1) {
pause();
printf(“Signal Received”); // will be printed when SIGALRM is received.
}
signal()

To ignore or catch a signal, we need to associate a signal-catching routine with that
signal

signal( ) system call
sighandler_t signal(int signum, sighandler_t handler);

Include <signal.h>

signum is the value of the signal to be caught



You may also use the symbolic name for the signal

It cannot be SIGKILL or SIGSTOP
handler is a pointer to a function

This is the function (handler) that is executed if signal signum is received

It may be a user-defined function

Or SIG_DFL - the default action for the signal

Or SIG_IGN - ignore this signal
The return value is the signal's previous handler
Example: signal_ex.c

/* This program demonstrates ignoring the SIGINT.
Follow the directions in the printf statements
to play with the program
*/
#include <stdio.h>
#include <signal.h>
main()
{
void (*oldHandler)(int); /* To hold old handler value */
printf("I can be Controled-C'ed\n");
sleep(3);
oldHandler = signal(SIGINT, SIG_IGN); /* Ignore Control-C */
printf("I'm protected from Control-C now\n");
sleep(3);
signal(SIGINT, oldHandler); /* Restore old handler */
printf("I can be Control-C'ed again\n");
sleep(3);
printf("Bye!\n");
}
signal_ex.c
The line void (*oldHandler)(int); declares oldHandler to be a function that
takes one int as an argument and returns a void pointer
Output:
> ./a.out
I can be Controled-C'ed
^C
> ./a.out
I can be Controled-C'ed
I'm protected from Control-C now
^CI can be Control-C'ed again
^C
>
User-defined signal handlers
 Use the handler to execute any code that you want when the signal is
received
 Usually the first line in a hander is another signal( ) statement

This reestablishes the connection between the handler and the signal

This is necessary because most signals are reset to take their default
action after being caught and before executing the handler

If you don't immediately execute another signal( ) statement, then you
can't catch another signal of the same type
 Note that there is a small time frame where if a signal is received, the
default action will occur

This is what makes signals an unreliable communication mechanism
Example: signal_ex2.c
 /* This program demonstrates using a signal handler
to catch SIGINT. Notice that the connection
between the signal handler and signal is reestablished
immediately up enter the handler.
To use:
1. As soon as it is running, hit multiple ^c's then <ret>
2. The signal handler counts the number of ^c's hit and
prints out the total
3. The default signal handler is then reestablished
4. Hit another ^c and the program ends
*/
Example: signal_ex2.c

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
int ctrl_c_count = 0;
void (* old_handler)(int);
void ctrl_c(int);
main()
{
int c;
old_handler = signal(SIGINT, ctrl_c);
while ((c = getchar())!='\n');
printf("ctrl-c count = %d\n", ctrl_c_count);
(void) signal(SIGINT, old_handler);
for (;;);
}
void ctrl_c(int signum)
{
(void) signal(SIGINT, ctrl_c);
++ctrl_c_count;
}
output
> ./a.out
^C^C^C
ctrl-c count = 3
^C
>
sigaction( ) system call
 int sigaction(int signum, const struct sigaction *act, struct sigaction
*oldact);
 used to associate an action with a signal
 signum is the number of the signal to handle

May be a symbolic name

May not be SIGKILL or SIGSTOP
 act is a structure that holds the pointer to the name of the handler to associate
with the signal
 oldact holds the pointer to the routine that was the signal's previous handler
 One important difference between sigaction and signal

The signal handling routine stays installed even after a signal has been
caught

So you don't have to reset the signal handler in the signal handling routine
struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
}

sa_handler - the name of the handler

sa_sigaction - do not use for now

sa_mask - gives a mask of signals which should be blocked during execution of
the signal handler

flags - modify the behavior of the signal handling process

See man pages for details.
–
SA_ONSTACK
–
SA_RESETHAND
–
SA_NODEFER
–
SA_RESTART
–
SA_SIGINFO
–
SA_NOCLDWAIT
–
SA_NOCLDSTOP
sigaction_ex.c

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
int ctrl_c_count = 0;
void ctrl_c(int);
main()
{
int c, i;
struct sigaction new_action;
struct sigaction old_action;
new_action.sa_handler = ctrl_c;
new_action.sa_flags = 0;
sigaction( SIGINT, &new_action, &old_action );
for ( i = 0; i < 3; i++ )
{
while ((c = getchar())!='\n');
printf("ctrl-c count = %d\n", ctrl_c_count);
}
sigaction( SIGINT, &old_action, NULL );
for (;;);
}
void ctrl_c(int signum)
{
++ctrl_c_count;
}
output
> ./a.out
^C^C
ctrl-c count = 2
^C^C^C^C
ctrl-c count = 6
^C^C^C^C
ctrl-c count = 10
^C
>
sigprocmask( ) system call
 Each process has a signal mask which is a set of signals that are currently
blocked from delivery
 If a blocked signal is sent to a process anyway
 It is added to a set of pending signals for the process

It will be delivered when the block is removed
 int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
changes the list of currently blocked signals
 how indicates how the list of signals in set should be treated

SIG_BLOCK
 The set of blocked signals is the union of the current set and set
 SIG_UNBLOCK
The signals in set are removed from the current set of blocked
signals
 SIG_SETMASK
 The set of blocked signals is set to set

 oldset contains the previous value of the signal mask
Example: sigprocmask_ex.c
 In this example, I will use SIGUSR1 and SIGUSR2.
 The definition of these in signal.h is:
Name
SIGUSR1
SIGUSR2
Value Default Event
16
Exit
User Signal 1
17
Exit
User Signal 2
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
sigset_t new_signals;
int main()
{
void signal_catcher( int ); // forward declaration of handler
struct sigaction new_action;
sigemptyset( &new_signals ); // Empties the set of blocked signals
sigaddset( &new_signals, SIGUSR1 ); // Adds SIGUSR1 to the set of
// blocked signals
sigprocmask( SIG_BLOCK, &new_signals, NULL ); // Unions the set of
process
// blocked signals with new_signals
new_action.sa_handler = signal_catcher;
new_action.sa_flags = 0;
/* Associate the handler with SIGUSR2 */
sigaction( SIGUSR2, &new_action, NULL );
printf( "Waiting for signal\n" );
pause( ); // Sleep until a signal is received
printf( "Done\n" );
return 0;
}
void signal_catcher( int n )
{
printf( "Received signal %i will release SIGUSR1\n", n );
sigprocmask( SIG_UNBLOCK, &new_signals, NULL );
printf( "SIGUSR1 released!\n" );
}
Output
> ./a.out &
[1] 21551
> Waiting for signal
kill -USR1 21551
> ps
PID TTY
TIME CMD
21339 pts/17
0:00 tcsh
21558 pts/17
0:00 ps
21329 pts/17
0:00 csh
21551 pts/17
0:00 a.out
> kill -USR2 21551
Received signal 17 will release SIGUSR1
> ps
PID TTY
TIME CMD
21339 pts/17
0:00 tcsh
21329 pts/17
0:00 csh
21559 pts/17
0:00 ps
[1] + User signal 1
./a.out
Pending signal
SIGUSR1 can now
terminate the process
sigsuspend( ) system call
 int sigsuspend(const sigset_t *mask);
 Is used to suspend a process
 It replaces the current signal mask with mask
 The process is suspended until a signal is delivered whose action is to

Execute a signal-catching function

Or terminate the process
 If the action is to terminate the process, sigsuspend() does not return.
 If the action is to execute a signal catching function, sigsuspend() returns
after the signal catching function returns. On return, the signal mask is
restored to the set that existed before the call to sigsuspend().
sigsuspend_ex.c
/* This program demonstrates using sigsuspend
To use:
1. a.out &
2. kill -USR1 %1
3. kill -INT %1
4. Then immediately issue a jobs command
*/
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
int main()
{
void signal_catcher( int ); // forward declaration of handler
struct sigaction new_action;
sigset_t no_sigs, blocked_sigs, all_sigs;
sigfillset( &all_sigs );
// Turn all bits on
sigemptyset( &no_sigs );
// Turn all bits off to mean no signals blocked
sigemptyset( &blocked_sigs ); // Initialize
new_action.sa_handler = signal_catcher;
new_action.sa_mask = all_sigs;
new_action.sa_flags = 0;
/* Associate the handler with SIGUSR1 */
sigaction( SIGUSR1, &new_action, NULL );
sigaddset( &blocked_sigs, SIGUSR1 );
sigprocmask( SIG_SETMASK, &blocked_sigs, NULL );
while( 1 )
{
printf( "Waiting for SIGUSR1 signal\n" );
sigsuspend( &no_sigs );
}
printf( "Done\n" );
return 0;
}
void signal_catcher( int n )
{
printf( "Beginning important stuff...\n" );
sleep( 10 );
printf( "Ending important stuff\n" );
}
Output
> ./a.out &
[1] 21674
> Waiting for SIGUSR1 signal
kill -USR1 21674
Beginning important stuff...
Ending important stuff
Waiting for SIGUSR1 signal
kill -INT 21674
> ps
PID TTY
21339 pts/17
TIME CMD
0:00 tcsh
21683 pts/17
0:00 ps
21329 pts/17
[1] + Interrupt
0:00 csh
./a.out
Example: alarm_ex.c
/* This program demonstrates the alarm( ) system call */
#include <stdio.h>
#include <signal.h>
int alarmFlag = 0; /* alarm flag */
void alarmHandler(); /* Forward declaration of alarm handler */
main()
{
signal(SIGALRM, alarmHandler); /* Install signal handler */
alarm(3); /* Schedule an alarm signal in 3 seconds */
printf("Looping....\n");
while(!alarmFlag) /* Loop until flag set */
{
pause(); /* Wait for signal */
}
printf("Loop ends due to alarm signal\n");
}
void alarmHandler()
{
printf("An alarm clock signal was received\n");
alarmFlag = 1;
}
output
> ./a.out
Looping....
An alarm clock signal was received
Loop ends due to alarm signal
>
Example: sigchld_ex.c
/* This program allows a user to limit the amount of time that a command takes
to execute. The first parameter to sigchld.out is the maximum # of seconds
that is allowed for execution, and the remaining parameters are the command
itself.
STEPS:
1. Parent process installs a SIGCHLD handler
2. Parent forks a child process to execute the command
3. Parent sleeps for specified number of seconds. When it wakes up, it
sends the child process a SIGINT to kill it
4. If child terminates before its parent wakes up, the parent's SIGCHLD
handler is executed, causing the parent to terminate immediately.
To use: a.out <num of secs> <command>
*/
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
int delay;
void childHandler();
main(int argc, char* argv[])
{
int pid;
signal(SIGCHLD, childHandler); /* Install death-of-child handler */
pid = fork(); /* Duplicate */
if (pid == 0) /* Child */
{
execvp(argv[2], &argv[2]); /* execute command */
perror("sigchld"); /* Should never execute */
}
else /* parent */
{
sscanf(argv[1], "%i", &delay); /* Read delay from command line */
printf("delay is: %i\n", delay);
sleep(delay); /* Sleep for the specified number of seconds */
printf("Child %i exceeded limit and is being killed\n", pid);
kill(pid, SIGINT); /* Kill the child */
}
}
void childHandler()
{
int childPid, childStatus;
childPid = wait(&childStatus); /* Accept child's termination code */
printf("Child %i terminated within %i seconds\n", childPid, delay);
exit (0);
}
output
> ./a.out 2 ls
delay is: 2
a.out
sigaction_ex.c
sigsuspend_ex.c
alarm_ex.c
sigchld_ex.c
signal_ex.c
sigpending_ex.c
signal_ex2.c
Child 21804 terminated within 2 seconds
> ./a.out 2 sleep 4
delay is: 2
Child 21806 exceeded limit and is being killed
>
sigprocmask_ex.c
Download