Linux下信号涉及知识点非常多,本章节先做一个铺垫,简单介绍一下信号种类及其含义,信号的几种默认处理方式等,后续再来介绍具体的信号处理函数以及注意事项也就方便很多了。工欲善其事,必先利其器。Let us get it!
信号
信号,通俗来讲就是给予一种提示,告知一方发生了啥。Linux下的信号指的是操作系统给进程发送信号,告知进程在合适的时间内去处理所接受的信号。其本质给发送信号的进程内的PCB中写入数据,修改相应的PCB字段,内核在返回处理时候得知信号记录再予以操作。
如下模拟场景:
用户输入一个命令,在shell下启动一个前台进程;
用户按下Ctrl+c,通过键盘输入产生一个硬件中断;
如果CPU当前正在运行此进程的代码,则该进程的用户空间的代码将暂停执行,CPU从用户态切换至内核态处理中断;
终端驱动程序将Ctrl+c解释为一个SIGINT信号,记在该进程的PCB中;
当某个时刻从内核返回至该进程的用户空间代码继续执行之前,首先处理PCB中记录的信号;SIGINT信号的默认处理动作为终止信号,所以直接终止进程而不再返回到它的用户空间代码;
信号种类
信号可以按照可靠性和时间关系划分,如下表:
划分 | Item1 | Item2 |
---|---|---|
可靠性 | 可靠信号 | 不可靠信号 |
时间关系 | 普通信号 | 实时信号 |
可靠信号
信号值位于SIGRTMIN和SIGRTMAX之间的信号都是可靠信号,信号的可靠与不可靠只与信号值有关,与信号的发送及安装函数无关。
实时信号
通过命令查看信号种类
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 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 33) SIGRTMIN 34) SIGRTMIN+1
35) SIGRTMIN+2 36) SIGRTMIN+3 37) SIGRTMIN+4 38) SIGRTMIN+5
39) SIGRTMIN+6 40) SIGRTMIN+7 41) SIGRTMIN+8 42) SIGRTMIN+9
43) SIGRTMIN+10 44) SIGRTMIN+11 45) SIGRTMIN+12 46) SIGRTMIN+13
47) SIGRTMIN+14 48) SIGRTMIN+15 49) SIGRTMAX-14 50) SIGRTMAX-13
51) SIGRTMAX-12 52) SIGRTMAX-11 53) SIGRTMAX-10 54) SIGRTMAX-9
55) SIGRTMAX-8 56) SIGRTMAX-7 57) SIGRTMAX-6 58) SIGRTMAX-5
59) SIGRTMAX-4 60) SIGRTMAX-3 61) SIGRTMAX-2 62) SIGRTMAX-1
63) SIGRTMAX
前32种信号已经有了预定义值,每个信号有了确定的用途及含义,并且每种信号都有各自的缺省动作,为普通信号。后32个信号表示实时信号,等同于前面阐述的可靠信号。这保证了发送的多个实时信号都被接收。
非实时信号都不支持排队,都是不可靠信号;实时信号都支持排队,都是可靠信号。
信号产生
产生来源
信号事件的发生有两个来源:
终端特殊按键
ctl+c SIGINT 暂停 ctl+z SIGTSTP 停止 ctl+\ SIGQUIT 退出
软件来源,包括发送信号相关函数和硬件异常产生信号
函数: kill raise alarm setitimer sigqueue 硬件异常 除0操作 SIGFPE 访问非法内存 SIGSEGV
信号产生缘由
以下罗列了各种信号产生的缘由
- SIGHUP:当用户退出shell时,由该shell启动的所有进程将收到这个信号,默认动作为终止进程
- SIGINT:当用户按下了<Ctrl+C>组合键时,用户终端向正在运行中的由该终端启动的程序发出此信号。默认动作为终止里程。
- SIGQUIT:当用户按下<ctrl+>组合键时产生该信号,用户终端向正在运行中的由该终端启动的程序发出些信号。默认动作为终止进程。
- SIGILL:CPU检测到某进程执行了非法指令。默认动作为终止进程并产生core文件
- SIGTRAP:该信号由断点指令或其他trap指令产生。默认动作为终止里程并产生core文件。
- SIGABRT:调用abort函数时产生该信号。默认动作为终止进程并产生core文件。
- SIGBUS:非法访问内存地址,包括内存对齐出错,默认动作为终止进程并产生core文件。 8. SIGFPE:在发生致命的运算错误时发出。不仅包括浮点运算错误,还包括溢出及除数为0等所有的算法错误。默认动作为终止进程并产生core文件。
- SIGKILL:无条件终止进程。本信号不能被忽略,处理和阻塞。默认动作为终止进程。它向系统管理员提供了可以杀死任何进程的方法。
- SIGUSE1:用户定义的信号。即程序员可以在程序中定义并使用该信号。默认动作为终止进程。
- SIGSEGV:指示进程进行了无效内存访问。默认动作为终止进程并产生core文件。
- SIGUSR2:这是另外一个用户自定义信号,程序员可以在程序中定义并使用该信号。默认动作为终止进程。
- SIGPIPE:Broken pipe向一个没有读端的管道写数据。默认动作为终止进程。
- SIGALRM:定时器超时,超时的时间由系统调用alarm设置。默认动作为终止进程。
- SIGTERM:程序结束信号,与SIGKILL不同的是,该信号可以被阻塞和终止。通常用来要示程序正常退出。执行 shell命令Kill时,缺省产生这个信号。默认动作为终止进程。
- SIGCHLD:子进程结束时,父进程会收到这个信号。默认动作为忽略这个信号。
- SIGCONT:停止进程的执行。信号不能被忽略,处理和阻塞。默认动作为终止进程。
- SIGTTIN:后台进程读终端控制台。默认动作为暂停进程。
- SIGTSTP:停止进程的运行。按下<ctrl+z>组合键时发出这个信号。默认动作为暂停进程。
- SIGTTOU:该信号类似于SIGTTIN,在后台进程要向终端输出数据时发生。默认动作为暂停进程。
- SIGURG:套接字上有紧急数据时,向当前正在运行的进程发出些信号,报告有紧急数据到达。如网络带外数据到达,默认动作为忽略该信号。
- SIGXFSZ:进程执行时间超过了分配给该进程的CPU时间,系统产生该信号并发送给该进程。默认动作为终止进程。
- SIGXFSZ:超过文件的最大长度设置。默认动作为终止进程。
- SIGVTALRM:虚拟时钟超时时产生该信号。类似于SIGALRM,但是该信号只计算该进程占用CPU的使用时间。默认动作为终止进程。
- SGIPROF:类似于SIGVTALRM,它不公包括该进程占用CPU时间还包括执行系统调用时间。默认动作为终止进程。
- SIGWINCH:窗口变化大小时发出。默认动作为忽略该信号。
- SIGIO:此信号向进程指示发出了一个异步IO事件。默认动作为忽略。
- SIGPWR:关机。默认动作为终止进程。
- SIGSYS:无效的系统调用。默认动作为终止进程并产生core文件。
- SIGRTMIN~(64)SIGRTMAX:LINUX的实时信号,它们没有固定的含义(可以由用户自定义)。所有的实时信号的默认动作都为终止进程。
信号处理
信号处理行为
进程处理信号的行为分为以下几种方式:
SIG_DFL 忽略信号
默认操作;由内核预定义的默认操作取决于信号的类型,可以是以下类型之一:
Treminate:进程被终止(杀死)
Dump:进程被终止(杀死),如果可能,创建包含进程执行上下文的核心转储文件
Ignore:信号被忽略
Stop:进程被停止,即把进程置为TASK_STOPPED状态
Continue:如果进程被停止,就把它设置为TASK_RUNNING状态
通过调用相应的信号处理函数捕捉信号(自定义信号捕捉)
信号处理流程
信号的处理动作是用户自定义的函数,在信号递达时就调用这个函数,这称为信号捕捉。由于信号处理函数的代码是在用户空间执行的,处理过程较为复杂,我们画图来进行解释:

