信号之函数sigaction、sigsetjmp、siglongjmp、sigsuspend以及abort

本文来自个人博客:https://dunkwan.cn

函数sigaction

sigaction函数的功能是检查或修改(或检查并修改)与指定信号向关联的处理动作。此函数取代了UNIX早期版本使用的signal函数。

#include <signal.h>
int sigaction(int signo, const struct sigaction *restrict act, struct sigaction *restrict oact);
返回值:若成功,返回0;若出错,返回-1

参数signo是要检测或修改其具体动作的信号编号。若act指针非空,则要修改其动作。如果oact指针非空,则系统经由oact指针返回该信号的上一个动作。

此函数中的sigaction结构如下:

struct sigaction{
    void (*sa_handler)(int); /* addr of signal handler */
    					   /* or SIG_IGN, or SIG_DFL */
    sigset_t sa_mask;        /* additional signals to block */
    int sa_flags;            /* signal options */
    /* alternate handler */
    void (*sa_sigaction)(int, siginfo_t *, void *);
};

以下是用sigaction函数实现signal

#include "../../include/apue.h"

/* Reliable version of signal(), using POSIX sigaction(). */
Sigfunc * signal(int signo, Sigfunc *func)
{
    struct sigaction act, oact;
    act.sa_handler = func;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    if(signo == SIGALRM){
#ifdef SA_INTERRUPT
        act.sa_flags |= SA_INTERRUPT;
#endif
    }else{
        act.sa_flags |= SA_RESTART;
    }

    if(sigaction(signo, &act, &oact) < 0)
        return (SIG_ERR);

    return (oact.sa_handler);
}

以下是支持不可重启动的signal函数版本。

#include "../../include/apue.h"

Sigfunc *signal_intr(int signo, Sigfunc *func)
{
    struct sigaction act, oact;

    act.sa_handler = func;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
#ifdef SA_INTERRUPT
    act.sa_flags |= SA_INTERRUPT;
#endif
    if(sigaction(signo, &act, &oact) < 0)
        return (SIG_ERR);
    return (oact.sa_handler);
}

函数sigsetjmpsiglongjmp

在信号处理程序中进程非局部转移时应当使用这两个函数。

#include <setjmp.h>
int sigsetjmp(sigjmp_buf env, int savemask);
返回值:若直接调用,返回0;若从siglongjmp调用返回,则返回非0void siglongjmp(sigjmp_buf env, int val);

这两个函数和setjmplongjmp之间的唯一区别是sigsetjmp增加了一个参数。如果savemask非0,则sigsetjmpenv中保存进程当前信号屏蔽字。调用siglongjmp时,如果带非0savemasksigsetjmp调用已经保存了env,则siglongjmp从其中恢复保存的信号屏蔽字。

测试示例:

#include "../../include/apue.h"
#include <setjmp.h>
#include <time.h>
#include <errno.h>

static void sig_usr1(int);
static void sig_alrm(int);
static sigjmp_buf jmpbuf;
void pr_mask(const char *);
static volatile sig_atomic_t canjump;

int main(void)
{
    if(signal(SIGUSR1, sig_usr1) == SIG_ERR)
        err_sys("signal(SIGUSR1) error");
    if(signal(SIGALRM, sig_alrm) == SIG_ERR)
        err_sys("signal(SIGALRM) error");

    pr_mask("starting main: ");

    if(sigsetjmp(jmpbuf, 1)){
        pr_mask("ending main: ");
        exit(0);
    }

    canjump = 1;

    for(;;)
        pause();
}

static void sig_usr1(int signo)
{
    time_t starttime;

    if(canjump == 0)
        return;

    pr_mask("starting sig_usr1: ");

    alarm(3);
    starttime = time(NULL);
    for(;;)
        if(time(NULL) > starttime + 5)
            break;

    pr_mask("finishing sig_usr: ");

    canjump = 0;
    siglongjmp(jmpbuf, 1);
}

static void sig_alrm(int signo)
{
    pr_mask("in sig_alrm: ");
}

