信号signal,为os传递给proc的一种事件通知。每种信号对应某种系统event。信号传递给proc,proc将会在内核态切换回用户态时捕捉,并执行。具体执行机制为内核态跳转到信号对应的处理函数,信号处理函数执行完毕之后切换回用户态,故而在信号处理函数执行的同时,proc本身的任务将处于休眠状态,不会运行。在用户态,proc不会捕捉处理信号。此处在实际编程中需要注意。
Linux系统中信号分为两类,不可靠信号与可靠信号。信号值小于SIGRTMIN(32)的信号均为不可靠信号,在SIGRTMIN(32)与SIGRTMAX(64)之间的为可靠信号。可靠与不可靠主要针对于信号丢失来说。(很多地方还有一条是信号处理之后将会变成默认,此条现在已经不再有意义,因均不需要重新安装。)可靠信号支持信号队列,不会发生信号丢失,而不可靠信号不支持队列。具体关于这方面的介绍可以google下,查看相关文档即可。
信号编程的步骤主要分为:
1、信号捕捉进程:
信号安装
实现信号处理函数,如果采取的方式为捕捉
2、信号发送进程:
信号发送
应用程序对于信号的处理主要分为三种模式:忽略,默认,捕捉。
信号的发送函数:
kill()
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid,int signo)
备注:pid>0 进程为pid的proc
pid=0 同属一个pg的proc
pid=-1 除发送进程以外的所有进程id大于1的进程
pid<0 && pid!=-1 pgid为pid的所有proc
signo为信号值,当其取值为0时,不发送信号,仅做例行检查。主要检查目标进程是否存在以及当前进程是否具有向目标进程发送信号的权限
调用成功时返回0,否则返回-1
raise()
#include <signal.h>
int raise(int signo)
向当前进程发送信号,调用成功返回0,否则返回-1。
sigqueue()
#include <sys/types.h>
#include <signal.h>
int sigqueue(pid_t pid, int sig, const union sigval val)
typedef union sigval {
int sival_int;
void *sival_ptr
}sigval_t;
该函数为新增的信号安装函数。其相对kill而言,多了一个参数val,用于传递附加信息给信号处理函数。其缺点在于仅能对一个进程发送信号,如果sig为零,其工作机制与kill一致,均为例行检查。
alarm()
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
专门为SIGALARM信号而设定的定时器,即在指定seconds之后触发,向当前进程本身发送SIGALARM信号。进程调用该方法之后,直接覆盖之前的调用,即多次调用仅有最后一次有效,返回值与之前是否调用过该方法有关,如果之前设定过闹钟时间,则返回之前的闹钟时间的剩余时间,否则,返回0。闹钟时间触发处理之后,该设定将会被自动取消,故而当需要多次调用时,需要处理完毕之后重新设定闹钟时间。该函数目前已经很少使用。
setitimer()
#include <sys/time.h>
int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue));
struct itimerval {
struct timeval it_interval; /* next value */
struct timeval it_value; /* current value */
};
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
};
功能与alarm类似,但相对强大很多。其工作机制为value自动递减其成员it_value,至零,触发信号SIGALARM,然后用it_interval填充it_value继续周期性执行。当it_interval与it_value均为零时,则定时器取消。故而如果设定一次性定时器,仅需设定其中it_value取值,而将it_interval设置为零,如果需要设置周期性定时器,则必须将it_interval设定值。
其参数which的取值有三种:ITIMER_REAL 设定绝对时间,对应信号SIGALARM, ITIMER_VIRTUAL 程序执行时间,对应信号SIGVALARM,ITIMER_PROF 进程执行以及内核因本进程消耗的时间和,对应信号SIGVALARM。
abort()
#include <stdlib.h>
void abort(void);
向进程本身发送SIGABORT信号。进程调用其对应的处理函数之后仍能接受SIGABORT信号。
信号安装:
signal()
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
参数signum为信号值,handler为处理函数句柄。如果忽略,使用SIG_IGN,如果默认则使用SIG_DFL,如果捕捉,则此处需要使用自定义的处理函数的入口地址。调用成功返回处理函数句柄,否则返回SIG_ERR。
sigaction()
#include <signal.h>
int sigaction(int signum,const struct sigaction *act,struct sigaction *oldact));
struct sigaction {
union{
__sighandler_t _sa_handler;
void (*_sa_sigaction)(int,struct siginfo *, void *);
}_u
sigset_t sa_mask;
unsigned long sa_flags;
void (*sa_restorer)(void);
}
signum对应需要安装的信号,act的成员sa_handler与_sa_sigaction对应处理函数的入口地址。其中sa_handler与_sa_sigaction仅可指定一个。其中_sa_handler仅存在一个参数,为信号值。如果使用函数_sa_sigaction,其存在三个参数,第三个参数无效。第一个参数为信号值,第二个参数为一个结构体。其结构如下:
struct siginfo_t {
int si_signo; /* Signal number */
int si_errno; /* An errno value */
int si_code; /* Signal code */
int si_trapno; /* Trap number that caused
hardware-generated signal
(unused on most architectures) */
pid_t si_pid; /* Sending process ID */
uid_t si_uid; /* Real user ID of sending process */
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; /* Signal value */
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 */
int si_band; /* Band event */
int si_fd; /* File descriptor */
}
其中void *si_ptr; 为4字节,对应之前信号发送函数sigqueue的第三个参数。此项在实际应用中具有很好的扩展性。
sa_mask标识在该信号处理函数执行过程中,那些信号将被屏蔽。缺省情况下为当前信号本身被屏蔽,以防止信号的嵌套发送。信号集处理函数包括:
#include <signal.h>
int sigemptyset(sigset_t *set);//初始化由set指定的信号集,信号集里面的所有信号被清空;
int sigfillset(sigset_t *set);//调用该函数后,set指向的信号集中将包含linux支持的64种信号;
int sigaddset(sigset_t *set, int signum);//在set指向的信号集中加入signum信号;
int sigdelset(sigset_t *set, int signum);//在set指向的信号集中删除signum信号;
int sigismember(const sigset_t *set, int signum);)//判定信号signum是否在set指向的信号集中。
sa_flags中包含了许多标志位,包括刚刚提到的SA_NODEFER及SA_NOMASK标志位。另一个比较重要的标志位是SA_SIGINFO,当设定了该标志位时,表示信号附带的参数可以被传递到信号处理函数中,因此,应该为sigaction结构中的sa_sigaction指定处理函数,而不应该为sa_handler指定信号处理函数,否则,设置该标志变得毫无意义。即使为sa_sigaction指定了信号处理函数,如果不设置SA_SIGINFO,信号处理函数同样不能得到信号传递过来的数据,在信号处理函数中对这些信息的访问都将导致段错误。
进程相关信号操作函数
//参数how的说明:
//SIG_BLOCK 在进程当前阻塞信号集中添加set指向信号集中的信号
//SIG_UNBLOCK 如果进程阻塞信号集中包含set指向信号集中的信号,则解除对该信号的阻塞
//SIG_SETMASK 更新进程阻塞信号集为set指向的信号集
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset));
int sigpending(sigset_t *set));//获得当前已递送到进程,却被阻塞的所有信号,在set指向的信号集中返回结果。
int sigsuspend(const sigset_t *mask));//用于在接收到某个信号之前, 临时用mask替换进程的信号掩码, 并暂停进程执行,直到收到信号为止。sigsuspend 返回后将恢复调用之前的信号掩码。信号处理函数完成后,进程将继续执行。该系统调用始终返回-1,并将errno设置为EINTR。
void pause(void);进程阻塞等待,直到有信号唤醒。