上图很好的说明了信号捕捉时用户态和内核态的切换(用户处理信号最好的时机是程序从内核态切换至用户态的时候),下面就上图的一系列操作作以解释说明:
- 用户程序注册了SIGQUIT信号的处理函数sighandler(自定义信号处理函数)。
- 当前正在执行main函数,这里发生中断、异常或者系统调用切换至内核态。
- 在内核中断处理完毕后要返回用户态的main函数之前,检查进程中有信号SIGQUIT递达。
- 内核决定返回用户态后不是恢复main函数的上下文信息继续执行,而是执行sighandler函数,sighandler函数和main函数使用不同的堆栈空间,两者之间不存在调用和被调用的关系,属于两个独立的控制流程。
- sighandler函数返回后自动执行特殊的系统调用,调用4的sigreturn再次进入内核态。
- 内核中如果没有新的信号要递达,再返回用户态就是恢复main函数的上下文继续向下执行。
从上面可以看出,对于信号捕捉函数的调用时机是异步的,简而言之,就是进程收到信号了不一定马上执行,只有调度的进程处于内核状态中,在即将返回用户空间时候,才会监测进程中PCB对应的信号区域是否有待处理信号存在,如果存在,先返回到信号处理函数,处理完成返回内核再次检测,之后才真正返回原来程序被中断的位置。
由此可以看出,如果进程处于长时间用户空间中,即长时间无中断、异常或系统调用进入内核态,也就是说无法由内核态转到用户空间,这种情况下,即使信号区域有信号记录,也不会调用信号捕捉函数。
总结
本章节大概介绍了信号基本知识,后续会介绍信号编程函数以及信号机制带来的若干问题。
邢文鹏Linux教学资料