chapter10

advertisement
System Programming
Chapter 10
1
Signals
• Objectives:
– An overview of signals
– Related function libraries and problems, e.g., reliability &
incompatibility.
• What is a signal?
– Software interrupts
• A way of handling asynchronous events
– Asynchronous means random time occurrences
• e.g., SIGABRT, SIGALRM.
– 15 signals for Version 7, 31 signals for SVR4 & 4.3+BSD –
<signal.h> (# > 0)
2
What conditions generate signals to a
process?
• Terminal-generated signals
– DELETE key, ^c  SIGINT
• Hardware exceptions
– SIGFPE ~ divided-by-0, SIGSEGV ~ illegal memory access, etc.
• Function kill: one process sends a signal to another process
– Owner or superuser
• Shell command kill, e.g., kill –9 pid
• Software conditions
– SIGPIPE ~ reader of the pipe terminated
– SIGALRM ~ expiration of an alarm clock
3
The disposition of a signal (the action)
• Tell the kernel what to do when a signal occurs
• Ignore signals
– SIGKILL and SIGSTOP can not be ignored -> process termination
– Some undefined behaviors for ignoring signals, such as SIGFPE.
• Catch signals
– Provide a signal handler
• e.g., calling waitpid() when a process receives SIGCHLD
• Apply the default action – Figure 10.1
4
Signals
• How to read Figure 10.1?
• ISO C / SUS: signals are defined in ISO C POSX.1 / XSI
• Default action: Terminate w/core – not POSIX.1
– Core = memory of image is left in the file named core in the current
working directory of the process -> for debugging
– When core file is not generated?
•
•
•
•
Non-owner setuid process
Non-grp-owner setgid process
No access rights at the working dir
File is too big (RLIMIT_CORE)
• Description: hardware faults
– Implementation-defined faults
5
Signals
• 41 UNIX signals in figure 10.1: let’s go through them!
• SIGABRT (abort?) – terminate w/core
– Call abort()
• SIGALRM (alarm?) – terminate
– Call setitimer()
• SIGBUS – terminate w/core
– Implementation-defined HW fault
• SIGCHLD (child?) – ignore
– It was sent whenever a process terminates or stops
6
More Signals
• SIGCONT – continue/ignore
– Continue a stopped process, e.g., vi – redraw the terminal screen
• SIGEMT – terminate w/core
– Implementation-defined HW fault
• SIGFPE (floating point error?) – terminate w/core
– Divid-by-0, floating point overflow, etc.
• SIGHUP (hung up?) – terminate
– Disconnection is detected by the terminal interface (no daemons) 
controlling process of a terminal
– Triggering of the rereading of the config files (daemons)
7
Signals
• SIGILL (illegal instruction?) – terminate w/core
– Illegal hardware instruction (4.3BSD do it for abort() in the past)
• SIGINFO – ignore (BSD4.3+)
– Status request for fg processes (^T)
• SIGINT (interrupt?) – terminate
– DELETE key or ^C
• SIGIO – terminate/ignore
– Indicate an asynchronous I/O event (SIGIO=SIGPOLL, Terminate on
SVR4)
8
Signals
• SIGIOT – terminate w/core
– Implementation-defined HW fault (System V did it for abort() in the
past)
• SIGKILL – terminate
– Could not be ignored or caught!
• SIGLWP – ignore
– Used internally by Solaris thread library
• SIGPIPE – terminate
– reader of the pipe/socket terminated
• SIGPOLL – terminate (SVR4)
– A specific event happens on a pollable device.
– Use with poll() function
9
Signals
• SIGPROF – terminate
– A profiling timer expires (setitimer)
• SIGPWR (power) – ignore (SVR4)
– System dependent on SVR4
• UPS  init shutdowns the system
• SIGQUIT – terminate w/core
– ^\ triggers the terminal driver to send the signal to all foreground
processes.
• SIGSEGV – terminate w/core
– Invalid memory access
10
Signals
• SIGSTOP – stop process (like SIGTSTP)
– Can not be caught or ignored
• SIGSYS – terminate w/core
– Invalid system call
• SIGTERM – terminate
– Termination signal sent by kill command
•
SIGTRAP – terminate w/core
– Implementation-defined HW fault
• SIGTSTP – stop process
– Terminal stop signal (^Z) to all foreground processes
11
Signals
• SIGTTIN – stop process
– Generated when bg processes try to read from the controlling
terminal
• SIGTTOU – stop process
– Generated when bg processes try to write to the controlling terminal
– Could be generated by terminal operations, e.g., tcflush
• SIGURG – ignore
– Urgent condition (e.g., receiving of out-of-band data on a network
connection)
– out-of-band data = data delivered with high priority (not received in
order)
12
Signals
• SIGUSR1 – terminate
– User-defined
• SIGUSR2 – terminate
– User-defined
• SIGVTALRM – terminate
– A virtual timer expires (setitimer)
• SIGWINCH – ignore
– Changing of a terminal window size
13
Signals
• SIGXCPU – terminate w/core
– Exceed the soft CPU time limit
• SIGXFSZ – terminate w/core
– Exceed the soft file size (max size of a file created)!
14
Catching a signal
• Specify a function (signal handler) to be called when a signal
occurs.
#include <signal.h>
void (*signal(int signo, void (*func)(int)))(int);
– signo – Figure 10.1
– func: SIG_IGN, SIG_DFL, the address of the signal handler/
signal-catching function
• SIGKILL & SIGSTOP
– Returned value: the address of the previous signal handler.
15
static void sig_usr(int); /* one handler for
both signals */
int main(void)
{
if (signal(SIGUSR1, sig_usr) == SIG_ERR)
err_sys("can't catch SIGUSR1");
if (signal(SIGUSR2, sig_usr) == SIG_ERR)
err_sys("can't catch SIGUSR2");
for ( ; ; )
pause();
}
static void
sig_usr(int signo)
/* argument is
signal number */
{
if (signo == SIGUSR1)
printf("received SIGUSR1\n");
else if (signo == SIGUSR2)
printf("received SIGUSR2\n");
else
err_dump("received signal %d\n",
signo);
}
linux1:~/sys_prog_06/test> fig10.2.exe
Suspended
linux1:~/sys_prog_06/test> bg
[1] fig10.2.exe &
linux1:~/sys_prog_06/test> ps
PID TTY
TIME CMD
1735 pts/7 00:00:00 tcsh
1793 pts/7 00:00:00 fig10.2.exe
1798 pts/7 00:00:00 ps
linux1:~/sys_prog_06/test> kill -USR1
1793
received SIGUSR1
linux1:~/sys_prog_06/test> kill 1793
16
signal()
• Remark:
– SVR4: signal function – unreliable signal semantics
• Unreliable = signals can be lost & a process never knows it
– 4.3+BSD: defined in terms of sigaction function – reliable
signal semantics
– typedef void Sigfunc(int)
• Sigfunc *signal(int, sigfunc *);
– Constants:
#define SIG_ERR (void (*)())-1
#define SIG_DFL (void (*)())0
#define SIG_IGN (void (*)())1
17
Program Start-up through exec
• All signals are set to their default actions unless some are
ignored.
– The exec functions change the disposition of any signals that are being
caught to their default action.
– Why?
• The address of the signal handler no longer exists in the new program
– How about Fork()?
• Child inherits the parent’s signal dispositions
• The shells automatically set the disposition of the interrupt
and quit signals of background processes to “ignored”.
– Type ^C: does not terminate the background processes?
18
Unreliable Signals: Problem 1
• In earlier version of UNIX:
unreliable Signals
– Signals could get lost & a process
never knows it
• Action of a signal was reset to its
default each time the signal
occurred
• Does this prevent process
termination?
– What if another signal
(SIGINT) occurs before the
call to signal in the signal
handler?
– Easy to debug?
int sig_int(); // my signal handler
routine
…
signal(SIGINT, sig_int); // establish
the handler
…
sig_int() {
signal(SIGINT, sig_int); // reestablish handler for next time
… // process the signal
}
19
Unreliable Signals: Problem 2
• Catch a signal or ignore the
signal
– Cannot turn off when the
process does not want it to
occur
• Code: prevent the following
signals from occurring, but
remember if they do occur
– What happens if signal occurs
before after comparison and
before pause()?
int sig_int_flag;
main() {
int sig_int();
…
signal(SIGINT, sig_int);
…
while (sig_int_flag == 0)
pause();
}
sig_int() {
signal(SIGINT, sig_int);
sig_int_flag = 1; }
• Process sleeps forever
20
Interrupted System Calls
• What if a signal occurs in a system call?
• When to deliver the signal to the process?
– Immediate: Interrupt the system call & deliver the signal
– Wait: till the completion of the system call & then deliver
the signal
• How to choose between them?
– Slow system calls vs. others.
21
Slow system calls & Slow devices
• “Slow” System Calls
– Reads from or writes to files that can block the caller forever (e.g.,
pipes, terminal devices, and network devices)
– Opens of files (e.g., terminal device) that block until some conditions
occurs.
– pause, wait, certain ioctl operations
– Some IPC functions
• Disk IO system calls are not slow (blocked only temporarily)
• Traditional Approach
– “Slow” system calls could be interrupted  errno = EINTR
• Interactive devices = slow devices
22
Dealing with Interrupted System Calls
• A typical code sequence
again:
if ((n = read(fd, buff, BUFFSIZE)) < 0) {
if (errno == EINTR)
goto again;
}
• Restarting of interrupted system calls – since 4.2BSD
– ioctl, read, readv, write, writev, wait, and waitpid.
• What if restarting is not wanted?
– 4.3BSD allows a process to disable the restarting on a persignal basis.
23
Different signal implementations
• Scan Figure 10.3 on page 305
24
Reentrant Functions
• When interrupts occur, a function could be called
twice at the same time and causes problems.
– Example: calling a function from a signal handler (figure
10.5)
• Potential Problem:
– In the signal handler, we can’t tell where the process was
executing when the signal was caught!
• Example: may be calling malloc() and in the middle of changing a
linked list
25
Calling a function (getpwnam) twice
static void
my_alarm(int signo)
{
struct passwd *rootptr;
int
main(void)
{
struct passwd *ptr;
printf("in signal handler\n");
if ((rootptr = getpwnam("root")) ==
NULL)
err_sys("getpwnam(root)
error");
alarm(1);
signal(SIGALRM, my_alarm);
alarm(1);
for ( ; ; ) {
if ((ptr = getpwnam("sar")) ==
NULL)
err_sys("getpwnam error");
if (strcmp(ptr->pw_name, "sar") !=
0)
printf("return value
corrupted!, pw_name = %s\n",
ptr->pw_name);
}
}
}
26
Non-Reentrant Functions
/* non-reentrant function */
char *strtoupper(char *string) {
static char buffer[MAX_STRING_SIZE];
int index;
for (index = 0; string[index]; index++)
buffer[index] = toupper(string[index]);
buffer[index] = 0 ;
return buffer;
}
Where/what is the problem?
overwriting the static buffer in the 2nd call
static variable should not be used
27
Reentrant Functions
/* reentrant function (a poor solution) */
char *strtoupper(char *string) {
char *buffer;
int index;
/* error-checking should be performed! */
buffer = malloc(MAX_STRING_SIZE);
for (index = 0; string[index]; index++)
buffer[index] = toupper(string[index]);
buffer[index] = 0 ;
return buffer;
}
Where/what is the problem?
malloc() is not reentrant safe.
28
Better Solution
/* reentrant function (a better solution) */
char *strtoupper_r(char *in_str, char *out_str) {
int index;
for (index = 0; in_str[index]; index++)
out_str[index] = toupper(in_str[index]);
out_str[index] = 0 ;
return out_str;
}
From “General Programming Concepts: Writing and Debugging Programs”
29
Some Non-reentrant Functions
• The POSIX.1 process environment functions
getlogin(), ttyname() (see ISO/IEC 9945:1-1996, §4.2.4
and 4.7.2)
– getlogin() returns a pointer to static data whose content is
overwritten by each call.
• The C-language functions asctime(), ctime(), gmtime()
and localtime() (see ISO/IEC 9945:1-1996, §8.3.4-8.3.7)
• The POSIX.1 system database functions getgrgid(),
getgrnam(), getpwuid() and getpwnam() (see ISO/IEC
9945:1-1996, §9.2.1 a
31
Reentrant Functions
• Figure 10.4 – reentrant functions
– *-marked functions – not in POSIX.1, but in SVR4
• Non-Reentrant Functions
– Those which use static data structures
– Those which call malloc or free
– Those which are part of the standard I/O library – usage
of global data structures
32
Reentrant Functions
• One Errno variable per thread
– Cause problems for reentrance functions?
– Example: main calling read() & setting errno; signal occurs,
signal handling calling read() overwriting errno.
• Solution: save & restore errno inside a handler
• longjmp() is not reentrance because of updating a
global data structure
– How to make it reentrance safe?
33
SIGCLD vs SIGCHLD
• Recall zombie process
– A process has terminated, but its parent has not waited for
it.
• SIGCLD (SV) vs SIGCHLD (BSD, POSIX)
– SIGCHLD
• Handling is similar to those of other signals.
– SIGCLD: Use signal() or sigset() to set its disposition 
• The children of the calling process which sets its disposition to
SIG_IGN will not generate zombie processes (not for BSD).
– wait() returns –1 with errno = ECHILD until all children terminate.
34
SIGCLD Semantics
• When SIGCLD is set to be caught, the kernel checks
if there is any child ready to be waited.
– If yes, call SIGCLD handler immediately!
• Problem in Program 10.3 – Page 309
35
static void
sig_cld(int);
int main()
{
pid_t pid;
static void
sig_cld(int signo)
/* interrupts pause() */
{
pid_t pid;
int
status;
if (signal(SIGCLD, sig_cld) == SIG_ERR)
perror("signal error");
if ((pid = fork()) < 0) {
perror("fork error");
} else if (pid == 0) {
/* child */
sleep(2);
_exit(0);
}
pause();
/* parent */
exit(0);
}
printf("SIGCLD received\n");
if (signal(SIGCLD, sig_cld) == SIG_ERR)
/* reestablish handler */
perror("signal error");
if ((pid = wait(&status)) < 0)
/*
fetch child status */
perror("wait error");
printf("pid = %d\n", pid);
}
continual string of SIGCHL
How to fix this?
move signal() after wait()
36
Reliable Signals
Signal generated
Pending
Signal delivered
set flag
sig_int()
Time
Divided by 0
• A signal is generated when
– the event that causes the signal occurs!
– A flag is set in the process table.
• A signal is delivered when
– the action for the signal is taken.
• A signal is pending during
– the time between its delivery and generation.
37
Reliable Signals
• A signal is blocked until
– the process unblock the signal, or
– The corresponding action become “ignore”.
– (if the action is either default or a handler)
• A process can set which signals are blocked and
pending!
– sigpending()
– Set signal mask for each process – sigprocmask()
• 1 bit per signal
• More details later
38
More than one signal is ready for
delivery?
• Signals are queued when
– a blocked signal is generated more than once.
– POSIX.1 (but not over many Unix) allows the
system to deliver the signal either once or more
than once.
• Delivery order of signals
– No order under POSIX.1, but its Rationale states
that signals related to the current process state,
e.g., SIGSEGV, should be delivered first.
39
Send a signal to a process or a process
group
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int signo);
int raise(int signo); // itself
– raise(signo) = kill(getpid(), signo)
– Return 0 if okay, -1 on error
– pid > 0  to the process
– pid == 0  to “all” processes with the same gid of the
sender (excluding proc 0, 1, 2)
– pid < 0  to “all” processes with gid == |pid|
– pid == -1  broadcast signals under SVR4 and 4.3+BSD
40
kill and raise
• Right permissions must be applied!
– Superuser is mighty!
– Real or effective uid of the sender == that of the receiver
• _POSIX_SAVED_IDS  receiver’s saved set-uid is checked up,
instead of effective uid
• SIGCONT  member of the session
• signo == 0 ~ a null signal
– Normal error checking is performed by kill() to see if
a specific process exists.
• kill() returns –1, and errno == ESRCH
41
Setting a timer (SIGALRM)
#include <unistd.h>
unsigned int alarm(unsigned int secs);
– Set a timer that will expire at a specified time in the future
– A previously registered alarm is replaced by the new
value – the left seconds is returned!
– There could be a delay in calling handler because of
processor scheduling delays.
– alarm(0) resets (cancels) the alarm.
– Default: termination
42
Suspend a process till a signal is caught
#include <unistd.h>
int pause(void);
– Return if a signal handler is executed.
• Returns –1 with errno = EINTR
43
Implement sleep1() with alarm() &
pause(): 3 potential problems
static void
sig_alrm(int signo)
{
/* nothing to do, just return to wake up the pause */
}
unsigned int
sleep1(unsigned int nsecs)
{
if (signal(SIGALRM, sig_alrm) == SIG_ERR)
return(nsecs);
alarm(nsecs);
/* start the timer */
pause();
/* next caught signal wakes us up */
return(alarm(0));
/* turn off timer, return unslept time */
}
44
Three potential problems
• Any previous alarm?
– What should be done if old alarm < nsecs?
– What should be done if old alarm > nsecs?
• The lost of the previous SIGALRM handler
• A race condition
– signal between alarm() & pause() -> sleep in
pause() forever
45
Any previous alarm - solution
if ( (oldalarm = alarm(nsecs)) > 0)
// get the old alarm value
if (oldalarm < nsecs) {
alarm(0);
alarm( oldalarm );
pause();
return(alarm(0));}
else {
pause();
return( alarm( oldalarm – nsecs) );
}
46
Previous SIGALRM handler - solution
if ( (oldhandler = signal(SIGALRM, sig_alrm))
== SIG_ERR)
return (nsecs);
// lines 15-16 here.
signal(SIGALRM, oldhandler);
return( alarm(0) );
47
race condition - solution
• What is problem?
– pause() may not be executed after signal()
• How to fix it?
– The trick is in the signal handler: jump past
pause().
48
race condition – SV2 solution
static jmp_buf env_alrm;
static void sig_alrm(int signo)
{
longjmp(env_alrm, 1); }
unsigned int sleep2(unsigned int nsecs)
{
if (signal(SIGALRM, sig_alrm) == SIG_ERR)
return(nsecs);
if (setjmp(env_alrm) == 0) {
alarm(nsecs);
/* start the timer */
pause();
/* next caught signal wakes us up */
}
return(alarm(0));
/* turn off timer, return unslept time */
}
49
problem in SV2 solution
• What if SIGALRM interrupts another signal
handler?
– longjmp() will abort that signal handler.
50
unsigned int
static void
sleep2(unsigned int);
sig_int(int);
int main(void)
{
unsigned int
static jmp_buf env_alrm;
static void sig_alrm(int signo)
{
longjmp(env_alrm, 1); }
unslept;
if (signal(SIGINT, sig_int) == SIG_ERR)
err_sys("signal(SIGINT) error");
unslept = sleep2(5);
printf("sleep2 returned: %u\n", unslept);
exit(0);
}
static void sig_int(int signo)
{
int
i, j;
volatile int k;
unsigned int sleep2(unsigned int nsecs)
{
if (signal(SIGALRM, sig_alrm) == SIG_ERR)
return(nsecs);
if (setjmp(env_alrm) == 0) {
alarm(nsecs);
/* start the timer */
pause();
/* next caught
signal wakes us up */
}
return(alarm(0));
/* turn off timer,
return unslept time */
}
/*
* Tune these loops to run for more than 5 seconds
* on whatever system this test program is run.
*/
printf("\nsig_int starting\n");
for (i = 0; i < 300000; i++)
for (j = 0; j < 4000; j++)
k += i * j;
printf("sig_int finished\n"); // does not finish
}
51
Lessons from sleep1() & sleep2()
• Easy to use signals incorrectly
• One more example:
– time out read on a slow device after some amount
of time
52
Program with 2 problems (race
condition & automatic restarting)
int main(void)
{
int
n;
char line[MAXLINE];
if (signal(SIGALRM, sig_alrm) == SIG_ERR)
err_sys("signal(SIGALRM) error");
race condition: process blocks more than 10 seconds
alarm(10);
if ((n = read(STDIN_FILENO, line, MAXLINE)) < 0)
err_sys("read error");
in case of automatically restarting, the restarted
alarm(0);
read call is not interrupted by alarm(10)
write(STDOUT_FILENO, line, n);
exit(0);
in case of
}
static void sig_alrm(int signo)
{
/* nothing to do, just return to interrupt the read */
}
53
static void
sig_alrm(int);
static jmp_buf env_alrm;
int
main(void)
{
int
n;
char line[MAXLINE];
static void
sig_alrm(int signo)
{
longjmp(env_alrm, 1);
}
if (signal(SIGALRM, sig_alrm) == SIG_ERR)
err_sys("signal(SIGALRM) error");
if (setjmp(env_alrm) != 0)
err_quit("read timeout");
alarm(10);
if ((n = read(STDIN_FILENO, line, MAXLINE))
< 0)
err_sys("read error");
alarm(0);
write(STDOUT_FILENO, line, n);
exit(0);
}
54
Announcements
• Final exam: 6/21 class time
– Coverage: chapters 7, 8, 10.1~10.19, 15.1~15.5
• MP4 is due after final exam
– TA will talk about it
55
Signal Sets: representing multiple
signals
• Why new type sigset_t?
– The number of different signals could exceed the number of bits in an
integer!
#include <signal.h>
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set, int sig_no);
int sigdelset(sigset_t *set, int sig_no);
int sigismember(const sigset_t *set, int
sig_no);
56
Changing signal set (signal mask)
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
• initialize the signal set pointed to by set so that all signals are
excluded/included
int sigaddset(sigset_t *set, int sig_no);
int sigdelset(sigset_t *set, int sig_no);
• add sign_no to set
int sigismember(const sigset_t *set, int
sig_no);
• check if sign_no is in set
57
Signal Sets
• A macro Implementation if # of signals <= bits
in an integer:
#define sigemptyset(ptr) ( *(ptr) = 0)
#define sigfillset(ptr) ( *(ptr) =
~(sigset_t)0, 0)
– Program 10.9
• Not one-line macros because of the checking
requirements for validity and the setting of errno under
POSIX.1.
58
sigprocmask()
examine/change signal mask
#include <signal.h>
int sigprocmask(int how, const sigset_t *set,
sigset_t *oset);
– If oset is non-null, current signal mask is returned
through oset
– If set is non-null, the how argument indicates how the
current signal mask is modified.
• SIG_BLOCK: add set to blocked signals (union of current signal
mask & set)
• SIG_UNBLOCK: add set to unblocked signals (intersection of
current signal mask & complement of set)
• SIGMASK: replace current signal mask by set
– At least one of the pending or one of unblocking signals
will be delivered when the sigprocmsk() returns.
59
sigprocmask example
print names of signals in signal mask
void
pr_mask(const char *str)
{
sigset_t
sigset;
int
errno_save;
errno_save = errno;
/* we can be called by signal handlers */
if (sigprocmask(0, NULL, &sigset) < 0)
err_sys("sigprocmask error");
printf("%s", str);
if (sigismember(&sigset, SIGINT)) printf("SIGINT ");
if (sigismember(&sigset, SIGQUIT)) printf("SIGQUIT ");
if (sigismember(&sigset, SIGUSR1)) printf("SIGUSR1 ");
if (sigismember(&sigset, SIGALRM)) printf("SIGALRM ");
/* remaining signals can go here */
printf("\n");
errno = errno_save;
}
60
sigpending()
access pending & blocked signal
#include <signal.h>
int sigpending(sigset_t *set);
– Returns the set of pending, blocked signals
• Program 10.15 – Page 322
– SIGQUIT is blocked, current signal mask is saved, the
process sleeps for 5 seconds, and SIGQUIT is unblocked.
– SIGQUIT is delivered until the signal is unblocked and
before sigprocmask() returns.
– Can we use “SET_UNBLOCK” instead of SIG_SETMASK?
– No queuing of signals.
61
int main(void) {
sigset_t
newmask, oldmask, pendmask;
if (signal(SIGQUIT, sig_quit) == SIG_ERR)
err_sys("can't catch SIGQUIT");
/*
* Reset signal mask which unblocks SIGQUIT.
(4) */
if (sigprocmask(SIG_SETMASK, &oldmask,
NULL) < 0)
err_sys("SIG_SETMASK error");
printf("SIGQUIT unblocked\n");
/*
* Block SIGQUIT and save current signal mask.
sleep(5);
/* SIGQUIT here will terminate
with core file */
(1) */
sigemptyset(&newmask);
exit(0);
sigaddset(&newmask, SIGQUIT);
}
if (sigprocmask(SIG_BLOCK, &newmask,
&oldmask) < 0)
static void sig_quit(int signo) {
err_sys("SIG_BLOCK error");
printf("caught SIGQUIT\n");
if (signal(SIGQUIT, SIG_DFL) == SIG_ERR)
sleep(5);
/* SIGQUIT here will remain
err_sys("can't reset SIGQUIT");
pending */
}
(3) if (sigpending(&pendmask) < 0)
err_sys("sigpending error");
if (sigismember(&pendmask, SIGQUIT))
printf("\nSIGQUIT pending\n");
$ ./a.out
(2) ^\ // generate signal once before 5 seconds
(3) SIGQUIT pending
(5) caught SIGQUIT
SIGQUIT unblocked
62
sigaction()
examine/change signal actions
#include <signal.h>
int sigaction(int signo, const struct
sigaction *act, struct sigaction *oact);
– change the action associated with signo in act
– if oact is non-null, return the previous action in oact.
– how does it differ from signal()?
• sa_mask is added to current signal mask before signal-catching
function is called, then removed after signal-catching function is
done.
– Why?
• block signals whenever a
signal hanlder is invoked
struct sigaction {
void (*sa_handler)(int);
sigset_t sa_mask;
int sa_flags;
void (*sa_sigaction) (int
siginfo_t *, void *)
};
63
sigaction()
examine/change signal actions
#include <signal.h>
int sigaction(int signo, const struct
sigaction *act, struct sigaction *oact);
– what is sa_flags?
• options for handling signal
– SA_INTERRUPT: system calls interrupted are not auto-restarted
– SA_INFO: additional info to a signal handler (sa_sigaction)
• see figure 10.16
– what is sa_sigaction?
• an alternative handler
struct sigaction {
void (*sa_handler)(int);
sigset_t sa_mask;
int sa_flags;
void (*sa_sigaction) (int
siginfo_t *, void *)
};
64
sa_sigaction()
• Used with sa_flags has SA_SIGINFO
void handler(int signo, siginfo_t * info, void *context)
• info: additional information about why this signal is
generated
• context: process context at the time of signal delivery
struct siginfo {
int si_signo;
int si_errno;
int si_code;
pid_t
si_pid;
uid_t
si_uid;
void *si_addr;
int si_status;
long si_band;
}
/* signal number */
/* if nonzero, errno value */
/* additional info – see figure 10.17*/
/* sending process id */
/* sending process real user id */
/* address that caused the fault */
/* exit value or signal number */
/* band number for SIGPOLL */
65
Implement signal() with sigaction()
/* Reliable version of signal(), using POSIX sigaction(). */
Sigfunc * signal(int signo, Sigfunc *func)
{
struct sigaction
act, oact;
act.sa_handler = func;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
if (signo == SIGALRM) {
#ifdef SA_INTERRUPT
act.sa_flags |= SA_INTERRUPT;
#endif
} else {
#ifdef SA_RESTART
act.sa_flags |= SA_RESTART; // any system calls interrupted by other signals are restarted
automatically, except for SIGALARM
#endif
}
if (sigaction(signo, &act, &oact) < 0)
return(SIG_ERR);
return(oact.sa_handler);
66
}
Implement signal_intr()
prevent interrupted system calls from being restarted
Sigfunc *
signal_intr(int signo, Sigfunc *func)
{
struct sigaction
act, oact;
#ifdef
act.sa_handler = func;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
SA_INTERRUPT
act.sa_flags |= SA_INTERRUPT;
#endif
if (sigaction(signo, &act, &oact) < 0)
return(SIG_ERR);
return(oact.sa_handler);
}
67
recall: setjmp and longjmp
Consider this as a non-local goto
#include <setjmp.h>
int setjmp(jmp_buf env);
– Return 0 if called directly; otherwise, it could return a
value val from longjmp().
– env tends to be a global variable.
int longjmp(jmp_buf env, int val);
– longjmp() unwinds the stack and affect some variables.
• Program 7.11
– setjmp and longjmp
68
main(void)
{
char line[MAXLINE];
if (setjmp(jmpbuffer) != 0)
printf(“error”);
while (fgets(line, MAXLINE, stdin) !=
NULL)
do_line(line);
exit(0);
}
}
}
void cmd_add(void)
{
int
token;
token = get_token();
/* rest of processing for this command */
/* nonfatal error -> invalid number -> go
back to reading the next line */
if (token < 0) {
longjmp(jumpbuffer, 1);
}
}
char *tok_ptr;
get_token() */
/* global pointer for
void do_line(char *ptr)
of input */
{
int
cmd;
/* process one line
tok_ptr = ptr;
while ((cmd = get_token()) > 0) {
switch (cmd) { /* one case for
each command */
…..
}
int get_token(void)
{
/* fetch next token from line pointed to
by tok_ptr */
}
69
sigsetjmp & siglongjmp
nonlocal branch in signal handlers
• Why are they needed?
– When a signal is caught, the signal-catching function is
entered with the current signal automatically added to the
signal mask.
• Why? prevent subsequent occurrences of that signal from
interrupting the same signal handler.
– What are values in the signal mask when we longjmp out
of the signal handler?
• Some (e.g., BSD) saves signal mask (at the time when sigsetjmp is
called) & restores it (at the time when siglongjmp is called)
• Some (e.g., Linux) does not.
70
sigsetjmp & siglongjmp
nonlocal branch in signal handlers
#include <setjmp.h>
int sigsetjmp(sigjmp_buf env, int savemask);
– Return 0 if called directly, nonzero if returning from a call
to siglongjmp
void siglongjmp(sigjmp_buf env, int val);
– What is the difference with setjmp & longjmp?
• sigsetjmp() saves the current signal mask of the process in
env if savemask !=0.
• When the siglongjmp() is called, signal mask is restored.
71
example using sigsetjmp & siglongjmp
what is it going to print?
static void
sig_usr1(int), sig_alrm(int);
static sigjmp_buf
jmpbuf;
static volatile sig_atomic_t canjump;
static void sig_usr1(int signo) {
time_t starttime;
if (canjump == 0)
return;
/* unexpected signal, ignore */
int main(void) {
if (signal(SIGUSR1, sig_usr1) == SIG_ERR)
err_sys("signal(SIGUSR1) error");
if (signal(SIGALRM, sig_alrm) == SIG_ERR)
err_sys("signal(SIGALRM) error");
pr_mask("starting main: "); /* {Prog prmask} */
if (sigsetjmp(jmpbuf, 1)) {
pr_mask("ending main: ");
(4) none
exit(0);
}
canjump = 1; /* now sigsetjmp() is OK */
for ( ; ; )
pause();
}
pr_mask("starting sig_usr1: "); (1) SIGUSER1
alarm(3);
/* SIGALRM in 3 seconds */
starttime = time(NULL);
for ( ; ; )
/* busy wait for 5 seconds */
if (time(NULL) > starttime + 5)
break;
pr_mask("finishing sig_usr1: ");
(3) SIGUSER1
canjump = 0;
siglongjmp(jmpbuf, 1); /* jump back to main, don't
return */
}
static void sig_alrm(int signo) {
pr_mask("in sig_alrm: ");
}
(2) SIGUSER1 SIGALARM72
sigsuspend
#include <signal.h>
if (sigprocmask(…) < 0)
err_sys(…);
int sigsuspend(const
sigset_t *sigmask);
pause();
CPU Scheduling
could occur!
– Atomic operation: (1) set
the signal mask to sigmsk
and (2) suspend until a
signal is caught or until a
signal occurs that
terminates the process.
– Return –1. errno = EINTR
73
why needs sigsuspend()?
• Say if you want to protect a region of code from a specific
signal.
• So implement something like:
sigset_t newmask, oldmask
sigemptyset(&newmask);
sigaddset(&newmask, SIGINT)
/* block SIGINT and save current signal mask */
if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
err_sys(“SIG_BLOCK error”);
/* critical region of code */
if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
err_sys(“SIG_BLOCK error”);
block indefinitely
pause(); /* wait for signal to occur */
74
a correct implementation using
sigsuspend()
sigset_t newmask, oldmask
sigemptyset(&newmask);
sigaddset(&newmask, SIGINT)
if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
err_sys(“SIG_BLOCK error”);
/* critical region of code */
/* pause & reset the signal maks which unblocks SIGINT*/
if (sigsuspend(&oldmask) < 0)
err_sys(“SIG_BLOCK error”);
75
#include "apue.h"
volatile sig_atomic_t
quitflag;
/* set nonzero by signal handler */
static void
sig_int(int signo)
/* one signal
handler for SIGINT and SIGQUIT */
{
if (signo == SIGINT)
printf("\ninterrupt\n");
else if (signo == SIGQUIT)
quitflag = 1;
/* set
flag for main loop */
}
waiting for a signal
handler to set a
global variable
(quitflag)
int main(void) {
sigset_t
newmask, oldmask, zeromask;
if (signal(SIGINT, sig_int) == SIG_ERR)
err_sys("signal(SIGINT) error");
if (signal(SIGQUIT, sig_int) == SIG_ERR)
err_sys("signal(SIGQUIT) error");
sigemptyset(&zeromask);
sigemptyset(&newmask);
sigaddset(&newmask, SIGQUIT);
/*
* Block SIGQUIT and save current signal mask.
*/
if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
err_sys("SIG_BLOCK error");
while (quitflag == 0)
sigsuspend(&zeromask);
/*
* SIGQUIT has been caught and is now blocked; do whatever.
*/
quitflag = 0;
/*
* Reset signal mask which unblocks SIGQUIT.
*/
if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
76
err_sys("SIG_SETMASK error");
abort
#include <stdlib.h>
void abort(void);
– Sends SIGABRT to the process!
– Can catch it in signal handler to do cleanup work, but abort() won’t
return to the caller (ANSI C)
• The SIGABRT won’t return if its handler calls exit, _exit, longjmp,
siglongjmp.
– Program 10.18 – Page 341
• POSIX.1 implementation of abort()
78
abort
– ANSI C
• The Implementation determines whether output
streams are flushed and whether temporary files
are deleted.
– POSIX.1
• POSIX.1 specifies that abort overrides the blocking
or ignoring of the signal by the process.
• If abort() terminates a process, all open standard
I/O streams are closed (by fclose()); otherwise,
nothing happens.
79
signal issues in system()
implementation
• A correct implementation of system() system call should
block SIGINT & SIGCHLD of the calling process.
• Why blocking these signals?
– system() fork/exec a child shell process (/bin/sh), which then fork/exec
the shell command string.
• What happened when interrupt signal occurs?
– SIGINT (default) sends to all parent processes.
• What happened when the command string process
terminates?
– SIGCHLD sends to calling process and its signal handler
– (but should be dealt with by the system())
80
system()
• Program 10.20 – Page 345
– The implementation of system()
– Ignoring of SIGINT and SIGCHLD & blocking of SIGCHLD
(POSIX.2)
– Setting of a proper signal mask before fork()
– Termination status of the shell
81
sleep
#include <unistd.h>
unsigned int sleep(unsigned int secs);
– Suspend until (1) the specified time elapsed, or
(2) a signal is caught and the handler returns returns the unslept seconds.
• Problems:
– alarm(10), 3 secs, sleep(5)?
• Another SIGALRM in 2 seconds? Both SVR4 &
4.3BSD – not POSIX.1 requirements
– Alarm() & sleep() both use SIGALRM.
82
sleep
• Under SVR4, alarm(6), 3 secs, sleep(5) 
sleep() returns in 3 secs!
• Program 10.21 – Page 318
– Implementation of sleep()
– Program 10.4 – Page 286
• Unreliable signals!
– No handling of previously set alarms.
83
main()
signal()
signal()
pr_mask()
sigsetjmp()
pause()
SIGUSR1 delivered
SIGUSR1
sig_usr1
pr_mask()
alarm()
time()
time()
time()
SIGUSR1
SIGARM
sig_alrm
SIGALARM delivered
pr_mask()
return()
Return from signal hander
pr_mask()
siglongjmp()
sigsetjmp()
pr_mask()
exit()
SIGUSR1
84
85
Download