Linux信号是一种进程间异步的通信机制,在实现上是一种软中断,信号可以导致一个正在运行的进程被中断,进而处理一个突发事件。Linux在/usr/include/asm-generic/signal.h中详细定义了信号的信号值,其内容如下:
1、信号的基本概念
#define SIGHUP 1 //连接断开
#define SIGINT 2 //终端中断符
#define SIGQUIT 3 //终端退出符
#define SIGILL 4 //非法硬件指令
#define SIGTRAP 5 //硬件故障
#define SIGABRT 6 //异常终止
#define SIGIOT 6 //硬件故障
#define SIGBUS 7 //硬件故障
#define SIGFPE 8 //算术异常
#define SIGKILL 9 //终止
#define SIGUSR1 10 //紧急情况
#define SIGSEGV 11 //无效存储访问
#define SIGUSR2 12 //用户自定义信号
#define SIGPIPE 13 //写至无读进程的管道
#define SIGALRM 14 //超时
#define SIGTERM 15 //终止进程
#define SIGSTKFLT 16
#define SIGCHLD 17 //子进程改变工作状态
#define SIGCONT 18 //使暂定状态的进程继续工作
#define SIGSTOP 19 //使进程暂停工作
#define SIGTSTP 20 //终端挂起符作业
#define SIGTTIN 21 //后台从控制tty读作业
#define SIGTTOU 22 //后台从控制tty写作业
#define SIGURG 23 //紧急情况
#define SIGXCPU 24 //超过CPU限制
#define SIGXFSZ 25 //超出文件长度限制
#define SIGVTALRM 26 //虚拟时间闹钟
#define SIGPROF 27 //时间超时
#define SIGWINCH 28 //终端窗口大小改变
#define SIGIO 29 //异步I/O
#define SIGPOLL SIGIO**
/*
#define SIGLOST 29
*/
#define SIGPWR 30 //电源失效/再启动
#define SIGSYS 31 //无效系统调用
#define SIGUNUSED 31
/* These should not be considered constants from userland. */
#define SIGRTMIN 32
#ifndef SIGRTMAX
#define SIGRTMAX _NSIG
#endif
下面介绍几中常见信号处理方式:
(1)SIGCHLD在子进程退出时,将发送该信号给父进程,父进程可根据该信号完成对子进程PCB资源的回收。
(2)SIGSTOP、SIGKILL不能被屏蔽、安装。
(3)SIGSTOP和SIGCOUNT是配对的,一个进程收到SIGSTOP后会暂停执行,并屏蔽除SIGKILL外所有信号,在收到SIGCOUNT后,才会继续执行
(4)信号可以唤醒处于sleep()的进程
信号的基本术语:
- 发送信号:产生信号,并将信号发送出去。
- 安装中断:设置信号到来时,不是执行默认操作,而是执行自己自定义的代码。
- 递送信号:一个信号被操作系统发送到目标进程。
- 捕获信号:被递送的信号在目标进程中引起某段代码的执行。
- 屏蔽信号:进程告诉操作系统暂时不接收某些信号。
- 忽略信号:进程被递送到目标进程,但目标进程不做任何操作,直接丢弃
- 未决信号:信号已经产生,但目标进程暂时屏蔽了该信号,从而导致不能被目标进程捕获的信号。
- 可靠信号:信号值大于32的为信号
- 不可靠信号:信号值小于32的信号
2、信号的产生
(1)、使用kill()函数向指定进程发送一个信号,函数声明如下:
//from /usr/include/asm-generic/signal.h
int kill(pid_t pid,int sig);
可以使用kill(pid_t pid,0)来检测指定进程是否存在。成功返回0,失败返回-1。
(2)、 使用raise()函数给当前进程发送一个信号,函数声明如下:
//from /usr/include/asm-generic/signal.h
int raise(int sig);
函数执行成功,返回0,失败返回-1。
(3)、使用alarm()函数可以持续的按照一定间隔发送一个SIGALARM信号,并不会信产生信号给当前进程,函数声明如下:
//from /usr/include/asm-generic/signal.h
unsigned int alarm(unsigned int seconds);
函数使用时,若seconds值为0,则表示取消先前发出的信号。
函数第一次调用时,执行成功返回0,失败返回-1。
若之前已有调用,再次调用alarm()则表示重置时间间隔,执行成功则返回距上次信号发送,剩余间隔时间。
另外需要强调的是,子进程并不会继承父进程的alarm信号,但在调用exec()函数执行新代码时,原来的设置的信号仍然有效。
(4)、 使用ualarm()持续产生SIGALARM信号,函数声明如下:
//from /usr/include/asm-generic/signal.h
__useconds_t ualarm(__useconds_t value,__useconds_t interval);
函数将在value 微妙内产生SIGALARM信号,并在之后每interval微妙时,再次产生该信号。
3、安装信号
(1)、 signal()安装信号,函数声明如下:
//from /usr/include/asm-generic/signal.h
__sighandler_t signal(int sig,__sighandler_t handler);
函数第1参数为收到的信号,第2个参数为接收此信号后的处理代码入口或以下几个宏:
#define SIG_ERR ((__sighandler_t ) -1) //返回错误
#define SIG_DEF ((__sighandler_t ) 0) //执行信号默认参数
#define SIG_IGN ((__sighandler_t ) 1) //忽略信号
2、sigaction()安装信号
sigaction()函数相对于signal()函数来说,更为安全可靠,获取的信息更多,函数声明如下:
//from /usr/include/asm-generic/signal.h
struct sigaction
{
union
{
/*SIGDFL,SIG_IGN,类似于signal*/
__sighandler_t sa_handler;
/*当sa_flags为SA_SIGINFO时,使用此变量设置信号处理函数,并将获取信号发送时的信息保存在结构体info中,包括信号发送进程ID*/
void (*sa_sigaction)(int sig,struct siginfo_t* info,void*);
}
sigset_t sa_mask; //屏蔽的信号集
unsigned long sa_flags; //特殊标志
...
};
int sigaction(int sig,struct sigaction* act,struct sigaction* oact);
函数的第1个参数为信号值,第2个参数为欲设置的信号处理方式,第3个参数为原先的信号处理方式信息。
另外sa_flags可以设置为以下值:
- SA_NOCLDSTOP:表示子进程在退出时,不生成SIGCHLD信号
- SA_RESTART:将指定的可中断函数将失败
- SA_RESETHAND:信号的处理方法将被重置为默认处理方式,即SIG_DFL
4、信号集处理
函数sigprocmask()用来设置当前进程屏蔽的信号集合,函数声明如下:
int sigprocmask(int how,sigset_t* set,sigset_t* oset);
函数的第1个参数为更改信号集的方式,可以使用以下几个值:
#define SIG_BLOCK 0 //第2个参数代表的信号集添加到当前进程屏蔽的信号集中
#define SIG_UNBLOCK 1 //第2个参数代表的信号集从当前进程屏蔽的信号集中删除
#define SIG_SETMASK 2 //设置第2个参数代表的信号集为当前进程屏蔽的信号集
函数执行成功返回0,否则返回-1
除了sigprocmask()函数,还有其他方式,例如:
/*清空set代表的信号集*/
int sigemptyset(sigset_t* set)
/*添加信号sig到信号集set中*/
int sigaddset(sigset_t* set,int sig);
/*从信号集set中删除信号sig*/
int sigdelset(sigset_t* set,int sig);
/*检测信号sig是否在信号集set中*/
int sigismember(sigset_t* set,int sig);
/*检测信号集set是否为空信号集*/
int sigisemptyset(sigset_t* set);
/*将信号集left与信号集right按照逻辑与的方式合并到信号集set中*/
int sigandset(sigset_t* set,sigset_t* left,sigset_t* right);
/*将信号集left与信号集right按照逻辑或的方式合并到信号集set中*/
int sigandset(sigset_t* set,sigset_t* left,sigset_t* right);