一.信号概念
(一).大致认识
信号是操作系统控制进程的一种方式,比如ctrl C、栈溢出程序崩溃、kill -9命令等底层都是操作系统发送信号给进程执行特定操作。
因此,所有信号就底层而言都是操作系统发出的。
同时,进程接收到信号后,不一定会立即处理,信号发送方也不一定会阻塞等待信号处理结果,即信号与进程是异步通信。
其中,1 - 31号信号是普通信号,34 - 64号是实时信号
(二).信号处理方式
信号的处理方式共有三种,即默认、忽略、自定义。
默认情况下,信号的处理方式按照操作系统提供的方式执行。
忽略情况下,信号的处理方式就是不处理,接收后略过该信号执行过程。
自定义情况下,接收信号后按照用户定义的函数执行具体的方法。
(三).常见信号
二.基础信号指令和函数
(一).信号指令
kill -l #查看所有信号
man 7 signal #查看信号的含义(主要是1 - 31号信号)
kill -x 进程pid #指定进程执行x 号信号
(二).信号函数
头文件:<signal.h>
①signal
用于自定义信号方法,即捕捉信号。
signum是指定的信号值。
handler是函数指针类型,即用户自定义的信号函数, 函数要求返回值为void,单参数且为int类型。
返回值是函数指针类型,返回旧的函数方法。
使用方式如下:
void func(int signum){
cout << "这是2号信号的自定义函数" << endl;
}
int main(){
signal(2, func);//方式一
signal(SIGINT, func);//方式二
return 0;
}
②kill
用于给指定进程发送特定信号
第一个参数即目标进程的pid。
第二个参数是要执行的信号。
成功返回0,失败返回-1。
使用方式:
int main(int argc, char* argv[]){
//...
if(...) kill(getpid(), 9);//终止本进程
else
//...
return 0;
}
③raise
用于给本进程发送特定信号
参数即指定信号。
返回值0为成功,非0为失败。
④abort
用于终止本进程(发送6号信号/SIGABRT)
与其他终止进程不同,该信号可以触发核心转储。
⑤alarm
用于在闹钟时间后终止进程(发送14号信号/SIGALRM)
参数是设定的闹钟时间。
返回值是上一次设定的闹钟剩余多少时间。如果上次闹钟中途被打断 那么剩余时间非0;如果上次的执行完毕,返回为0。
使用方式:
//每隔2秒打印一次hello world
void func(int signum){
std::cout << "hello world" << std::endl;
alarm(2);//再次设定闹钟
}
int main(){
signal(SIGALRM, func);
alarm(2);
return 0;
}
三.核心转储(core dump)
核心转储功能,应用于代码出错的调试,如果进程被信号异常终止且该信号有核心转储功能,那么就会生成一个数据文件到磁盘中,在gdb调试时可用于查看异常信息。
(一).生成数据文件
相关命令如下:
ulimit -c num #打开核心转储,num为目标数据文件的大小对齐数。
ulimit -a #查看核心转储信息
同时waitpid中status参数的第8比特位即核心转储,0代表没发生核心转储,1代表发生核心转储。
假设我们有一个死循环的子进程,发送kill -6信号给该子进程,那么磁盘中就会生成一个数据文件。
int main(){
int id = fork();
if(id == 0)
{
while(true){
sleep(1);
cout << "test now " << getpid() << endl;
}
}
int status;
waitpid(id, &status, 0);
//打印回收子进程的退出信号和是否发送核心转储
cout << "child quit " << "退出信号: " << (status & 0x7F)\
<< "core dump: " << (status >> 7 & 1) << endl;
return 0;
}
(二).数据文件的使用
core-file filename #在gdb调试中使用,可直接定位到错误位置及原因
如果debugging是一种消灭bug的过程,那编程就一定是把bug放进去的过程——Edsger Dijkstra
如有错误,敬请斧正