LINUX信号

信号概述

信号是一种软中断,它为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信号。

信号的处理

当一个信号出现时,有三种方式进行处理:

  1. 忽略此信号。大多数信号都可使用这种方式进行处理,但SIGKILL/SIGSTOP不能被忽略。
  2. 捕捉信号。信号发生时,调用一个用户定义的函数。可以通过sigaction函数设置。
  3. 执行默认动作。每一个信号都有一个默认动作,大多数信号的默认动作是终止进程。

每个信号的默认行为都是下面中的一个:

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);//检查信号是否在信号集中

信号处理相关函数

kill函数

   #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返回前,至少有一个未阻塞的信号被递送到发送线程。

sigqueue函数

#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不存在.

sigaction

#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时也会出现这个错误。

raise

#include 
int raise(int sig);

发送信号sig到调用进程, 如果信号的处理函数被调用,则函数在信号处理函数返回后再返回。成功返回0, 失败返回非0值。这个函数相当于:kill(getpid(), sig);

abort

#include 
noreturn void abort(void);

导致进程异常终止: 这个函数首先解除阻塞信号SIGABRT,然后发送SIGABRT到调用进程。

sigwait

#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多一个时间参数,用于指示等待的时间

sigprocmask

#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返回前,至少将其中之一递送给该进程。

sigpending

#include 

int sigpending(sigset_t *set);

返回当前进程阻塞的、未决的信号集。

你可能感兴趣的:(#,linux网络编程,linux)