信号是操作系统中的一种对进程干涉的方式,通过信号可以挂起恢复进程,也可以杀死进程。
利用信号也可以实现进程间通信。
通过kill -l可以查看信号,一共有64个信号,1-32是经典信号,也称为不可靠信号、标准信号。33-64是自定义信号,也称为可靠信号、实时信号。早期Unix系统中的信号机制比较简单和原始,后来在实践中暴露出一些问题,因此,把那些建立在早期机制上的信号叫做不可靠信号,后面增加的33-64信号被称为可靠信号。
在这之中,32,33这两个信号是隐藏的,事实上32也是可靠信号。
除去这两个,其中1-31的信号是UNIX经典信号,34-64是自定义信号用于设备绑定。
信号的来源主要是硬件和软件
硬件
在键盘上使用组合键:
ctrl + c:触发SIG_INT 将会杀死进程
ctrl + z:触发SIG_STOP 将会挂起进程
ctrl + /:触发SIG_QUIT 将会杀死进程
当IO缓冲区检测到按键按下时,这个事件就会被告知操作系统,操作系统发送对应的信号对进程进行干预。这些操作只对前台进程有效(当前黑框里执行的那个)
当发生某些硬件异常时也将触发信号:
也是有三种常见情况
非法操作内存,对内存权限不足,例如只读内存写操作,检测出硬件异常,系统向违规进程发送SIGSEGV(11)(段错误),杀死进程
非法操作内存,对内存越界访问,检测出硬件异常,系统向违规进程发送SIGBUGS(7)(总线错误),杀死进程
cpu违规计算,CPU计算器产生运算异常,系统向进程发送SIGFPE(8)(浮点数例外),杀死进程
对于这些错误相关的信号,他们的默认行为都是终止进程并生成核心文件。利用核心文件方便查看那里出了问题。
软件
除了键盘操作还可以用kill发送信号:
格式一般是:kill 信号序号 进程ID(代表着向某个进程发送某个信号)
某些函数的执行也可以触发信号:
常见的有3个:
kill(int pid , int signo)
向任意进程发送任意信号(顺便说一下,kill命令就是使用kill函数实现的)
raise(int signo)
向被调用的进程发送任意信号
abort(void)
向自身进程发送SIGABRT(6),这个信号在Linux系统中的含义是进程自己发送一个中止信号,通常是由于进程检测到某些严重错误或异常情况而导致的。他的默认行为是立即终止进程并生成一个核心转储文件(core dump file),用于调试进程运行过程中的错误。
信号的优先级
信号也是一种中断,既然中断有优先级,那么信号也是有优先级的,一般是标准信号(1-32)的优先级高于实时信号(33-64),其中标准信号中,SIGKILL和SIGSTOP是高权集信号,优先级更高并且无法被屏蔽、捕捉和忽略,也是发送必抵达,其他的标准信号一般是序号越小优先级越高,而SIGUER1和SIG_USRE2的优先级比较低。在实时信号中,没有明显的优先度。
信号行为结构体
每一个信号都有自己的行为结构体,在结构体中可以定义行为(捕捉,忽略,屏蔽),以及其他的属性。
struct sigaction {
void (*sa_handler)(int); // 信号处理函数的地址
sigset_t sa_mask; // 在信号处理函数执行期间要阻塞的信号集合
int sa_flags; // 信号处理的标记
void (*sa_sigaction)(int, siginfo_t *, void *); // 信号处理函数的地址(带有附加信息)
};
在一个结构体中:
void (*sa_handler)(int)和void (*sa_sigaction)(int, siginfo_t *, void *)是互斥的,只能出现一个。选择捕获函数的地址就能使信号发出后对应执行捕获函数。也可以选择默认选项,执行默认的行为。设置为SIG_IGN就是不执行任何操作。
sa_flags是
用于设定信号处理的一些标记,比如SA_RESTART
表示系统调用被中断后会自动重启,SA_SIGINFO
表示信号处理函数是带有附加信息的扩展类型。
而sa_mask是用来定义屏蔽的,可以防止在处理当前信号时又收到其他信号。
我们可以修改信号行为结构体,然后利用sigaction替换信号行为结构体来实现对信号的操作。
相较于早版本的signal函数处理信号,使用信号结构体提供了更多的灵活性和可定制性。所以现在一般都有信号结构体了。