信号是一种软中断,它为linux提供了一种处理异步事件的方法。linux支持POSIX标准信号与实时信号,通过kill -l命令可以查看linux支持的信号,以及信号名字和序号。
kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL
5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE
9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2
13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGSTKFLT
17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU
25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH
29) SIGIO 30) SIGPWR 31) SIGSYS 34) SIGRTMIN
35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3 38) SIGRTMIN+4
39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12
47) SIGRTMIN+13 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14
51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10
55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7 58) SIGRTMAX-6
59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
1–31是POSIX标准信号,32/33 glibc POSIX线程实现内部使用, 34–64是实时信号。
有很多种情况会产生信号:
1.终端按键触发信号: 当某个进程正在执行时,用户在终端键盘上按下某些键时,此时会产生一个硬件中断。此时终端驱动程序会将其解释为某一种信号,操作系统该信号发送个正在前台运行的进程。
2.硬件异常产生信号:如当前进程执行了除0操作,CPU的运算单元会产生异常,内核会将该异常解释为SIGFPE信号发送给进程,进程采取相应的处理动作;再比如当前进程非法访问内存时,MMU会产生异常,内核将其解释为SIGSEGV信号将其发送给进程
3.命令和系统调用接口产生信号:如调用系统调用kill向一个进程发送信号。
4.软件事件产生信号:如alarm闹钟超时,产生SIGALRM信号;子进程退出向父进程发送SIGCHLD信号。
当一个信号出现时,有三种方式进行处理:
每个信号的默认行为都是下面中的一个:
Term Default action is to terminate the process.
Ign Default action is to ignore the signal.
Core Default action is to terminate the process and dump core
Stop Default action is to stop the process.
Cont Default action is to continue the process if it is currently stopped.
信号的描述以及对应的默认行为如下表如示:
Signal Action Comment
────────────────────────────────────────────────────────────────────────
SIGHUP Term Hangup detected on controlling terminal or death of controlling process
SIGINT Term Interrupt from keyboard
SIGQUIT Core Quit from keyboard
SIGILL Core Illegal Instruction
SIGTRAP Core Trace/breakpoint trap
SIGABRT Core Abort signal from abort
SIGBUS Core Bus error (bad memory access)
SIGFPE Core Floating-point exception
SIGKILL Term Kill signal
SIGUSR1 Term User-defined signal 1
SIGSEGV Core Invalid memory reference
SIGUSR2 Term User-defined signal 2
SIGPIPE Term Broken pipe: write to pipe with no readers
SIGALRM Term Timer signal from alarm
SIGTERM Term Termination signal
SIGSTKFLT Term Stack fault on coprocessor
SIGCHLD Ign Child stopped or terminated
SIGCONT Cont Continue if stopped
SIGSTOP Stop Stop process
SIGTSTP Stop Stop typed at terminal
SIGTTIN Stop Terminal input for background process
SIGTTOU Stop Terminal output for background process
SIGURG Ign Urgent condition on socket
SIGXCPU Core CPU time limit exceeded
SIGXFSZ Core File size limit exceeded
SIGVTALRM Term Virtual alarm clock
SIGPROF Term Profiling timer expired
SIGWINCH Ign Window resize signal
SIGIO Term I/O now possible
SIGPWR Term Power failure
SIGSYS Core Bad system call
realtime signal Term Application-defined
1.当造成信号的事件发生时,为进程产生一个信号。信号可以是硬件异常、软件条件、终端产生的信号或调用kill发送信号。
2.当对信号采取了动作,称向进程递送了一个信号
3.在信号产生和递送之间的时间间隔内,称信号是未决的(pending).
4.信号可以被阻塞,在解除阻塞之前,信号不会被递送给进程。内核在递送一个被阻塞的信号时(不是在产生信号时), 才决定对它的处理方式。
5.实时信号支持排队,即在信号被阻塞时,发送给进程的多个信号实例被放入队列,当解除阻塞时,队列中的多个信号实例被递送给进程;而标准信号不支持排队,只有一个信号实例被递送给进程,从而导致信号丢失。
信号集表示多个信号的集合,用sigset_t表示,如下为处理信号集的函数:
#include
int sigemptyset(sigset_t *set);//初始化信号集,清除其中所有的信号
int sigfillset(sigset_t *set);//初始化信号集,使其包含所有的信号
int sigaddset(sigset_t *set, int signum);//将一信号添加到信号集
int sigdelset(sigset_t *set, int signum);//从信号集中删除一个信号
int sigismember(const sigset_t *set, int signum);//检查信号是否在信号集中
#include
int kill(pid_t pid, int sig);
发送一个信号到进程或者进程组。
pid
如果pid>0,则表示发送信号到进程pid; 如果pid=0,表示将信号发送到当前进程所在的进程组。如果pid=-1, 表示将信号发送到除了1(init)进程外的所有进程,这些进程必须满足权限要求,即当前进程有权限发送信号到这些进程。
sig
表示信号数值编号,如果sig=0,则不会发送信号,但会对pid与权限进行检查。 这常用来测试某个进程是否存在或当前进程是否有权限发送信号到pid.
如果成功(至少有一个信号被成功发送),则返回0; 如果失败,返回-1, 并设置errno;
errno可能的值:
EINVAL 指定了非法的信号值.
EPERM 当前进程没有权限发送信号到任一目标进程
ESRCH 目标进程或进程组不存在。
注意事项
1. 当调用kill(-1,sig)时,如果进程1(init)安装了sig的处理函数,即捕捉信号sig,sig会被发送到init进程。
2. linux允许进程给自己发送信号,但是kill(-1,sig)不会发生信号给当前进程。
3. 当一个进程发送信号给自己,如果发送线程没有阻塞这个信号,并且没有其它线程在调用sigwai等待这个信号或者解除阻塞该信号,则在kill返回前,至少有一个未阻塞的信号被递送到发送线程。
#include
int sigqueue(pid_t pid, int sig, const union sigval value);
将信号sig及其携带的信息value放入进程队列.
value是一个联合体结构,表示信号可以携带一个Int型或一个指针型数据,定义如下:
union sigval {
int sival_int;
void *sival_ptr;
};
如果进程调用sigaction函数设置了信号的处理函数,并且其struct sigaction参数的标志位设置了SA_SIGINFO,那么进程接收到信号时就可以从信号处理函数的参数中获取信号携带的信息。
如果成功,函数返回0,表明信号被成功放入接收进程队列; 如果失败,返回-1,并设置errno,errno的可能值如下:
EAGAIN 进程队列中的信号数超出限制.
EINVAL 信号值不合法.
EPERM 当前进程没有权限向接收进程发送信号
ESRCH 进程pid不存在.
#include
int sigaction(int signum, const struct sigaction *restrict act,struct sigaction *restrict oldact);
检测或改变进程对一个信号的动作。
signum 指定一个信号,可以是除了SIGKILL和SIGSTOP的任意系统支持的信号.
act是struct 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_mask 指定在执行信号处理函数期间要阻塞的信号。 触发调用当前处理函数的信号,在函数执行期间默认被阻塞。
sa_flags 信号标志位, 这些标志位可以影响信号的行为。常用的有如下几个:
SA_RESTART: 表示被信号中断的系统调用自动重启。
SA_SIGINFO: 表示信号处理函数有三个参数,即应该设置sa_sigaction指针。
SA_NODEFER: 表示信号处理函数执行时,不阻塞触发当前调用的信号。
SA_ONSTACK: 表示在signalstack函数设置的信号栈上调用信号处理函数。如果此信号栈不可用,则使用默认栈。
SA_RESETHAND: 表示执行信号处理函数时,将信号的行为恢复为默认。
sa_handler/sa_sigaction 信号处理函数指针,通常只需指定一个。 当SA_SIGINFO 标志位设置时,需设置sa_sigaction,也就是说当信号递送到进程后会调用这个函数; 否则设置sa_handler。
sa_handler有三种可能值:
SIG_DFL --默认行为
SIG_INL --忽略信号
指向信号处理函数的指针 --这个函数只有一个表示信号编号的参数。
sa_sigaction所指向的信号处理函数有三个参数,除了信号编号,还有信号的相关信息以及信号的上下文信息。信号处理函数的原型如下:
void handler(int sig, siginfo_t *info, void *ucontext)
sig 表示触发此调用的信号
siginfo_t 这个结构有很多成员,但对一个特定的信号,只有部分成员有意义。其中si_signo/si_errno/si_code每个信号都会定义,其它字段是联合体结构,对每一个信号的有不同的意义。
ucontext 表示信号上下文信息。
如果act不为空,则为信号设置act指向的信息; 如果oldact不为空,则信号之前的信与被保存在oldact.
如果成功返回0; 失败返回-1,并设置errno,其可能值:
EFAULT act或oldact指向的地址无效。
EINVAL 指定了非法的信号。当指定SIGKILL/SIGSTOP时也会出现这个错误。
#include
int raise(int sig);
发送信号sig到调用进程, 如果信号的处理函数被调用,则函数在信号处理函数返回后再返回。成功返回0, 失败返回非0值。这个函数相当于:kill(getpid(), sig);
#include
noreturn void abort(void);
导致进程异常终止: 这个函数首先解除阻塞信号SIGABRT,然后发送SIGABRT到调用进程。
#include
int sigwait(const sigset_t *restrict set, int *restrict sig);
int sigwaitinfo(const sigset_t *restrict set,siginfo_t *restrict info);
int sigtimedwait(const sigset_t *restrict set,siginfo_t *restrict info,const struct timespec *restrict timeout);
sigwai用于等待一个信号。它会中止当前进程的执行,直到set指定的信号集中的某个信号变成pending(未决的)。 函数接受这个信号(从pending列表中移除),并返回信号编号。
sigwaitinfo与sigwait相比, 除了信号编号,还会返回siginfo_t结构信息。
sigtimedwait比sigwaitinfo多一个时间参数,用于指示等待的时间
#include
int sigprocmask( int how, const sigset_t *restrict set, sigset_t *restrict oset );
每一个进程都有一个信号屏蔽字,它规定了当前进程要阻塞的信号集。函数sigprocmask用于检测或更改进程信号屏蔽字。
how 表示更改当前屏蔽字的方法,取值及其说明如下:
SIG_BLOCK 该进程新的信号屏蔽字是其当前信号屏蔽字和set指向信号集的并集。set包含了我们希望阻塞的附加信号
SIG_UNBLOCK 该进程新的信号屏蔽字是其当前信号屏蔽字和set所指向信号集补集的交集。set包含了希望解除阻塞的信号
SIG_SETMASK 该进程新的信号屏蔽字将被set指向的信号集的值代替
当set为空指针时,则不改变进程的信号屏蔽字,how的值无意义。
当oset为非空指针时,那么进程当前信号屏蔽字通过oset返回。
在调用sigprocmask后如果有任何未决的、不再阻塞的信号,则在sigprocmask返回前,至少将其中之一递送给该进程。
#include
int sigpending(sigset_t *set);
返回当前进程阻塞的、未决的信号集。