使用sigaction来取代signal作为信号处理器函数

早期ISO C提供了像这样的函数来支持自定义信号处理

typedef void (*sighandler)(int);
sighandler signal(sighandler func);

但是由于标准库并不涉及系统层次,所以很多细节方面都是未定义的,比如在执行某信号(下文均以SIGINT为例)的处理器函数时,是否阻塞该信号?

给出一段代码(均忽略了对系统调用的错误处理)

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

void handler(int sig)
{
    if (sig == SIGINT)
    {
        sigset_t mask;
        sigprocmask(SIGINT, NULL, &mask);
        if (sigismember(&mask, SIGINT))
            printf("执行SIGINT处理器函数时阻塞了该信号\n");
        else
            printf("执行SIGINT处理器函数时没有阻塞该信号\n");
    }
}

int main()
{
    signal(SIGINT, handler);
    raise(SIGINT);
    return 0;
}

这段代码的执行结果是未定义的,参考APUE,早期版本signal函数的问题是在进程每次接到信号对其进行处理时会将该信号动作重置为默认值(即下面的处置方式1)。

回忆下,对信号的处置方式有3种:1、采取默认行为;2、忽略信号;3、调用处理器函数(记为handler)。

而这种行为是不可靠的,因为可能会导致信号的丢失。以考虑这样的时间顺序:
进入handler -> 又收到这个信号 -> 退出handler

虽然如果handler非常简单,其执行时间微乎其微的话,出现这种情况的可能性很小,但还是有出现的可能,这点不能置之不理。

结果就是,我们其实是想要用handler来取代默认行为,然后却执行了默认行为,假如默认行为是结束程序,那就是个问题了。

比如信号是SIGFPE(算术异常,比如0作为除数),我们要执行N次运算,每次都要检查,如果算术异常则将错误信息写到日志里(即handler的代码块),然后继续下次运算,我们不想因为一次运算错误而导致后面的运算都没进行就结束程序。

绝大多数情况下,可能handler内的代码执行时间很短,而SIGFPE短时间大量重复发送的可能性很小,导致一段有隐患的代码通过了测试。但是假如这段代码在长时间运行的服务器中,出现这种隐患的可能性越来越大,不可靠的signal函数很有可能会在某次出现问题。

虽然我们可以改变signal的内部实现,但由于不能跨平台,而且另一方面signal很多东西不能自定义,在最新的应用程序中应该使用sigaction来取代signal。

用法就是把上述代码的signal(SIGINT, handler);改成下面这样

    struct sigaction sa;
    sa.sa_handler = handler;
    sa.sa_flags = 0;  // 默认是执行处理器函数时阻塞该信号
//    sa.sa_flags |= SA_NODEFER;  // 若设置了SA_NODEFER, 则行为和早期的signal一样
    sigemptyset(&sa.sa_mask);
    sigaction(SIGINT, &sa, NULL);

通过像上述代码一样设置sa_flags来自定义处理器函数的行为,更多行为可以man 2 sigaction来查询手册

时间: 2024-08-01 10:44:57

使用sigaction来取代signal作为信号处理器函数的相关文章

linux编程下signal()函数

linux编程下signal()函数 当服务器close一个连接时,若client端接着发数据.根据TCP协议的规定,会收到一个RST响应,client再往这个服务器发送数据时,系统会发出一个SIGPIPE信号给进程,告诉进程这个连接已经断开了,不要再写了.根据信号的默认处理规则SIGPIPE信号的默认执行动作是 terminate(终止.退出), 所以client会退出. 若不想客户端退出可以把 SIGPIPE设为SIG_IGN 如: signal(SIGPIPE,SIG_IGN); 这时SI

signal函数、sigaction函数及信号集(sigemptyset,sigaddset)操作函数

