Signals
An Introduction |
Prof. David Bernstein |
Computer Science Department |
bernstdh@jmu.edu |
void ( *signal(int num, void (*handler)(int)) ) (int);
num
|
The signal number |
handler
|
The signal handler |
Return | The previous disposition on success; SIG_ERR on error |
Understanding the Signature:
handler
is a pointer to a function that returns nothing and is
passed an int
.
signal()
itself returns a pointer to a function that
returns nothing and is passed an int
.
signal()
typedef void (*signalhandler_t)(int);
signal()
:
sighandler_t signal(int num, sighandler_t handler);
SIG_DFL
:
SIG_IGN
:
signal()
:
read()
, wait()
) that are
interrupted by a signal do not resume when the handler
returns (instead they return immediately and
set errno
to EINTR
)
sigaction()
allows the caller to specify
all behaviors in a standard way (but it is complicated
to use)int sigaction(int num, const struct sigaction *act, struct sigaction *oact)
num
|
The signal number |
act
|
The action to be assoicated with the signal (or NULL) |
oact
|
The previous action assoicated with the signal (or NULL) |
Return | 0 on success; -1 on error |
Note: struct sigaction
contains a pointer to the signal handler,
the additional signals to be blocked, flags, etc...
kill -9 22807
sends signal 9 (i.e.,
SIGKILL
) to process 22807kill -19 -11975
sends signal 19 (i.e.,
SIGSTOP
) to process group 11975SIGINT
to every process in the foreground process group
(which, unless the default action has been modified, terminates
the process)SIGTSTP
to every process in the foreground process group
(which, unless the default action has been modified, suspends
the process)int kill(pid_t pid, int num)
pid
|
The destination of the signal, specifically the process ID (if > 0), all processes in the group of the sender (if 0), all processes (if -1), all processes in the absolute value of the given group ID (if < -1) |
num
|
Is the signal number |
Return | 0 on success; -1 on error |
Note: A signal will only be sent to processes for which the calling process has the appropriate permission
#include <stdio.h> #include <stdlib.h> #include <sys/wait.h> #include <sys/types.h> #include <unistd.h> #include "tax_lib.h" int main(void) { float rate, sales, tax; int should, status; pid_t pid; should = 0; rate = 0.05; pid = fork(); switch(pid) { case -1: exit(1); case 0: should = should_tax(); break; default: sales = total_sales(); wait(&status); // Note: There is only one child if (should) tax = sales * rate; else tax = 0.0; printf("Tax: %5.2f\n", tax); break; } }
should
have in the two processes?
Why won't giving should
file scope fix it?
#define _POSIX_SOURCE #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <sys/wait.h> #include <sys/types.h> #include <unistd.h> #include "tax_lib.h" volatile int should = 0; static void sighandler(int sig) { should = 1; } int main(void) { float rate, sales, tax; int status; pid_t pid; rate = 0.05; pid = fork(); signal(SIGUSR1, sighandler); switch(pid) { case -1: exit(1); case 0: if (should_tax()) kill(getppid(), SIGUSR1); break; default: sales = total_sales(); wait(&status); // Note: There is only one child if (should) tax = sales * rate; else tax = 0.0; printf("Tax: %5.2f\n", tax); break; } }
wait()
for the child to terminatewait()
The Goal: Sequence Task 1 Before Task 2
#define _POSIX_SOURCE #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <unistd.h> static void handle_continue(int sig) { // Nothing to do but interrupt the call to pause() } /* * The Goal: Sequence Task 1 before Task 2 * The Race Condition: kill() in child may be called before pause() in parent */ int main(void) { // Setup the signal handler signal(SIGCONT, handle_continue); pid_t pid = fork(); switch(pid) { case -1: exit(1); case 0: // Child printf("Task 1\n"); kill(getppid(), SIGCONT); exit(0); default: // Parent pause(); printf("Task 2\n"); exit(0); } }
The Race Condition: The child process might call kill()
before the parent process calls pause()
.
#define _POSIX_SOURCE #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <unistd.h> #include "tax_lib.h" // An example with a fairly obvious race condition volatile int should = 0; static void sighandler(int sig) { should = 1; } int main(void) { float rate, sales, tax; pid_t pid; rate = 0.05; pid = fork(); signal(SIGUSR1, sighandler); switch(pid) { case -1: exit(1); case 0: if (should_tax()) kill(getppid(), SIGUSR1); record_status(); // This takes a long time break; default: sales = total_sales(); pause(); if (should) tax = sales * rate; else tax = 0.0; printf("Tax: %5.2f\n", tax); break; } }
pause()
and pause()
will never return.
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset)
how
|
The change to perform |
set
|
The set of signals |
oldset
|
Will hold the old set of signals (if non-NULL) |
Return | 0 on success; -1 on error |
how
can be SIG_BLOCK
to make
blocked = blocked | set
, SIG_UNBLOCK
to make
blocked = blocked & ~set
, or SIG_SETMASK
to
make blocked = set
.
#define _POSIX_SOURCE #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <unistd.h> #include "tax_lib.h" // An example with a more subtle race condition volatile int should = 0; static void sighandler(int sig) { should = 1; } int main(void) { float rate, sales, tax; pid_t pid; sigset_t set, oldset; rate = 0.05; pid = fork(); signal(SIGUSR1, sighandler); sigemptyset(&set); sigaddset(&set, SIGUSR1); sigprocmask(SIG_BLOCK, &set, &oldset); switch(pid) { case -1: exit(1); case 0: if (should_tax()) kill(getppid(), SIGUSR1); record_status(); // This takes a long time break; default: sales = total_sales(); sigprocmask(SIG_SETMASK, &oldset, NULL); pause(); if (should) tax = sales * rate; else tax = 0.0; printf("Tax: %5.2f\n", tax); break; } return 0; }
pause()
is invoked, in which
case pause()
will never return.
sigprocmask(SIG_BLOCK, &set, &oldset); pause(); sigprocmask(SIG_BLOCK, &oldset, NULL);
sigprocmask()
but before the call to
pause()
volatile
:
sig_atomic_t
: