分类:
2008-12-08 12:16:53
10.16 sigsuspend
function
#include
int sigsuspend(const sigset_t *sigmask); |
Returns: 1 with errno set to EINTR |
这个函数可以允许我们以一个指定的signal mask来block。当指定的信号到来时,suspend返回,signal mask自动还原为sigsuspend调用之前的状况。
注意,制定信号的handler在sigsuspend返回之前执行。
适用场合:
如果我们想执行一段代码,不想让他被SIGINT打断,当我们工作完成后,又想着等待该信号的到来,如果这样做:
(一)不好的做法
(1)sigprocmask先将SIGINT信号给block
(2)做工作
(3)工作完成,将对SIGINT的block打开,用sigprocmask
(4)调用pause()
上面这种方法已经被证明是存在race condition的,因为在(3)和(4)之间,甚至在(3)的sigprocmask函数返回之前,以及在工作中都有可能接收到这个信号,导致我们在pause()之前,即(3)和(4)之间或者(3)的sigprocmask返回之前,就执行了该信号的handler。那么我们的pause()将不会再得到信号。根本原因在于(3)(4)之间,打开block和pause()不是原子操作。
这种做法的代码:
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 */
/* reset signal mask, which
unblocks SIGINT */
if (sigprocmask(SIG_SETMASK,
&oldmask, NULL) < 0)
err_sys("SIG_SETMASK
error");
/* window is open */
pause(); /* wait for signal to occur */
/* continue processing */
(二)好的作法
(1)sigprocmask先将SIGINT信号给block
(2)做工作
(3)工作完成,
调用suspend(&newset)来进入block状态,在newset中我们指定SIGINT不被block。
这样(3)中SIGINT的unblock操作和等待成了一个原子操作。我们不再丢失SIGINT。
这种做法的代码:
#include "apue.h"
static void sig_int(int);
int
main(void)
{
sigset_t newmask, oldmask, waitmask;
pr_mask("program start:
");
if (signal(SIGINT, sig_int) ==
SIG_ERR)
err_sys("signal(SIGINT)
error");
sigemptyset(&waitmask);
sigaddset(&waitmask,
SIGUSR1);
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.
*/
pr_mask("in critical region:
");
/*
* Pause, allowing all signals
except SIGUSR1.
*/
if (sigsuspend(&waitmask) !=
-1)
err_sys("sigsuspend
error");
pr_mask("after return from
sigsuspend: ");
/*
* Reset signal mask which
unblocks SIGINT.
*/
if (sigprocmask(SIG_SETMASK,
&oldmask, NULL) < 0)
err_sys("SIG_SETMASK
error");
/*
* And continue processing ...
*/
pr_mask("program exit:
");
exit(0);
}
static void
sig_int(int signo)
{
pr_mask("\nin sig_int:
");
}
执行结果:
$ ./a.out
program start:
in critical region: SIGINT
^? type the interrupt character
in sig_int: SIGINT SIGUSR1
after return from sigsuspend:
SIGINT
program exit
还有一个例子,这个例子等待SIGINT和SIGQUIT信号,但只有SIGQUIT信号使程序退出。:
#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 */
}
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)
err_sys("SIG_SETMASK
error");
exit(0);
}
结果:
$ ./a.out
^? type the
interrupt character
interrupt
^? type
the interrupt character again
interrupt
^? and
again
interrupt
^? and
again
interrupt
^? and
again
interrupt
^? and
again
interrupt
^? and
again
interrupt
^\ $ now
terminate with quit character