信号是与一定的进程相联系的.也就是说,一个进程可以决定在进程中对哪些信号进行什 么样的处理.例如,一个进程可以忽略某些信号而只处理其他一些信号:另外,一个进程还可以选择如何处理信号.总之,这些总与特定的进程相联系的.因此,首 先要建立其信号和进程的对应关系,这就是信号的安装登记. Linux 主要有两个函数实现信号的安装登记:signal和sigaction.其中signal在系统调用的基础上实现,是库函数.它只有两个参数,不支持信号 传递信息,主要是用于前32个非实时信号的安装:而sigact

UNIX环境编程学习笔记(25)——信号处理进阶学习之 sigaction 函数

lienhua342014-11-05 sigaction 函数跟 signal 函数一样,用于设置信号处理函数.此函数是用于取代 UNIX 早期版本使用的 signal 函数.UNIX 早期版本的 signal 函数在捕获到一个信号之后,就会自动将该信号的处理动作恢复为默认处理动作.于是,如果我们希望注册的信号处理函数长期生效,则需要在信号处理程序中再次调用 signal 函数注册一次.这样的操作太麻烦,而且在信号处理函数中再次调用 signal 注册信号处理函数之前可能又会产生该信号,而这个

10.3 signal函数

UNIX系统的信号特性的最简单的接口就是signal函数: #include <signal.h> void (*signal(int signo, void(* func)(int)))(int); Returns:previous disposition of signal(see following)if OK,SIG_ERR on error. 函数signal由ISO C定义,并不涉及到多进程,进程组终端IO等等,因此,其信号的定义的模糊不清的,因此对于UNIX系统来说几乎没有用.

信号处理篇alarm ferror kill mkfifo pause pclose perror pipe popen sigaction sigaddset sigdelset sigemptyset signal sleep strerror

alarm(设置信号传送闹钟) 相关函数 signal,sleep 表头文件 #include<unistd.h> 定义函数 unsigned int alarm(unsigned int seconds); 函数说明 alarm()用来设置信号SIGALRM在经过参数seconds指定的秒数后传送给目前的进程.如果参数seconds 为0,则之前设置的闹钟会被取消,并将剩下的时间返回. 返回值 返回之前闹钟的剩余秒数,如果之前未设闹钟则返回0. 范例 #include<unistd.h

signal 和 sigaction

signal,此函数相对简单一些,给定一个信号,给出信号处理函数则可,当然,函数简单,其功能也相对简单许多,简单给出个函数例子如下: [cpp] view plaincopy 1 #include <signal.h> 2 #include <stdio.h> 3 #include <unistd.h> 4 5 void ouch(int sig) 6 { 7     printf("I got signal %d\n", sig); 8     /

UNIX高级环境编程(13)信号 - 概念、signal函数、可重入函数

信号就是软中断. 信号提供了异步处理事件的一种方式.例如,用户在终端按下结束进程键,使一个进程提前终止. ? 1 信号的概念 每一个信号都有一个名字,它们的名字都以SIG打头.例如,每当进程调用了abort函数时,都会产生一个SIGABRT信号. 每一个信号对应一个正整数,定义在头文件<signal.h>中. 没有信号对应整数0,kill函数使用信号编号0表示一种特殊情况,所以信号编号0又叫做空信号(null signal). 下面的各种情况会产生一个信号: 当用户在终端按下特定的键时,会产生

linux 信号signal和sigaction理解

今天看到unp时发现之前对signal到理解实在浅显,今天拿来单独学习讨论下. signal,此函数相对简单一些,给定一个信号,给出信号处理函数则可,当然,函数简单,其功能也相对简单许多,简单给出个函数例子如下: [cpp] view plaincopy 1 #include <signal.h> 2 #include <stdio.h> 3 #include <unistd.h> 4 5 void ouch(int sig) 6 { 7     printf(&quo

信号之signal函数的使用

typedef  void(*sighandler_t)(int)    =====>  xxx   就是  void xxx(int y)  的函数指针 入口地址  sighandler_t signal(int signum,sighandler_t handler)  =====>void (*signal(int signum , (void *))(int)))(int) handler就是函数的入口地址 /* #include<signal.h> 作用:注册信号的行为