CS代考 Operating Systems – CSCI 402

Operating Systems – CSCI 402
Handling Signals
Two ways to handle signals
handling it asynchronously using signal handlers
handling it synchronously
using sigwait() in a signal-catching thread
1
321 0
Copyright ý . Signals Asynchronously
Signal handler
each signal in a process can have at most one handler to specify a signal handler of a process, use:
sigset/signal()
returns the current handler (which could be the “default
handler”)
sigaction()
more functionality
#include
typedef void (*sighandler_t)(int);
sighandler_t sigset(int signo, sighandler_t handler);
sighandler_t signal(int signo, sighandler_t handler);
sighandler_t OldHandler = sigset(SIGINT, NewHandler);
Operating Systems – CSCI 402
signal handler is part of the context of a process Copyright ý . Cheng
321 0
2

use the default handler
usually terminates the process sigset/signal(SIGINT, SIG_DFL);
SIG_IGN
ignore the signal
sigset/signal(SIGINT, SIG_IGN);
Operating Systems – CSCI 402
SIG_DFL
Special Handlers
3
321 0
Copyright ý . Cheng

sigset(SIGINT, handler);
while(1)
; return 1;
}
void handler(int signo) {
printf(“I received signal %d. Whoopee!!\n”, signo);
}
SIGINT is blocked inside handler()
but how do you kill this program from your console?
can use the “kill” shell command, e.g., “kill -15 ” instead of using sigset(), you can also use sigaction()
321 0
Operating Systems – CSCI 402
#include
int main() {
void handler(int);
Example
4
Copyright ý . Cheng

#include
int main() {
void handler(int);
Example
sigset(SIGINT, handler);
while(1)
; return 1;
}
void handler(int signo) {
printf(“I received signal %d. Whoopee!!\n”, signo);
sigset(SIGINT, handler);
} re-establish the signal handler inside
the signal handler if you want to receive the same signal more than once
321 0
Operating Systems – CSCI 402
in some systems, you may have to
5
Copyright ý . Cheng

sigaction
int sigaction(int sig,
const struct sigaction *new,
struct sigaction *old);
struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
};
sigaction() allows for more complex behavior
e.g., block additional signals (specified by sa_mask) when handler is called
int main() {
struct sigaction act;
void sighandler(int);
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
act.sa_handler = sighandler;
sigaction(SIGINT, &act, NULL);

}
Operating Systems – CSCI 402
6
321 0
Copyright ý . Systems – CSCI 402
Async-Signal Safety
The problem with asynchronous signal is that you have to worry about async-signal safety
if you don¡¯t take care of it just right, bad things can happen
Async-Signal Safety: Make your code safe when working with asynchronous signals
The general rule to provide async-signal safety:
any data structure the signal handler accesses must be async-signal safe
i.e., an async signal must not corrupt data structures An alternative is to make async-signal synchronous
use a signal-catching thread to receive a particular signal
7
321 0
Copyright ý . 1: Waiting for a Signal
sigset(SIGALRM, DoSomethingInteresting);

