Linux信号 二 信号处理函数注册

每一个信号都有一个信号处理函数,可以是SIG_IGN, SIG_DFL或者是用户自定义的处理函数。使用用户自定义的处理函数需要注册,注册接口有如下两种。

第一种是signal调用

#include 

/**
 *  sighandler_t是GNU的扩展,如果在glibc下面使用的话,编译的时候需要加上-D_GNU_SOURCE
 *  或者手动定义
 */
typedef void (*sighandler_t)(int);

/**
 *  为信号signum注册信号处理函数handler
 *  成功返回该信号之前的处理函数,失败返回SIG_ERR并将失败原因填写到errno中
 */
sighandler_t signal(int signum, sighandler_t handler);

使用signal调用会有兼容性问题,尤其是移植到其它UNIX系统上,所以推荐使用第二种信号注册函数sigaction,该函数功能相对signal而言,能够提供更多功能。

#include 
/**
 *  注册信号处理函数,成功返回0,失败返回-1并置errno
 *  参数act存储待注册的信号处理函数结构体
 *  如果oldact非空的话,旧的信号处理函数会存储到该结构体中
 */
int sigaction(int signum, const struct sigaction *act,
                     struct sigaction *oldact);

struct sigaction {
    void     (*sa_handler)(int);
    void     (*sa_sigaction)(int, siginfo_t *, void *);
    sigset_t   sa_mask;
    int        sa_flags;
    void     (*sa_restorer)(void);
};

该结构在注册信号处理函数sigaction中使用
1. sa_handler是一个参数为信号值的处理函数
2. sa_sigaction也是一个信号处理函数,不过它有三个参数,能够获取到处信号值以外更多
   信息,当sa_flags中包含SA_SIGINFO标志位的时候需要用到该函数。
3. sa_mask是信号处理函数执行期间的屏蔽信号集。就是说在信号处理函数执行期间,屏蔽某
   些信号。但是不是所有信号都能够被屏蔽,SIGKILL和SIGSTOP这两个信号就无法屏
   蔽,因为操作系统自身要能够控制住进程。
4. sa_flags可以是下面这些值的集合:
   1. SA_NOCLDSTOP,
      这个标志位只用于SIGCHLD信号。父进程可以检测子进程三个事件,子进程终止、
      子进程停止、子进程回复。SA_NOCLDSTOP标志位用于控制后两个事件。即一旦父进程
      为SIGCHLD信号设置了这个标志位,那么子进程停止和子进程恢复这两件事情,就无需
      向父进程发送SIGCHLD信号

   2. SA_NOCLDWAIT
      这个标志只用于SIGCHLD信号,它可控制子进程终止时候的行为,如果父进程
      为SIGCHLD设置了SA_NOCLDWAIT标志位,那么子进程终止退出时,就不会进入僵尸
      状态,而是直接自行了断。但是对Linux而言,子进程仍然会发送SIGCHLD信号,这
      点和上面的SA_NOCLDSTOP略有不同。

   3. SA_ONESHOT和SA_RESETHAND
      这两个标志位本质是一样的,表示信号处理函数是一次性的,信号递送出去以后,信号
      处理函数便恢复成默认值SIG_DFL.

   4. SA_NODEFER和SA_NOMASK
      这两个标志位的作用是一样的,信号处理函数执行期间,不阻塞当前信号。

   5. SA_RESTART
      这个标志位表示,如果系统调用被信号中断,则不返回错误,而是自动重启系统调用。

   6. SA_SIGINFO
      这个标志位表示信号发送者会提供额外的信息。这种情况下,信号处理函数应该为
      三参数的函数。

 当sa_flags含有SA_SIGINFO的时候 ,需要使用带三个参数的处理函数:

void
handler(int sig, siginfo_t *info, void *ucontext)
{
               ...
}

第一个参数 sig 为信号值
第三个参数 ucontext,该结构体提供了进程上下文信息,通常都不会使用到该参数,具体细节
可参考man     sigreturn

第二个参数 info 是一个siginfo_t类型的指针,包含了信号更多的信息。该结构体如下:

siginfo_t {
    int      si_signo;     /* 信号值 */
    int      si_errno;     /* An errno value */

    int      si_code;      /* 信号来源,可以通过该值来判断信号来源
                            * 可选值及含义
                            * SI_USER : 调用kill 或 raise的用户进程
                            * SI_TKILL :调用tkill或tgkill的用户进程
                            * SI_QUEUE : 调用sigqueue的用户进程
                            * SI_MESGQ : 消息到达POSIX消息队列
                            * SI_KERNEL : 内核产生的信号
                            * SI_ASYNCIO : 异步I/O操作完成
                            * SI_TIMER: POSIX定时器到期
                            */
    int      si_trapno;    /* Trap number that caused
                              hardware-generated signal
                              (unused on most architectures) */
    pid_t    si_pid;       /* 信号发送进程ID */
    uid_t    si_uid;       /* 信号发送进程这是用户ID */
    int      si_status;    /* Exit value or signal */
    clock_t  si_utime;     /* User time consumed */
    clock_t  si_stime;     /* System time consumed */
    sigval_t si_value;     /* 使用sigqueue函数发送信号时携带的伴随数据 */
    int      si_int;       /* POSIX.1b signal */
    void    *si_ptr;       /* POSIX.1b signal */
    int      si_overrun;   /* Timer overrun count;
                              POSIX.1b timers */
    int      si_timerid;   /* Timer ID; POSIX.1b timers */
    void    *si_addr;      /* Memory location which caused fault */
    long     si_band;      /* Band event (was int in
                              glibc 2.3.2 and earlier) */
    int      si_fd;        /* File descriptor */
    short    si_addr_lsb;  /* Least significant bit of address
                              (since Linux 2.6.32) */
    void    *si_lower;     /* Lower bound when address violation
                              occurred (since Linux 3.19) */
    void    *si_upper;     /* Upper bound when address violation
                              occurred (since Linux 3.19) */
    int      si_pkey;      /* Protection key on PTE that caused
                              fault (since Linux 4.6) */
    void    *si_call_addr; /* Address of system call instruction
                              (since Linux 3.5) */
    int      si_syscall;   /* Number of attempted system call
                              (since Linux 3.5) */
    unsigned int si_arch;  /* Architecture of attempted system call
                              (since Linux 3.5) */
}
上面的sigval_t结构体定义如下:
union sigval {
    int sival_int;
    void *sival_ptr;
}
通过指定sigqueue函数的第三个参数,可以传递给一个int值或者指针值个目标进程。考虑
到不同的进程有各自独立的地址空间,传递指针到另一个进程几乎没有意义。

参考资料:

1. 《Linux 环境编程,从应用到内核》高峰,李彬著

2.   man signal : https://linux.die.net/man/2/signal

      man sigaction : http://www.man7.org/linux/man-pages/man2/sigaction.2.html

你可能感兴趣的:(Linux,Signal,Linux,信号)