void pr_mask(const char *str)
{
    sigset_t sigset;
    int errno_save;

    errno_save = errno;
    if(sigprocmask(0, NULL, &sigset) < 0){
        err_ret("sigprocmask error");
    }else {
        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;
}

结果如下:

函数sigsuspend

如果希望对一个信号解除阻塞,然后pause以等到以前被阻塞的信号发生,假定该信号是SIGINT,实现这一点的并不正确的方法是:

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  */

/* restore signal mask, which unblocks SIGINT */
if(sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
    err_sys("SIG_SETMASK error");

/* window is open */
pause();

/* continue processing */

如果在信号被阻塞时, 产生了信号,那么该信号的传递就被推迟直到对它解除了阻塞。对应用程序而言,该信号好像发生在解除对SIGINT的阻塞和pause之间。如果发生了这种情况,或者如果在解除阻塞时刻和pause之间确实发生了信号,那么就会产生问题。因为可能不会再见到该信号,所以从某种意义上讲,在此时间窗口中发生的信号丢失了。这样使得pause永远阻塞。这是早期的不可靠信号机制的另一个问题。

sigsuspend函数用于需要在一个原子操作中先恢复信号屏蔽字,然后使进程休眠。

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

进程的信号屏蔽字设置为由sigmask指向的值。在捕捉到一个信号或发生了一个会终止该进程的信号之前,该进程被挂起。如果捕捉到一个信号而且从该信号处理程序返回,则sigsuspend返回,并且该进程的信号屏蔽字设置为调用sigsuspend之前的值。

注意,此函数没有成功返回。如果它返回到调用者,则总是返回-1,并将errno设置为EINTR(表示一个被中断的系统调用)。

测试示例1:

保护代码临界区,使其不被特定信号中断的方法。

#include "../../include/apue.h"

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

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);



    if(sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
        err_sys("SIG_BLOCK error");


    pr_mask("in critical region: ");

    if(sigsuspend(&waitmask) != -1)
        err_sys("sigsuspend error");

    pr_mask("after return from sigsuspend: ");


    if(sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
        err_sys("SIG_SETMASK error");

    pr_mask("program exit: ");

    exit(0);
}

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

void pr_mask(const char *str)
{
    sigset_t sigset;
    int errno_save;

    errno_save = errno;
    if(sigprocmask(0, NULL, &sigset) < 0){
        err_ret("sigprocmask error");
    }else {
        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;
}

结果如下:

测试示例2:

等待一个信号处理程序设置一个全局变量。

#include "../../include/apue.h"

volatile sig_atomic_t quitflag;

static void sig_int(int signo)
{
    signal(SIGINT, sig_int); /* additional code  */
    signal(SIGQUIT, sig_int);/* additional code  */
    if(signo == SIGINT)
        printf("\ninterrupt\n");
    else if(signo == SIGQUIT)
        quitflag = 1;
}

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);


    if(sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
        err_sys("SIG_BLOCK error");

    while(quitflag == 0)
        sigsuspend(&zeromask);

    quitflag = 0;

    if(sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
        err_sys("SIG_SETMASK error");


    return 0;
}

结果如下:

注意: 测试示例2中additional code处为另加的代码,由于书中代码的运行版本与测试机版本可能存在很多差异,如若按源代码运行无法得到预期结果,所以特别添加此处代码,这才得到原书中所要达到的结果。

用信号实现父子进程之间的同步。

#include "apue.h"

static volatile sig_atomic_t sigflag; /* set nonzero by sig handler */
static sigset_t newmask, oldmask, zeromask;

static void
sig_usr(int signo)	/* one signal handler for SIGUSR1 and SIGUSR2 */
{
	sigflag = 1;
}

void
TELL_WAIT(void)
{
	if (signal(SIGUSR1, sig_usr) == SIG_ERR)
		err_sys("signal(SIGUSR1) error");
	if (signal(SIGUSR2, sig_usr) == SIG_ERR)
		err_sys("signal(SIGUSR2) error");
	sigemptyset(&zeromask);
	sigemptyset(&newmask);
	sigaddset(&newmask, SIGUSR1);
	sigaddset(&newmask, SIGUSR2);

	/* Block SIGUSR1 and SIGUSR2, and save current signal mask */
	if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
		err_sys("SIG_BLOCK error");
}

void
TELL_PARENT(pid_t pid)
{
	kill(pid, SIGUSR2);		/* tell parent we're done */
}

void
WAIT_PARENT(void)
{
	while (sigflag == 0)
		sigsuspend(&zeromask);	/* and wait for parent */
	sigflag = 0;

	/* Reset signal mask to original value */
	if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
		err_sys("SIG_SETMASK error");
}

void
TELL_CHILD(pid_t pid)
{
	kill(pid, SIGUSR1);			/* tell child we're done */
}

void
WAIT_CHILD(void)
{
	while (sigflag == 0)
		sigsuspend(&zeromask);	/* and wait for child */
	sigflag = 0;

	/* Reset signal mask to original value */
	if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
		err_sys("SIG_SETMASK error");
}

函数abort

abort函数用于使程序异常终止。

#include <stdlib.h>
void abort(void);
此函数不返回值。

以下是按POSIX.1说明实现的abort函数。

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

void
abort(void)			/* POSIX-style 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、付费专栏及课程。

余额充值