sigsuspend 函数和 abort 函数

pause 函数可以阻塞进程以等待一个信号的发生,但如果该信号是阻塞的,那么该信号的传递就会被推迟直到解除阻塞。对应用程序而言,就好像该信号发生在解除阻塞和 pause 之间(取决于内核如何实现信号)。如果发生了这种情况,或者如果在解除阻塞时刻和 pause 之间确实发生了信号,那么就会产生问题,因为可能不会再见到该信号,这就使得 puase 永远阻塞。因此需要在一个原子操作中先恢复信号屏蔽字,然后使进程休眠。函数 sigsuspend 就提供了这种功能。

#include <signal.h>
int sigsuspend(const sigset_t *sigmask); /* 返回值:-1,并将 errno 设置为 EINTR */

它会把进程的信号屏蔽字设置为由 sigmask 指向的值。在捕捉到一个信号或发生了一个会终止进程的信号之前,该进程会被挂起。当捕捉到一个信号并从该信号的处理程序返回后,sigsuspend 才返回,并恢复该进程的信号屏蔽字。
下面这个示例演示了如何使用 sigsuspend 函数来保护代码临界区,避免其被特定信号中断。其中的 pr_mask 函数见[url=http://aisxyz.iteye.com/admin/blogs/2395420]sigsetjmp和siglongjmp函数[/url]。

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

extern void pr_mask(const char *);
static void sig_int(int);

int main(void){
sigset_t newmask, oldmask, waitmask;
pr_mask("program start:");
if(signal(SIGINT, sig_int) == SIG_ERR)
printf("signal(SIGINT) error\n");
sigemptyset(&waitmask);
sigaddset(&waitmask, SIGUSR1);
sigemptyset(&newmask);
sigaddset(&newmask, SIGINT);
if(sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
printf("SIG_BLOCK error\n");
pr_mask("in critical region:"); // critical region of code.
sigsuspend(&waitmask); // pause, allowing all signals except SIGUSR1
pr_mask("after return from sigsuspend:");
// reset signal mask which unblocks SIGINT.
if(sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
printf("SIG_SETMASK error\n");
pr_mask("program exit:");
exit(0);
}

static void sig_int(int signo){
pr_mask("\nin sig_int:");
}

运行结果:

$ ./criticalRegion.out
program start:
in critical region: SIGINT
^C # 键入中断字符
in sig_int: SIGINT SIGUSR1
after return from sigsuspend: SIGINT
program exit:

在调用 sigsuspend 时,将 SIGUSR1 信号加到了进程的信号屏蔽字中,所以当运行 SIGINT 的信号处理程序时,我们得知信号屏蔽字已经改变了。而在 sigsuspend 返回时,它将信号屏蔽字恢复为调用它之前的值。
sigsuspend 的另一种应用是等待一个信号处理程序设置一个全局变量。下面这个程序用于捕捉中断信号和退出信号,但是仅当捕捉到退出信号时才唤醒主例程。

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

volatile sig_atomic_t quitflag; // set nonzero by signal handler

static void sig_int(int signo){
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)
printf("signal(SIGINT) error\n");
if(signal(SIGQUIT, sig_int) == SIG_ERR)
printf("signal(SIGQUIT) error\n");
sigemptyset(&zeromask);
sigemptyset(&newmask);
sigaddset(&newmask, SIGQUIT);
// block SIGQUIT and save current signal mask.
if(sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
printf("SIG_BLOCK error\n");
while(quitflag == 0)
sigsuspend(&zeromask);
quitflag = 0; // SIGQUIT has been caught and is now blocks
if(sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
printf("SIG_SETMASK error\n");
exit(0);
}

程序输出示例:

$ ./globalVar.out
^C # 键入中断字符
interrupt
^C # 再一次
interrupt
^\$ # 键入退出符终止


abort 函数可使程序异常终止。

#include <stdlib.h>
void abort(void);

此函数将 SIGABRT 信号发给调用进程(进程不应忽略此信号),并向主机环境递送一个未成功终止的通知,其方法是调用 raise(SIGABRT) 函数。ISO C 要求若捕捉到此信号而且相应信号处理程序返回,abort 仍不会返回到调用者。要这样做的唯一方法是它调用 exit、_exit、_Exit、longjmp 或 siglongjmp 之一。POSIX.1 也说明 abort 并不理会进程对此信号的阻塞和忽略。让进程捕捉 SIGABRT 的意图是:在进程终止前由其执行所需的清理操作。如果进程并不在信号处理程序中终止自己,POSIX.1 声明当信号处理程序返回时,abort 终止该进程。
下面这个程序是 abort 函数的一个 POSIX.1 实现。

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void abort(void){ // POSIX-stype abort() function
sigset_t mask;
struct sigaction action;
/* Caller can't ignore SIGABRT, if so reset to default */
sigaction(SIGABRT, NULL, &action);
if(action.sa_handler == SIG_IGN){
action.sa_handler = SIG_DFL;
sigaction(SIGABRT, &action, NULL);
}
if(action.sa_handler == SIG_DFL)
fflush(NULL); // flush all open stdio streams
/* Caller can't block SIGABRT; make sure it's unblocked */
sigfillset(&mask);
sigdelset(&mask, SIGABRT); // mask has only SIGABRT turned off
sigprocmask(SIG_SETMASK, &mask, NULL);
kill(getpid(), SIGABRT); // send the signal

/* If we're here, process caught SIGABRT and returned */
fflush(NULL); // flush all open stdio streams
action.sa_handler = SIG_DFL;
sigaction(SIGABRT, &action, NULL); // reset to default
sigprocmask(SIG_SETMASK, &mask, NULL); // just in case ...
kill(getpid(), SIGABRT); // and one more time
exit(1); // this should never be executed ...
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值