一、信号的分类,常用的几种信号来源?
1.信号的分类
可以从两个不同的分类角度对信号进行分类:①可靠性方面,可分为可靠信号与不可靠信号;②与时间关系上,可分为实时信号与非实时信号
(1)可靠信号与不可靠信号
不可靠信号:
Linux信号机制基本上是从Unix系统中继承过来的。早期Unix系统中的信号机制比较简单和原始,后来在实践中暴露出一些问题,因此,把那些建立在早期机制上的信号叫做"不可靠信号",信号值小于SIGRTMIN(Red hat 7.2中,SIGRTMIN=32,SIGRTMAX=63)的信号都是不可靠信号。这就是"不可靠信号"的来源。
它的主要问题是:
进程每次处理信号后,就将对信号的响应设置为默认动作。在某些情况下,将导致对信号的错误处理;因此,用户如果不希望这样的操作,那么就要在信号处理函数结尾再一次调用signal(),重新安装该信号。
信号可能丢失,后面将对此详细阐述。 如果在进程对某个信号进行处理时,这个信号发生多次,对后到来的这类信号不排队,那么仅传送该信号一次,即发生了信号丢失。
因此,早期unix下的不可靠信号主要指的是进程可能对信号做出错误的反应以及信号可能丢失。
Linux支持不可靠信号,但是对不可靠信号机制做了改进:在调用完信号处理函数后,不必重新调用该信号的安装函数(信号安装函数是在可靠机制上的实现)。因此,Linux下的不可靠信号问题主要指的是信号可能丢失。
可靠信号:
随着时间的发展,实践证明了有必要对信号的原始机制加以改进和扩充。所以,后来出现的各种Unix版本分别在这方面进行了研究,力图实现"可靠信号"。由于原来定义的信号已有许多应用,不好再做改动,最终只好又新增加了一些信号,并在一开始就把它们定义为可靠信号,这些信号支持排队,不会丢失。同时,信号的发送和安装也出现了新版本:信号发送函数sigqueue()及信号安装函数sigaction()。POSIX.4对可靠信号机制做了标准化。但是,POSIX只对可靠信号机制应具有的功能以及信号机制的对外接口做了标准化,对信号机制的实现没有作具体的规定。
信号值位于SIGRTMIN和SIGRTMAX之间的信号都是可靠信号,可靠信号克服了信号可能丢失的问题。Linux在支持新版本的信号安装函数sigation()以及信号发送函数sigqueue()的同时,仍然支持早期的signal()信号安装函数,支持信号发送函数kill()。
(2)实时信号与非实时信号
早期Unix系统只定义了32种信号,Ret hat7.2支持64种信号,编号0-63(SIGRTMIN=31,SIGRTMAX=63),将来可能进一步增加,这需要得到内核的支持。前32种信号已经有了预定义值,每个信号有了确定的用途及含义,并且每种信号都有各自的缺省动作。如按键盘的CTRL ^C时,会产生SIGINT信号,对该信号的默认反应就是进程终止。后32个信号表示实时信号,等同于前面阐述的可靠信号。这保证了发送的多个实时信号都被接收。
Linux信号的编号是从1-64,其中32和33空缺,没有对应的信号。
实时信号是POSIX标准的一部分,可用于应用进程。
非实时信号都不支持排队,都是不可靠信号;实时信号都支持排队,都是可靠信号。
2.常用的信号来源
信号事件的发生有两个来源:硬件来源(比如我们按下了键盘或者其它硬件故障);软件来源,最常用发送信号的系统函数是kill, raise, alarm和setitimer以及sigqueue函数,软件来源还包括一些非法运算等操作。
二、信号的处理流程和处理方式?信号的发送,捕获,处理如何操作?用什么函数实现?
1.信号的处理流程和处理方式
进程可以通过三种方式来处理一个信号:
(1)忽略信号,即对信号不做任何处理,其中,有两个信号不能忽略:SIGKILL及SIGSTOP;
(2)捕捉信号。定义信号处理函数,当信号发生时,执行相应的处理函数;
(3)执行缺省操作,Linux对每种信号都规定了默认操作,详细情况请参考[2]以及其它资料。注意,进程对实时信号的缺省反应是进程终止。
Linux究竟采用上述三种方式的哪一个来响应信号,取决于传递给相应API函数的参数。
2.信号的发送,捕获,处理如何操作,用什么函数来实现
(1)信号的发送
发送信号的主要函数有:kill()、raise()、sigqueue()、alarm()、setitimer()以及abort()。,下面介绍几种常用信号发送函数:
①kill函数
kill函数用于传送信号给指定的进程
相关函数 | Raise,signal |
表头文件 | #include <sys/types.h> #include <signal.h> |
定义函数 | Int kill(pid_t pid,int sig); |
函数说明 | kill函数可以用来传送参数sig指定的信号给参数pid指定的进程,参数pid有以下几种情况。 pid>0 进程ID为pid的进程 pid=0 同一个进程组的进程 pid<0 pid!=-1 进程组ID为-pid的所有进程 pid=-1 除发送进程自身外,所有进程ID大于1的进程 |
返回值 | 执行成功则返回0,如果有错误则返回-1 |
错误代码 | EINVAL:参数sig不合法 ESRCH:参数pid所指定的进程或进程组不存在 EPERM:权限不够,无法传送信号给指定进程 |
②raise函数
函数作用:发送信号给自身
函数原型:int raise(int sig)
头文件:#include <signal.h>
③alarm函数
函数作用:用于设置信号传送闹钟
函数原型:unsigned int alarm(unsigned)
返回值:返回之前闹钟的剩余秒数
④pause函数
函数作用:让进程暂停直到信号出现
函数原型:int pause(void)
返回值:-1
(2)信号的设置
①signal函数
函数作用:设置信号的处理方式
函数原型:void *(signal(int signum, void(* handler)(int)))(int);
返回值:返回先前的信号处理函数指针,如果有误则返回SIG_ERR(-1)
3.函数使用的代码示例
(1)kill、raise函数
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
pid_t pid;
int ret;
if((pid=fork()) < 0)
{
perror("fork");
exit(1);
}
if(pid == 0)
{
raise(SIGSTOP);
exit(0);
}
else
{
printf("pid=%d\n", pid);
if((waitpid(pid, NULL, WNOHANG)) == 0)
{
kill(pid,SIGKILL);
printf("kill %d\n", pid);
}
else
{
perror("kill");
}
}
}
(2)signal函数
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
void my_func(int sign_no)
{
if(sign_no==SIGINT)
printf("I have get SIGINT\n");
else if(sign_no==SIGQUIT)
printf("I have get SIGQUIT\n");
}
int main()
{
printf("Waiting for signal SIGINT or SIGQUIT \n ");
/*注册信号处理函数*/
signal(SIGINT, my_func);
signal(SIGQUIT, my_func);
pause();
exit(0);
}