linux的信号详解
信号概念
定义:
信号是事件发生时对进程的通知机制。
信号产生场景:
- 键盘事件
- 非法内存操作
- 硬件故障
- 从用户态切换到内核态
信号分类:
-
标准信号
标准信号的局限性:阻塞信号可能会丢失。当一个信号阻塞时,这个信号即使多次发送给进程,也被执行一次信号句柄。
信号交付没有携带与信号有关信息。接受到信号的进程无法区分同种信号的不同情况,也不知道信号从何而来。
信号的交付没有优先级。当有多个信号悬挂与一个进程时,交付的顺序不确定。
-
实时信号
实时信号对标准信号做了一下扩充,有以下的特点:增加了信号从SIGRTMIN到SIGRTMAX的实时信号,可以通过sysconf(_SC_RTSIG_MAX)获得当前操作系统支持的实时信号的个数。
实时信号在队列中并按顺序交付。同一类型的实时信号将按顺序交付给进程。
实时信号可以携带额外的信息。
进程能够通过专门的函数更快的回复信号。
当定时器到期、空消息队列有消息到达、有异步IO完成时,信号能够及时交付给进程。
进程收到信号的三种处理方式:
1.默认处理:
- 忽略信号:内核将信号丢弃,信号没有对进程产生任何影响
- 终止进程:进程异常终止
- 产生核心转储文件,同时终止文件
- 停止进程:暂停进程的执行
- 恢复之前暂停的进程继续执行
2.忽略处理:
- 信号来了不做任何处理
不能忽略SIGKILL和SIGSTOP
3.捕获并处理:
- 信号来了捕获信号,并执行程序员自己写的程序
不能捕获SIGKILL和SIGSTOP
信号列表:
列表中,编号为1 ~ 31的信号为传统UNIX支持的信号,是不可靠信号(非实时的),编号为32 ~ 63的信号是后来扩充的,称做可靠信号(实时信号)。不可靠信号和可靠信号的区别在于前者不支持排队,可能会造成信号丢失,而后者不会。
系统信号详解:
- SIGHUP
当终端断开时,将发送该信号给终端控制进程。SIGHUP信号还可用于守护进程。 - SIGINT
当用户键入终端中断字符(如:Ctrl + C)终端驱动程序将发送该信号给前台进程组。该信号默认行为是终止进程。 - SIGQUIT
当用户在键盘键入退出字符(如Ctrl+\)时,该信号将发往前台进程组。默认情况下,该信号终止进程,并生成可用于调试的核心转储文件。当进程陷入无限循环或者不在响应,使用该信号很合适。 - SIGILL
进程试图非法执行机器语言指令,系统将向该进程发送该信号。 - SIGTRAP
该信号用来实现断点调试功能以及strace命令所执行的跟踪系统调用功能。 - SIGABRT
当进程调用abort函数时,系统向该进程发送该信号。默认情况下,该信号会终止进程,并产生核心转储文件。 - SIGBUS
总线错误,表示发生了某种内存访问错误。当使用mmap()所创建的内存映射时,如果试图访问的地址超出了底层内存映射文件的结尾,会产生该错误。 - SIGFPE
在发生致命的算术运算错误时发出. 不仅包括浮点运算错误, 还包括溢出及除数为0等其它所有的算术的错误。 - SIGKILL
此信号为必杀信号,处理器程序无法阻塞、忽略或者捕获,故而总能杀死进程(僵尸进程除外)。 - SIGUSR1
用户自定义信号,内核绝不会为进程产生该信号。 - SIGSEGV
试图访问未分配给自己的内存, 或试图往没有写权限的内存地址写数据. - SIGUSR2
同SIGUSR1描述。 - SIGPIPE
当某一进程试图向管道,FIFO或套接字写入信息时,如果这些设备并无相应的阅读进程,系统将产生该信号。(管道破裂) - SIGALRM
经调用alarm()或setitimer()而设置的实时定时器一旦到期,内核将产生该信号。 - SIGTERM
这是用来终止进程的标准信号,也是kill和killall命令所发送的默认信号。用户经常会使用kil -9显示向进程发送SIGKILL信号,然而这一做法通常是错误的。精心设计的应用程序应当为SIGTERM信号设置处理器程序,以便于其能够预先清理临时文件和释放资源,做到全身而退。发送SIGKILL信号可以杀掉某个进程,从而绕开了SIGTERM的信号处理程序。因此,总是应该首先尝试使用SIGTERM信号来终止进程,而把SIGKILL信号作为最后手段,去对付那些失控的进程。 - SIGSTKFLT
linux对该信号做了定义,但并未加以使用。 - SIGCHLD
当父进程的某一子进程退出时,内核将向父进程发送该信号。 - SIGCONT
该信号发送给已停止的进程,进程将恢复运行。当接收信号的进程当前处于非停止状态 时,默认情况下将忽略该信号。 - SIFSTOP
进程收到该信号将停止运行,处理器程序无法将其阻塞、忽略或者捕获,故而总能停止进程。 - SIGTSTP
作业控制的停止信号,当用户在键盘输入挂起字符(如:Ctrl+Z)时,将发送该信号给前台进程组,使其停止运行。(该信号可以被处理和忽略) - SIGTTIN
在作业控制shell下运行时,若后台进程组试图对终端进行read()操作,终端驱动程序则将该进程组发送该信号。该信号默认将停止进程。 - SIGTTOU
该信号与SIGTTIN类似,但在写终端(或修改终端模式)时收到。 - SIGURG
系统发送该信号给一个进程,表示套接字上存在带外(紧急)数据。 - SIGXCPU
当进程的CPU时间超出对应的资源限制时,将发送此信号给进程。 - SIGXFSZ
如果进程试图增大文件而突破对进程文件大小的资源限制时,将发送该信号给进程。 - SIGVTALRM
虚拟时钟信号. 类似于SIGALRM, 但是计算的是该进程占用的CPU时间。 - SIGPROF
类似于SIGALRM/SIGVTALRM, 但包括该进程用的CPU时间以及系统调用的时间. - SIGWINCH
窗口大小改变时发出该信号。 - SIGIO
文件描述符准备就绪, 可以开始进行输入/输出操作。 - SIGPWR
电源故障信号。 - SIGSYS
如果进程发起的系统调用有误,将产生该信号。
操作信号
使用命令发送信号给进程
kill -信号 进程id
kill命令是使用kill函数实现的。
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
pid:指定哪个进程
- pid>0:会发送信号给指定进程号为pid的进程。
- pid=0:发送信号给调用进程组同组的每个进程,包括调用进程本身。
- pid=-1:调用进程有权将信号发往的每一个进程,除去init(1号进程)和调用进程自身。如果特权级进程发起这一调用,那么会将信号发给系统中所有进程。有时也称这种信号称为广播信号。
- pid<-1:会向该pid绝对值的进程组下属进程发送信号。
sig:发送哪个信号
kill还有一个特殊的用途:当sig=0时,表示无信号发送,调用kill()函数会去执行错误检查,查看是否可以向目标进程发送信号。这也就意味着可以指定空信号来检测ID进程是否存在。
给本进程发信号:
#include <signal.h>
int raise(int sig);
调改变信号处置:
系统调用函数
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
- 参数:
- signum:表示希望修改的信号编号
- handler:表示信号到达时执行的处理器程序的地址,该函数无返回值。
1. 自定义函数
2.SIG_DFL:将信号处置重置为默认值。这适用于将之前signal()调用所改变的信号处置还原。
3.SIG_IGN:忽略该信号。
handler一般格式:
void handler(int sig)
{
//处理程序
}
- 返回值:
- 成功:signal()的返回值是之前的信号处置,像handler参数一样,是一个指针,指向的是带有一个整形参数且无返回值的函数。
- 失败:返回SIG_ERR
信号处理器程序介绍
信号处理器程序时当指定信号传递给进程时将会调用的一个函数。
调用信号处理程序,可能会随时打断主程序流程,内核代表进程来调用处理器程序,当处理器程序返回时,主程序会在打断的位置处恢复执行。
信号处理器程序设计原则:力求简单。
示例代码:
void handler(int sig) //处理器程序
{
printf("ctrl + c\n");
}
int main()
{
int sig = SIGINT; //信号
if(signal(sig,handler) == SIG_ERR)
perror("signal"),exit(1);
int i = 0;
while(1)
{
printf("%c ",i%26+'a');
fflush(stdout);
i++;
sleep(1);
}
return 0;
}
执行结果:
当键入Ctrl+C时,会去执行信号处理程序,进程不会结束,执行完毕再返回继续执行主程序,最后发送SIGQUIT(Ctrl + \)信号,默认执行核心转储,进程退出。
上一篇: 信号的捕捉以及SIGCHLD信号
下一篇: Linux----详解信号