|
|||||
| | |||||
generated: sent to signal (from user/ from a timer)
delivered: when takes action based on the signal
lifetime: duration of deliverd - generated
pending:
catches a signal: executes a signal handler when the signal is delivered
if signal is delivered, and igored, that's the end of the signal
struct sigaction tells what to do with a signal after its delivered.
a blocked signal stays pending until (and not delivered to the program) until the signal is unblocked; blockign temporarily delays signal delivery to the process
struct sigprocmask handles the mask for signal
all POSIX signals will have these
"implementation dependent":
SIGINT: Ctrl-C from keyboard
default action == process termination
to terminate program which as stopped SIGINT is to send a SIGKILL which cannot be caught and can't be ignored and can't be blocked
SIGSEGV: invalid memory reference (bad pointer reference)
SIGUSR: can't be generated from keystrokes
use kill function: SIGTERM is the default signal, which can be caught
SIGKILL can't be caught/ignored
kill -9 pid
kill -KILL pid
kill -l
lists signals understood by the system
kill function in code
#include <sys/types.h>
#include <signal.h>
int kill (pid_t pid, int sig);
if (kill (3423, SIGUSR1) == -1)
perror("fialed to kill parent");
if (kill(getpid(), SIGTERM) == -1)
perror ("failed to kill parent");
if (raise (SIGUSR1) != 0)
perror("failed to raise SIGUSR1");
int main(void) {
alarm(10);
for (; ; );
}
/* sleep doesn't use cpu time */
/* alarm does use cpu time */
question: what happens after termination?
The datatype which held the signalmask was originally an integer and each bit in the integer (e.g. bit 0 set and bit 1 cleared) meant something. Worked so long as had only 32 signals, now, we have more signals than 32 and its not a good idea to tie them to the size of an int.
remember file descriptors, we had fd_set (a set of integers), which was a set of integers is just a set of integers, but the method of handling them is different. For signals, here the data type is the sigset_t.
*sigemptyset(&set)* takes out all signals
*sigaddset(&set, SIGNAL)
*sigfillset()* fills all existing signals to set
*sigdelset()*
*sigismember()* tells if the signal is inside the set and returns true if it is and false if it is not.
*sigpending()* The set of pending signals is returned by the sigpending(2) system call.
Note: blocked signals remain pending until the signal is unblocked
Note: A signal may also be blocked, in which case its delivery is postponed until it is unblocked.
the function which then acts on the sigset is sigprocmask:
#include <signal.h>
sigprocmask (int how, const sigset_t * restrict set, sigset_t * restrict oset);
/* passing pointers to the struct just because it is */
/* more efficient to do so than to pass the whole structure */
the how parameter can be: SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK
If SIG_SETMASK that becomes the mask. for SIG_BLOCK whatever is in the block becomes blocked and everything else is not affected. for SIG_UNBLOCK unblocks all in the set, everything else is not affected.
so, can be used to change and insepect and save the signal mask.
either set or oset can be NULL poionters, if oset is NULL it means we are not interested in the old signal mask, if set is null, we don't want to chnage the signal mask we just want to see what the values are.
some basic, simple examples:
if ((sigemptyset(&twosigs) == -1) || sigaddset(&twosigs, SIGINT) == -1) || sigaddset(&twosigs, SIGQUIT) == -1)
perror ("failed to set up a signal mask");
sigset_t newsigset;
if ((sigemptyset(&newsignet) == -1 || sigaddset(&newsigset, SIGINT) == -1))
perror("Failed to initialize the signal set");
else if (sigprocmask(SIG_BLOCK, &newsigset, NULL) == -1)
perror("failed to block SIGINT");
sigemptyset(&intmask);
sigaddset(&intmask, SIGINT);
sigprocmask(SIG_BLOCK, &intmask, NULL);
.. do calculations
sigprocmask(SIG_UNBLOCK, &intmask, NULL);
.. do calculations
we could kill process during last calculations using Ctrl-C
but not the former ones
terminal "noecho" setting will stay in effect if login program is killed when setting is in effect
setecho(fd, 1); /* turns off echo */
ctermid(termbuf);
fd = open(termbuf, O_RDONLY);
one way is to call the signal function like this:
signal(SIGINT, catch_int);
signal(SIGTSTP, catch_suspend);
the full code is at
http://users.actcom.co.il/~choo/lupg/tutorials/signals/signals-programming.html
another way, is to call sigaction and set a sigaction structure's sa_handler
flag to a function name to do the handling.
I suppose at this point that sigaction can therfore be called with different
sigcation structures for different signals?
sigaction is a function which determines the action of a signal when a signal is delivered.
There is also a structure and function called sigaction.
so sigaction can refer to either a data structure or a function
sometimes sa_flags can be set to sa_restart. This automatically restarts system calls when a system comes in. So, a read or write will not get EINTR, wo we could omit that check. Problem with this is that it doesn't always work right, like it says it will,even on systems which say that it has this flag available.
#include <signal.h>
int sigaction(int signo, const struct sigaction * act, struct sigaction *oact);
/* either sigaction structs can be NULL
/* if first is null don't change anything, but stores what it was
/*
struct sigaction {
void (*sa_handler)(int); /* SIG_DFL, SIG_IGN, or pointer to a function
*/
sigset_t sa_mask; /* additional signals to be blocked during execution of handler */
int sa_flags; /* special flgs and options */
void (*sa_sigaction) (int, siginfo_t *, void *); / realtime handler/
};
SIG_DFL: default action, whatever action was is what will happen
(usually the default action is to terminate the process)
SIG_IGN:
sa_mask what happens inside signal_handler? I can do it with sigset_t sa_mask this only affects how those signals behave when I am inside the signal handler
sa_siginfo then this affects the signal handler
if (sigaction (SIGINT, NULL, &act) == -1) /* find current SIGINT handler */
perror ("Failed to get old handler for SIGINT");
else if (act.sa_handler == SIG_DFL) { /* if SIGINT handler is default */
act.sa_handler = SIG_IGN;
if (sigaction (SIGINT, &act, NULL) == -1)
perror("Failed to ignore SIGINT");
}
/* becase several programs may have been written by several people */
/* in some places may not know of SIG_INT is default action */
void (catchctrl(int signo) {
char handmsg[] = "I found ctrl-C\n";
int msglen = sizeof(handmsg);
write(STDERR_FILENO, handmsg, msglen);
}
...
struct sigaction act;
act.sa_handler = catchctrlc;
act.sa_flags = 0;
if (sigemptyset(&act.sa_mask) == 01) || sigaction (SIGINT, &act, NULL) == -1)
perror("faild to est SIGINT to handle CTRL-C");
struct sigaction newact;
newact.sa_handler = SIG_DFL;
newact.sa_flags = 0;
if ((isgemptyset(&newact.sa_mask) == -1) ||
sigaction(SIGINT, &newact, NULL) == -1)
perror();
static int doneflag(int signo) {
doneflag = 1;
}
struct sigaction act;
act.sa_handler = setdoneflag;
sigemptyset(&act.sa_mask);
sigaction(SIGINT, &act, NULL);
/* runs loops until have done setdoneflag */
static volatile sig_atomic_t doneflag = 0;
static: this variable not accessible outside this file, inside both main and inside the doneflag function, static just makes it less global than program-scope global.
volatile: tells compiler that this thing may change through methods unknown to the compiler. in loop, if (!doneflag) { ... } the compiler can't see that doneflag will ever change.
sig_atomic_t: when a variable is assigned on some systems, it is possible that the assignment is done in more than one step. A double on most machines is 64 bits but say the machine has a 32 bit data path mechanism. So, it was converted between 2 instructions. They are not atomic so that the cpu could get lost between them. Sometimes, we worry about this, so we choose an "int" which is small enough to be done in one instruction. It need not be an "int" but will be whatever can be done on that system atomically.
summary: pause is bad, sigsuspend is good
2 uses:
1. waiting for something else to happen before continuing
-- need to avoid busy waiting
-- is there a way to remove myself from the ready queue?
2. getting an asynchronous signal from somewhere else
#include <unistd.h>
int pause(void);
static volatile sig_atomic_t sigreceived = 0;
while (sigreceived = 0)
pause(); /* removes process from cpu until signal is received */
after a signal is received, sigreceived is again checked
the idea is to wait until a signal is achieved
only works 99.9999% of the time
what could go wrong? cpu received before pause but after (while (sigreceived = 0))
YOU CANT GET AROUND THIS PROBLEM COMPLETELY USING PAUSE
after checking to see if signal is received nad it's not and we are just about to suspend process, and th esignal comes in, we solve htis problem by making sure that the signal is blocked at the time the process is suspended.
/* need pause replacmenet which does 2 things atomicsally:
- - block signal while checking if sigreceived == 0
-- block signal while causign pause
HOWEVER, signal will be blocked inside pause, too */
static volatile sig_atomic_t
#include <signal.h>
int sigsuspend(const sigset_t * sigmask);
does 2 things: suspends the process and sets signalmask
returns when a signal is delivered
returns sigmask to what it was before calling sigsuspend
then blocks signal again.
static volatile sig_atomic_t sigreceived = 0;
sigset_t maskblocked, maskold, maskunblocked;
int signum = SIGUSR1;
sigprocmask(SIG_SETMASK, NULL, &maskblocked);
sigprocmask(SIG_SETMASK, NULL, &maskunblocked);
sigaddset(&maskblocked, signum);
sigdelset(&maskunblocked, signum); // this set doesn't contain signumand whatever is in the set was in there before
sigprocmask(SIG_BLOCK, &maskblocked, &maskold); // need maskold because at end will restore that mask
while (sigreceived == 0)
sigsuspend (&maskunblocked);
sigprocmask(SIG_SETMASK, &maskold, NULL); /* resets mask to original value */
these are for exam, sigsuspend and understand why each lines is necessary
<url:/languages/c/unix_c/signals/simplesuspend.c:simplesuspend.c>
this file does not contain main, I know.... but please continue .... these
are utility functions. The file has many static variables to share between main and functions.
catcher is the signal handler, which all it does is set signal received to
1.
To set the sa_handler to catcher call initsuspend, then can call routine "simplesuspend()" to make sure it is initialized.
does above check for variable sigreceived.
(the restore routine restores signal value to what it was before)
becase in initsuspend we call *sigaction(signo, act, &oact)*
<url:/languages/c/unix_c/signals/notifyonoff.c:notifyonoff.c>
int initnotify(int signo1, int signo2) /* signo1 sets to 1, signo2 sets to 0 */
waitnotifyon /* waits until notifyflag is turned on */
/* then resets flag */
<url:/languages/c/unix_c/signals/biff.c:biff.c>
biff is a program which notices user when there is mail with a beep
uses same ideas of flag being set by a signal, and the flag is checked with sigsuspend
another way to wait for signals when we block all signals and we keep them blocked forever. We put the signals we want to wait for in
#include <signal.h>
int sigwait ( const sigset_t * restric sigmask, int * restrict signo);
Is activated when there is a pending signal; iow, "sigwiat gives you a signal that was pending"
(if signal is blocked, signal nver executes, so dont' need a signal handler)
second arg stores signal which it found
<url:/languages/c/unix_c/signals/countsignals.c:countsignals.c>
/* wait for just one signal */
if ((sigemptyset(&sigset) == -1) ||
(sigaddset(&sigset, signum) == -1 ||
(sigprocmask(SIG_BLOCK, &sigset, NULL) == -1)
ERROR
for (; ; ) {
if (sigwait(sigset, &signo) == -1) {
ERROR
}
signalcount++;
fprintf("sig received");
}
what happens if a read gets an error while reading? we have to handle that, in our familiar rendition of the read call
error handlers in a signal handler;
read or rite may modify error number.
if read -> signal caught -> error from signal handler -> CONFUSION
so in signal handlers we must save errno and then reassign it.
void myhander (int signo) {
int esaved;
esaved = error;
write(STDOUT_FILENO, "got a sign'al\n", 1);
errno = esaved;
}
error would have to occur between time of I/O and time of error in system call, which is uncommon
which functions can be used?
can't use strtok inside a signal hande\ler, because we are in a main loop, familiar thing with strtok non-static problems. Only a few functions are asynch signal safe.
complete list on webpage for course
there are about 100 functions, including read/write/pipe/open/dup2/time
No c-library functions (strlen, fprintf [remember need to exclude fprintf from signal handler])
void myhander (int signo) {
int esaved;
esaved = error;
write(STDOUT_FILENO, "got a signal\n", 13); /* can't call strlen */
errno = esaved;
}
some problems with this: write may not write out everything, write may make errors, can do sizeof(array_made_from_string). so a solution is the folloing: char msg[[] = "got a signal\n"; size = 14 because it counts the string terminator. If we did char * msg = "got a signal\n"; would give a sizeof() 4 (or 8 on some systems).
all operating systems should have this list of asynch signal safe functions.
struct sigaction {
void (*sa_handler)(int); /* SIG_DFL, SIG_IGN, or pointer to a function
*/
sigset_t sa_mask; /* additional signals to be blocked during execution of handler */
int sa_flags; /* special flgs and options */
void (*sa_sigaction) (int, siginfo_t *, void *); / realtime handler/
};
we could set our handler in either last or first field.
If the SA_SIGINFO flag of the sa_flags field is set sa_sigation specifies the action to be taken.
This defines a new type of signal handler which takes 3 parameters instead of one. It also defines a new datatype siginfo_t which is a structure containing at least the following
int si_signo; /* signal number */
int si_code; /* cause of the signal */
union sigval si_value; /* signal value */
points 2,3 are in addition to the default signal handler
where the union sigval contains at least:
int sival_int;
void * sival_ptr;
2. counts number of times
old signal handlers, just uses flag for setting bit is only one bit which doesn't count signals. Other systems use a counter for the number of times that a signal is pending.
Sometimes, we don't want to miss a signal. We could miss not just by blocking. We could be in ready queue and can't receive signal until we get the cpu.
But if system supports signal handler (new style) if 3 signals are generated, then 3 will be delivered.
#include <signal.h>
int sigqueue(pid_t pid, int signo, const union sigval value);
/* final param can be either an integer or a pointer */
there is no command like sigqueue. An example of writing it itself is <url:/languages/c/unix_c/signals/sendsigqueue.c:sendsigqueue.c>
int main() {
union sigval value;
value.sival_int = sval;
if (sigqueue(pid, signo, value) == -1) {
..
}
<url:/languages/c/unix_c/signals/sigqueuehandler.c:sigqueuehandler.c>
static void myhandler(int signo, siginfo_t * int\fo, void * context) {
char * code= NULL;
switch (info->si_code) {
case SI_USER: code = "USER"; break;
case SI_QUEUE: code = "USER"; break;
case SI_TIMER: code = "USER"; break;
case SI_ASYNCIO: code = "USER"; break;
case SI_MSGQUEUE: code = "USER"; break;
default: code = "unknown";
}
fprintf("...");
fprintf("...");
}
/* about fprintf .. can't print the number as string, can't convert with snprintf ...
/* is "unsafe to use fprintf inside a signal handler" ...
/* sometimes fprintf may use static variables, so if it's in middle of main
/* and printfing, then moves to signalhandler, then when program goes
/* back to main, will confuse fprintf
/* not signal safe therefore because the c specification doesn't say if fprintf
/* uses static or not
/* therefore we can use fprintf becase not in main
/* well, its used but only before we call the signal handler
/* "if you use it you need to know it isn't used anywhere else"
main() {
act.sa_flags = SA_SIGINFO;
act.sa_sigaction = myhandler;
if ((sigemptyset(&act.sa_mask) == -1) ||
sigaction(SIGUSR1,&act,NULL) == -1) {
ERROR
for (; ;)
pause();
}
/* be careful when using pause
/* all this pause does is avoid busy waiting until a signal comes in
/* I'm not testing anything so pause is usable here
/* in general, don't use pause when waiting for a signal because we don't
/* know if a signal came in between checking the variable and
/* calling pause, but here we aren't checking against any value after/before
/* the pause
| Leave a Reply |