APUE学习笔记:Linux下signal和sigaction的使用区别

《UNIX环境高级编程》第三版,图10-20在Linux环境下的运行结果与书中所述有所不同,信号处理函数执行期间未自动屏蔽该信号,详见:
信号之sigsetjmp和siglongjmp函数

其原因在于:

  • glibc 2之后,如果系统定义了_BSD_SOURCE宏或_GNU_SOURCE宏的话,那么系统将通过调用sigaction函数的方式去实现signal函数,否则signal函数将遵循System V语义

signal函数在System V下的语义:

  • 信号处理函数被调用后,该信号的处理方式被重置为默认处理方式,因此在信号处理函数中要用signal函数对信号重新注册
  • 信号处理函数执行期间,该信号未被自动屏蔽

因此书中例程可修改为


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

static void         sig_usr1(int);
static void         sig_alrm(int);
static sigjmp_buf   jmpbuf;
static volatile sig_atomic_t    canjump;

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

#else
    struct sigaction sa_usr1, osa_usr1, sa_alrm, osa_alrm;
    struct sigaction *sa_usr1_p, *osa_usr1_p, *sa_alrm_p, *osa_alrm_p;
    sa_usr1_p = &sa_usr1;
    osa_usr1_p = &osa_usr1;
    sa_alrm_p = &sa_alrm;
    osa_alrm_p = &osa_alrm;

    sa_usr1_p->sa_handler = sig_usr1;
    sigemptyset(& sa_usr1_p->sa_mask);
    sa_usr1_p->sa_flags = 0;

    sa_alrm_p->sa_handler = sig_alrm;
    sigemptyset(& sa_alrm_p->sa_mask);
    sa_alrm_p->sa_flags = 0;

    if(sigaction(SIGUSR1, sa_usr1_p, osa_usr1_p) == -1)
        err_sys("sigaction(SIGUSR1,,) error");
    if(sigaction(SIGALRM, sa_alrm_p, osa_alrm_p) == -1)
        err_sys("sigaction(SIGALRM,,) error");
#endif

    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_usrr1: ");

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

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

或者可以使用以下两种方法:
关于__GNU_SOURCE 这个宏
gcc中一个编译选项 -D_GNU_SOURCE

程序运行结果:
APUE学习笔记:Linux下signal和sigaction的使用区别_第1张图片

以下引自《Linux程序员手册》:

  1. 只有在信号的处理方式被设为SIG_DFL和SIG_IGN的情况下,signal函数才是可移植的,其他情况下,signal的语义取决于系统的具体实现。
  2. 在早先的UNIX系统中,使用signal函数安装信号处理函数,等同于以sa.sa_flags = SA_RESETHAND | SA_NODEFER的方式调用sigaction函数,同时在信号处理函数执行期间,该信号的传递未被自动屏蔽
  3. BSD对此做了优化,等同于以sa.sa_flags = SA_RESTART的方式调用sigaction函数,同时引入了信号的自动屏蔽机制
  4. Linux则遵循以下语义:
    (1)若signal为内核的系统调用函数,则遵循System V语义
    (2)glic2及以后,signal函数不再执行内核系统调用,如果_BSD_SOURECE功能测试宏已定义,那么遵循BSD语义,若_GNU_SOURCE被定义,则_BSD_SOURECE被隐式定义
  5. 推荐使用sigaction而非signal函数注册信号!

你可能感兴趣的:(APUE学习)