目录
信号的概念:
异步的概念:
信号的3种处理方式:
修改2号信号为自定义信号处理:
编辑
信号捕捉后恢复和信号的忽略:
信号的分类与编号:
特殊的信号:
进程信号表的继承:
进程管理收到的普通信号:
系统调用kill向进程发送信号:
系统调用raise向自己发送信号:
系统调用abort向自己发送6号信号:
信号的产生方式:
异常产生信号:
发送信号的本质:
信号的概念:
-
信号是进程间通信的一种异步通知机制,用于向目标进程发送通知。
-
信号的处理是异步的,意味着信号可以在任何时候产生,而进程会在适当时机对信号作出处理。
异步的概念:
-
异步(Asynchronous)是指在编程或系统设计中,任务的执行不需要立即等待其他任务完成,而是可以在不阻塞的情况下继续进行。
信号的3种处理方式:
-
信号有三种处理方式:默认处理,忽略,自定义处理(捕捉)。
-
信号的捕捉,就是对信号设置自定义信号函数,signal函数会直接修改信号表中对应信号的处理函数指针,使其指向自定义信号处理函数。
#include<signal.h> //signal函数使用需要包含的头文件
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
修改2号信号为自定义信号处理:
#include<iostream>
#include<unistd.h> //使用sleep
#include<signal.h> //使用signal
#include<cstdlib> //使用exit
using namespace std;
void signalHandle(int signum) //返回值必须为void,参数必须为int
{
cout<<"收到信号"<<signum<<endl;
exit(1);
}
int main()
{
signal(2,signalHandle); //将2号信号设置自定义处理函数
while(true)
{
cout<<"等待接收信号..."<<endl;
sleep(1);
}
return 0;
}
信号捕捉后恢复和信号的忽略:
signal(int signum,SIG_IGN); //SIG_IGN:忽略该信号。
signal(int signum,SIG_DFL); //SIG_DFL:恢复为信号的默认处理方法(之前设置的处理函数将作废)。
信号的分类与编号:
-
信号分为两类:普通信号(1~31)和实时信号(34~64)。
-
信号以宏的方式实现,使用信号可以通过信号的宏名也可以通过信号的值。
-
使用 kill -l 可以查看当前系统支持的所有信号。
-
使用man 7 signal查看信号详细介绍。
特殊的信号:
-
没有 0 号信号,0 常用于表示进程的正常退出。
-
32 和 33 号信号是为操作系统内部保留的,用户进程不会使用这些信号。
-
9 号信号 (SIGKILL) 不能被进程捕获或忽略,称为“管理员信号”,确保进程可以被强制终止。
进程信号表的继承:
-
每个进程都有一张信号表,本质是一个函数指针数组,数组的下标就对应每一个信号的编号。
-
当使用fork()系统调用创建子进程时,子进程会复制父进程的信号处理表。这意味着父进程如何处理信号,子进程刚被创建时也会如何处理信号。
-
当一个进程通过 exec() 系列函数进程程序替换时,信号表中的自定义处理方式会恢复为默认处理方式(操作系统定义的默认行为)。
进程管理收到的普通信号:
-
进程收到某个信号,操作系统不会立即中断进程的执行,而是先记录该信号。当进程进入内核态时,操作系统会检查是否有待处理的信号,并根据优先级和进程的状态决定何时处理这些信号。
-
操作系统会使用一个位图来表示进程接收到的信号状态。位图中的每一位对应一个信号编号,通常信号编号从1开始。因此位图第0位是不用的。当对应位置为1时表示进程收到该信号,为0时表示进程没有收到对应信号。
-
位图的比特位数需要覆盖所有可能的信号。现代Linux系统中支持的信号通常远超过32个,所以可能需要使用多个uint32_t或更大的数据结构(如sigset_t)来存储信号。
系统调用kill向进程发送信号:
#include <signal.h> //头文件包含
int kill(pid_t pid, int sig); //向pid进程发送sig信号
系统调用raise向自己发送信号:
#include <signal.h> //头文件包含
int raise(int sig); //向自己发送sig信号,相当于kill(getpid(),sig);
系统调用abort向自己发送6号信号:
#include <stdlib.h>
void abort(void); //给自身发送6号信号强终止自己,即使使用signal重写了信号的处理方法,进程也会被强制结束。
信号的产生方式:
-
信号的产生方式主要有三种:
-
在linux下,通过特殊的键盘组合键可以产生信号,如上
-
通过一些系统调用可以产生信号。如上
-
程序运行过程中,某些程序异常会自动产生异常信号,如下
异常产生信号:
-
非法访问内存:当程序试图访问无效的内存地址时,会产生 SIGSEGV 信号(分段错误)。
-
浮点运算异常:当程序发生非法的浮点运算(如除以零)时,会产生 SIGFPE 信号。
-
非法指令:当程序试图执行非法或未知的指令时,会产生 SIGILL 信号。
-
总线错误:当程序发生不对齐的内存访问等硬件错误时,会产生 SIGBUS 信号。
-
断管(Broken Pipe):当进程向一个没有读取端的管道写入数据时,会产生 SIGPIPE 信号。
发送信号的本质:
-
操作系统向进程发送信号,本质上是操作系统修改进程pcb的位图。
-
只有操作系统可以向进程发送信号,因为操作系统才是内核数据结构的管理者。
-
因此,上述产生信号的方式虽然有几种,但是信号的发送者只能是操作系统。