Unix信号处理函数及简单代码示例

  • Post author:
  • Post category:其他




概述



1、什么是信号

  • 信号是一种内容受限的进程间通信机制。
  • 信号是一种软件中断,是异步的。
  • 信号就是int型的数字编号。



2、产生信号的四种场景

  1. 中断按下按键,比如ctrl+c
  2. 内核给应用程序发,比如访问无效内存、硬件故障等
  3. 使用kill命令
  4. 软件在满足某种条件的时候,比如alarm时间到了。



3、信号的三种处理方式

  1. 忽略信号:除了SIGKILL和SIGSTOP,都是可以忽略的或者被捕获
  2. 捕获信号:使用自定义函数处理信号
  3. 默认处理:包括忽略、终止、终止+core



常见的信号

  1. SIGINT:ctrl+c时候,内核给每个前台进程发送这个信号
  2. SIGABRT:调用abort函数,让进程异常终止
  3. SIGPOLL、SIGIO:异步IO事件会用到
  4. SIGKILL:杀死进程,并且信号不能被忽略
  5. SIGSEGV:无效的内存访问时,内核会发出这个信号给应用程序
  6. SIGPIPE:管道和socket会用到
  7. SIGALARM:alarm会用到
  8. SIGTERM:kill命令发送的默认信号
  9. SIGCHLD:子进程停止时内核给父进程发送这个信号



旧注册信号处理函数:signal

signal可以注册信号处理函数,但是一致性比较差,现在推荐使用sigaction。

#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
sighandler_t:信号处理函数的函数模板,传递的参数是信号值
signum:信号值
handler:这个信号发生的时候,由哪个信号处理函数来处理。
    SIG_IGN:忽略信号
    SIG_DEF:使用默认的方式处理这个信号
    自定义函数:使用自定义函数处理这个信号
返回值:成功,返回这个信号之前绑定的函数地址;失败返回SIG_ERR,同时置上errno。

SIG_ERR、SIG_IGN和SIG_DEF宏的定义,其实就是把常数强制类型转化成__sighandler_t函数指针:

less /usr/include/bits/signum.h
#define SIG_ERR ((__sighandler_t) -1)       /* Error return.  */
#define SIG_DFL ((__sighandler_t) 0)        /* Default action.  */
#define SIG_IGN ((__sighandler_t) 1)        /* Ignore signal.  */



新注册信号处理函数:sigaction

struct sigaction {
               void     (*sa_handler)(int);
               void     (*sa_sigaction)(int, siginfo_t *, void *);
               sigset_t   sa_mask;
               int        sa_flags;
               void     (*sa_restorer)(void);
};//绝大多数时候只要关注sa_handler
#include <signal.h>
int sigaction(int signum, const struct sigaction *act,
                     struct sigaction *oldact);
signum:需要捕获的信号值
act:如果act非空,那么使用act->sa_handler指向的函数来处理这个信号,如果act可以为空,这个时候不会修改这个信号的处理函数,一般用于使用oldact来获取信号当前处理函数。
oldact:如果oldact非空,那么oldact指向这个信号之前的处理函数,如果oldact为空,那么表示不关注老的处理函数。
sa_mask:信号处理函数被执行的过程中,哪些信号被阻塞,直到执行完了之后再捕获这些信号    
返回值:成功,返回0,失败返回-1,并且置上errno。



信号集的处理

信号集一般和struct sigaction配合使用,比如使用sigaction捕获信号的时候,希望把其他的好几个信号阻塞,等被捕获的信号处理完了之后,再恢复其他信号的处理,就可以使用信号集的处理函数来指定哪些信号被阻塞。



清空信号集:sigemptyset

sigemptyset把信号集里清空。

#include <signal.h>
int sigemptyset(sigset_t *set);
set:信号集
返回值:成功,返回0,失败,返回-1,同时置上errno  



填满信号集:sigfillset

sigfillset所有的信号都加入到信号集当中。

#include <signal.h>
int sigfillset(sigset_t *set);
set:信号集
返回值:成功,返回0,失败,返回-1,同时置上errno  



添加一个信号:sigaddset

sigaddset把某一个信号加入到信号集当中。

#include <signal.h>
int sigaddset(sigset_t *set, int signum);
set:信号集
signum:被添加的信号    
返回值:成功,返回0,失败,返回-1,同时置上errno  



删除一个信号:sigdelset

sigdelset把某一个信号从信号集中删除。

#include <signal.h>
int sigdelset(sigset_t *set, int signum);
set:信号集
signum:被添加的信号    
返回值:成功,返回0,失败,返回-1,同时置上errno  



判断信号是否在信号集中:sigismember

sigismember判断一个信号是否在信号集中。

#include <signal.h>
int sigismember(const sigset_t *set, int signum);
set:信号集
signum:待判断的信号
返回值:如果在信号集中,返回1,如果不在,返回0,如果出错,返回-1,同时置上errno   



代码实例

捕获SIGHUP的同时,阻塞SIGINT:

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <sys/types.h>

void sig_handler(int sig)
{
        if (sig != SIGHUP) {
                printf("sig %d is not SIGHUP");
                return;
        }
        int i = 0;
        for(i = 0; i < 10; i ++) {
                printf("%s\n", __func__);
                sleep(1);
        }
        return;
}

int
main(int argc, char **argv)
{
        int i;
        struct sigaction sact;
        memset(&sact, 0, sizeof(struct sigaction));
        sact.sa_handler = sig_handler;
        sigemptyset(&(sact.sa_mask));
        sigaddset(&sact.sa_mask, SIGINT);
        sact.sa_flags = 0;

        if (sigaction(SIGHUP, &sact, NULL) < 0) {
                perror("sigaction:");
                return -1;
        }

        for(i = 0; i < 10; i++) {
                printf("wait signal, pid: %d\n", getpid());
                sleep(1);
        }
        return 0;
}



版权声明:本文为a3876247995原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。