前言:本文主要介绍信号是什么,信号的产生以及核心转储的机制。
一、信号
1)信号是什么
信号,又称为软中断信号,是Linux系统响应某些条件而产生的一个事件。是操作系统向一个进程或者线程发送的一种异步通知,用于通知该进程或线程某种事件已经发生,需要其做出相应的处理。
在进程接收到信号以后,一般有以下三种处理方式
1.忽略信号:即不对信号该信号做任何处理。
2.执行默认处理:在没有自定义处理信号的情况下,执行信号的默认操作,linux下信号的默认操作一般都会终止进程的运行。
3.捕获信号:自定义在接收某一信号时对该信号进行处理。
2)信号的分类
在linux下面,可以使用kill -l 查看常见的信号。其中,前31个为非实时信号,亦称为不可靠信号,是在signal.h下定义的宏,其前面的数字即为其值;后31个为实时信号,也叫做可靠信号,相对于前31个,其特性是当信号被处理时,进程会不受cpu时间片限制,直至完成对信号的处理。
二、信号的使用
1)信号的产生
1.1使用kill产生信号
kill -信号编号/信号宏 进程PID
例如,通过发送SIGINT方式终止PID为1166259的进程
1.2使用键盘产生信号
通过键盘可以向前台进程发送信号,例如,ctrl+c对应的是2号信号;ctrl+\对应的是3号信号
1.3系统调用产生信号
kill系统调用
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
//作用:向指定进程发送指定信号
//pid:目标进程的pid
//sig:需要发送的信号
//返回值:如果发送成功,返回值为0,否则为-1
raise系统调用
#include <signal.h>
int raise(int sig);//作用是向调用者发送指定信号
//sig:指定信号
absort系统调用
#include <stdlib.h>
void abort(void);
//作用是向调用者发送6号信号
1.4软件条件产生信号
在系统调用中,存在alarm闹钟,当设定的时间结束,系统会发送14号SIGALRM信号至调用者。
#include <unistd.h>
unsigned int alarm(unsigned int seconds);//作用是定时结束后操作系统会发送SIGALRM信号到调用者
//seconds:指定定时时长,如果second为0,则立即停止计时,并且发送信号
//返回值:返回上一次未完成计时的剩余时长
int main()
{int cnt = 0;alarm(5);while(true){cout<<++cnt<<endl;sleep(1);if(cnt == 3){int ret = alarm(0);cout<<"ret is: "<<ret<<endl;break;}}
}
时钟提前终止,返回值为上一轮时钟的剩余时间
此外,还有例如管道通信中,读端被关闭后,操作系统会发送13号信号至写端使其关闭也属于软件条件产生信号。
1.5异常产生信号
代码在运行过程中如果出现异常,根据异常类型的不同,会在cpu的EFLAGS(标志寄存器)中将溢出标志位修改,并且告知操作系统,操作系统再向进程发送异常信号。
写的代码在运行过程中出现异常,例如下面除0异常,在不退出进程的情况下,cpu会一直存在异常并且告知操作系统,操作系统会一直向进程发送8号信号。
void handler(int sig)
{cout<<"get a sig: "<<sig<<endl;
}int main()
{signal(SIGFPE,handler);int a = 1;a/= 0;while(1){sleep(1); }
}
除此之外,野指针造成的段错误会导致11号信号异常。
在对野指针指向的内存进行访问时,cpu会调用CR3(存放页表首地址的物理地址)中的内存与虚拟地址进行处理,如果MMU(内存管理单元,用于虚拟地址到物理地址的映射)在映射过程中如果出现异常(例如权限问题,越界问题等),就会将野指针存到CR2寄存器当中,并且告知操作系统发生异常,操作系统向进程发送异常信号11。
2)信号的发送/写入
在linux底层pcb(进程控制模块)中,存在一个字段为sigending,其类型为32位的整型,其每一位代表一种信号,1表示有该信号,0表示无该信号,当信号传入该进程时,会将进程中sigendiing变量的对应位置1,后续进程再对信号进行处理。例如,向该进程发送2号信号,即把sigending的第二位置1。
从而可以得知,发送信号的过程其实就是修改进程里面的signending变量对应位的状态,该操作只能由操作系统完成。所以无论以上哪种方式产生信号,都需要操作系统介入,将信号写入到对应进程的pcb当中。
三、Core和core文件
1)Core 和 Term的区别
在31种普通信号中,大部分信号的默认行为是终止进程,而终止进程又一般分成两类:Core和Term,例如SIGABRT的终止操作为Core,而SIGALRM的终止操作为Term。其区别是当core dump功能开启时,Core方式终止会生成一个异常信息文件,调试时可以直接打开该文件提示程序异常信息。
2)dump core标志位
在进程等待时,wait和waitpid接口中都有一个参数int *wstatus ,其低16位作用是记录进程退出信息,在正常终止的情况下,高8位为其状态码,表示退出状态;低8位为0,表示其终止信号。在进程被信号终止的情况下,高8位不使用,第9位表示core dump是否启用,低7位表示终止信号。当dumpcore标志位为1表示打开coredump(核心转储)功能,生成一个core文件,包含与调试有关的核心数据。
3)打开和关闭coredump功能
在linux下面,coredump功能默认关闭,可使用ulimit -a查看,其中core file size 为0,表示功能关闭,可通过ulimit -c设置其值开启,设回0再次关闭coredump。设置完成后重新运行程序,待重新被信号终止即可。
4)core文件的使用
程序通过携带可调试信息编译运行后由于信号导致进程意外退出,生成文件core。使用gdb打开调试程序,在gdb中输入core-file core文件名,gdb会读取程序异常信息,并且定位行数和位置及异常原因。