struct timeval waitperiod = {0, 1000};
/* seconds, microseconds */
struct timeval interval = {0, 0};
struct itimerval timerval;
timerval.it_value = waitperiod;
timerval.it_interval = interval;
setitimer(ITIMER_REAL, &timerval, 0);
/* SIGALRM sent in ~one millisecond */
pause(); /* wait for it */
can SIGALRM occur before pause() is called?
Note: strickly speaking, this is not a deadlock it has a race condition
321 0
8
Operating Systems – CSCI 402
Copyright ý . Cheng

}}
long-running job that can take days to complete
the handler() can be used to print a progress report need to make sure that state is in a consistent state this is a synchronization issue
our handler() is not async-signal safe
Note: this is not a deadlock and really not a race condition this is the case where the code is not async signal safe
321 0
Operating Systems – CSCI 402
Example 2: Status Update
#include
computation_state_t state;
int main() {
void handler(int);
sigset(SIGINT, handler);
long_running_proc();
return 0;
void long_running_proc() {
while (a_long_time) {
update_state(&state);
compute_more();
} }
void handler(int signo) {
display(&state);
9
Copyright ý . 2: Status Update
void long_running_proc() {
while (a_long_time) {
pthread_mutex_lock(&m);
update_state(&state);
pthread_mutex_unlock(&m);
compute_more();
} }
void handler(int signo) {
pthread_mutex_lock(&m);
display(&state);
pthread_mutex_unlock(&m);
}
Operating Systems – CSCI 402
Does this work?
no (this code is not reentrant)
it may get stuck in handler()
signal handler gets executed till completion
yes, you can deadlock with yourself even if you only have one thread
321 0
in general, keep it simple and brief
10
Copyright ý . (Blocking) Signals
Solution: control signal delivery by masking/blocking the signal
don¡¯t mask/block all signals, just the ones you want a set of signals is represented as a set of bits called
sigset_t
TCB
which is just an unsigned int
if a mask bit is 1, the corresponding signal is
blocked; otherwise, the corresponding signal is unblocked
when a child thread is created, it inherits signal mask from the parent thread
Operating Systems – CSCI 402
signal mask
110100110…
11
321 0
Copyright ý . (Blocking) Signals
To examine or change the signal mask of the calling process
#include
int sigprocmask(
int how,
const sigset_t *set,
sigset_t *old);
TCB
how is one of three commands:
SIG_BLOCK: the new signal mask is the union of the current signal mask and (*set)
SIG_UNBLOCK: the new signal mask is the intersection of the current signal mask and the complement of (*set) SIG_SETMASK: the new signal mask is (*set)
Operating Systems – CSCI 402
signal mask
110100110…
12
321 0
Copyright ý . Cheng

sigset_t
There are bunch of functions to manipulate sigset_t be careful, with some APIs, 1 means to allowed/unblock a signal, and with other APIs,
1 means to blocked a signal
To clear a set:
int sigemptyset(sigset_t *set); To add or remove a signal from the set:
int sigaddset(sigset_t *set, int signo);
int sigdelset(sigset_t *set, int signo);
Example: to refer to both SIGHUP and SIGINT:
TCB
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGHUP);
sigaddset(&set, SIGINT);
sigset_t set;
sigfillset(&set);
sigdelset(&set, SIGHUP);
sigdelset(&set, SIGINT);
Copyright ý . Systems – CSCI 402
signal mask
110100110…
13
321 0

sigset_t set, oldset;
sigemptyset(&set);
sigaddset(&set, SIGALRM);
sigprocmask(SIG_BLOCK, &set, &oldset);
/* SIGALRM now masked */
setitimer(ITIMER_REAL, &timerval, 0);
/* SIGALRM sent in ~one millisecond */
sigfillset(&set);
sigdelset(&set, SIGALRM);
sigsuspend(&set); /* wait for it safely */
/* SIGALRM masked again */

sigprocmask(SIG_SETMASK, &oldset, (sigset_t *)0);
/* SIGALRM unmasked */
sigsuspend() replaces the caller¡¯s signal mask with the set of signals pointed to by the argument
in the above, all signals are blocked/masked except for SIGALRM
atomically unblocks the signal and waits for the signal
321 0
14
Operating Systems – CSCI 402
Example 1: Correct Way of Waiting for a Signal
Copyright ý . 1: Correct Way of Waiting for a Signal
sigset_t set, oldset;
sigemptyset(&set);
sigaddset(&set, SIGALRM);
sigprocmask(SIG_BLOCK, &set, &oldset);
/* SIGALRM now masked */
setitimer(ITIMER_REAL, &timerval, 0);
/* SIGALRM sent in ~one millisecond */
sigfillset(&set);
sigdelset(&set, SIGALRM);
sigsuspend(&set); /* wait for it safely */
/* SIGALRM masked again */

sigprocmask(SIG_SETMASK, &oldset, (sigset_t *)0);
/* SIGALRM unmasked */
sigsuspend()
atomically unblocks the signal and waits
OK
SIGALRM delivery
Time wait for SIGALRM
Operating Systems – CSCI 402
for the signal
unblocks SIGALRM
ATOMIC
321 0
15
Copyright ý . is only one correct way to wait for an asynchronous event someone else generated
e.g., wait for a guard to become true in a guarded command
Step 1) block the asynchronous event
Step 2) check if the event has been generated, if not, unblock the
event and wait for the event in one atomic operation
16
Operating Systems – CSCI 402
Async-Signal Safety
There is only one correct way to wait for an asynchronous event you generated
e.g., an alarm that you wind up and wait for is an asynchronous event you generate
Step 1) Step 2)
Step 3)
block the asynchronous event
do something that will cause the asynchronous event to get generated
unblock the event and wait for the event in one atomic operation
321 0
Copyright ý . 2: Correct Way to Handle Status Update
#include
computation_state_t state;
sigset_t set;
int main() {
void handler(int);
sigemptyset(&set);
sigaddset(&set, SIGINT);
sigset(SIGINT, handler);
long_running_proc();
return 0;
void long_running_proc() {
while (a_long_time) {
sigset_t old_set;
sigprocmask(
SIG_BLOCK,
&set,
&old_set);
update_state(&state);
sigprocmask(
SIG_SETMASK,
&old_set,
0);
compute_more();
void handler(int signo) {
display(&state);
}
now SIGINT cannot be delievered in
update_state()
}
} }
Operating Systems – CSCI 402
17
321 0
Copyright ý . about the signal mask (i.e., blocked/enabled signals)?
should one set of sigmask affect all threads in a process? or should each thread gets it own sigmask?
this certainly makes more sense
POSIX rules for a multithreaded process:
the thread that is to receive the signal is chosen randomly from the set of threads that do not have the signal blocked
if all threads have the signal blocked, then the signal remains pending until some thread unblocks it
at which point the signal is delivered to that thread
Operating Systems – CSCI 402
Signals and Threads
In Unix, signals are sent to processes, not threads!
in a single-threaded process, it¡¯s obvious which thread would handle the signal
in a multi-threaded process, it¡¯s not so clear
in POSIX, the signal is delivered to a thread chosen at random
child thread inherits signal mask from parent thread Copyright ý . Cheng
321 0
18

}
} return(0);
SIGINT);
sigprocmask(
SIG_BLOCK,
&set, 0);
// main thread
// blocks SIGINT
pthread_create(
&thread, 0,
monitor, 0);
long_running_proc();
while (1) {
sigwait(&set, &sig);
pthread_mutex_lock(&m);
display(&state);
pthread_mutex_unlock(&m);
}
Operating Systems – CSCI 402
some_state_t state;
sigset_t set;
main() {
pthread_t thread;
sigemptyset(&set);
sigaddset(&set,
void long_running_proc() {
while (a_long_time) {
pthread_mutex_lock(&m);
update_state(&state);
pthread_mutex_unlock(&m);
compute_more();
} }
void *monitor() {
int sig;
Synchronizing Asynchrony
this is PREFERRED, no need for signal handler! Copyright ý . Cheng
321 0
19

sigwait
int sigwait(sigset_t *set, int *sig)
sigwait() blocks until a signal specified in set is received return which signal caused it to return in sig
if you have a signal handler specified for sig, it will not get invoked when the signal is delivered
instead, sigwait() will return
You should make sure that all the threads in your process have these signals blocked!
this way, when sigwait() is called, the calling thread temporarily becomes the only thread in the process who can receive the signal
sigwait(set) atomically unblocks signals specified in set and waits for signal delivery
OK
signal delivery
Time wait for signal
Operating Systems – CSCI 402
Copyright ý . TOMIC
unblocks signals
321 0
20