If we want to mask off a signal, do some critical jobs, and then re-enable the signal, waiting for it to occur. We may write code like this:
//Block the signal
sigprocmask(SIG_BLOCK, &newmask, &oldmask);
/* Do some critical jobs */
//Unblock the signal
sigprocmask(SIG_SETMASK, &oldmask, NULL);
// wait for the signal to occur
pause();
There is a very small time window between sigprocmask and pause, if the signal occurs at this period, our program will never wake up.
We need one atomic operation to do both things: unblock the signal and pause.
2. sigsuspend system definition
ubuntu@ip-172-31-23-227:~$ less /usr/include/signal.h
......
/* Change the set of blocked signals to SET,
wait until a signal arrives, and restore the set of blocked signals.
This function is a cancellation point and therefore not marked with
__THROW. */
extern int sigsuspend (const sigset_t *__set) __nonnull ((1));
......
Note: sigsuspend only return -1 when stopping suspending when signal occurs, if it is paused, it doesn't return. So sigsuspend is having a problem if it does NOT return -1.
3. Example:
suspend.c:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
#include<errno.h>
void pr_mask(const char* str)
{
sigset_t sigset;
int errno_save;
errno_save = errno;
if(sigprocmask(0, NULL, &sigset) < 0) {
printf("sigprocmask error!\n");
exit(1);
}
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 ");
printf("\n");
errno = errno_save;
}
void sig_int(int signo)
{
pr_mask("\nin sig_int: ");
}
int main(int argc, char* argv[])
{
sigset_t newmask, oldmask, waitmask;
pr_mask("program start: ");
if(signal(SIGINT, sig_int) == SIG_ERR)
printf("signal error!\n");
sigemptyset(&waitmask);
sigaddset(&waitmask, SIGUSR1);
sigemptyset(&newmask);
sigaddset(&newmask, SIGALRM);
// Block the SIGINT signal
if(sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0) {
printf("sigprocmask error!\n");
exit(1);
}
// It should output the SIGUSR1 signal only
pr_mask("in critical region: ");
// sigsuspend should always return -1, if not -1, it has some error
// During suspending period, it block one more signal SIGUSR1
if(sigsuspend(&waitmask) != -1) {
printf("sigsuspend error!\n");
exit(2);
}
pr_mask("after return from sigsuspend: ");
// Unblock all signals
if(sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) {
printf("sigprocmask error!\n");
exit(1);
}
pr_mask("program exit: ");
exit(0);
}
shell:
At the beginning of the program, we setup the signal mask to be SIGALRM.
After calling sigsuspend, the current mask becomes: SIGUSR1(not having SIGALRM)
Then we type the interrupt character, and the program enters sig_int handler, where SIG_INT is masked off in addition to the SIGUSR1.
After leaving sig_int handler, SIGINT is enabled. Then sigsuspend returned, the masked signal is restored to original status: only SIGALRM is masked.
Before program exited, we unblock all signals.
ubuntu@ip-172-31-23-227:~$ ./suspend.out
program start:
in critical region: SIGALRM
^C
in sig_int: SIGINT SIGUSR1
after return from sigsuspend: SIGALRM
program exit:
No comments:
Post a Comment