一、信号概念
是什么?
Linux系统提供的让用户(进程)给其他进程发送异步信息的一种方式,注意,信号与信号量没有任何关系。
1、进程可以识别信号并作处理;
2、信号到来时,如果此时有更重要的事情要处理,那么到来的信号要临时保存,即在合适的时候处理;
3、信号的产生是随时的,不能准确预料,所以信号是异步发送的;
4、信号的产生,是由别的进程产生的,当前的进程收到这个信号之前,在做其他的事情,是并发的。
为什么?
因为系统要求进程要有随时响应外部信号的能力,并作出反应。
怎么办?
准备->信号产生->信号保存->信号处理
准备:
1️⃣常见信号:
数字和名字都可以识别信号,名字其实是宏。从34开始是实时信号。注意,没有32、33信号。
2️⃣信号的处理方式
有:默认、自定义、忽略
默认,就是按原来信号的处理方式进行处理
比如:下面代码死循环用2号信号终止,终止是2号信号的默认处理方式
int main()
{
while(true)
{
cout << "I am acting..., pid: " << getpid()<<endl;
sleep(1);
}
return 0;
}
自定义是由用户来决定信号的处理方式,与信号的默认处理方式不同。2号信号是终止进程,用户可以让它的处理方式通过函数变成打印一句话和终止进程,终止进程的退出码为100,再把函数给系统调用接口signal。自定义的方式也叫信号的捕捉。
void handler(int sig)
{
cout << "I am a sig, sig: " <<sig<<endl;
exit(0);
}
int main()
{
signal(2, handler);
while(true)
{
cout << "I am acting..., pid: " << getpid()<<endl;
sleep(1);
}
return 0;
}
注意:signal调用完,handler方法不会立即被执行,要收到对应的信号才执行;如果没有收到信号,handler方法就不会被调用。
信号忽略,相当于忽略了默认和自定义的处理方法
void handler(int sig)
{
cout << "I am a sig, sig: " <<sig<<endl;
exit(100);
}
int main()
{
//signal(2, handler);
signal(2, SIG_IGN);
while(true)
{
cout << "I am acting..., pid: " << getpid()<<endl;
sleep(1);
}
return 0;
}
二、信号产生
信号的产生可以通过kill命令、键盘,还可以通过系统调用接口、软件条件(设置闹钟)、异常产生
1️⃣键盘:比如:ctrl+c
2️⃣kill系统调用接口——对任意进程发送任意信号
int main(int argc, char* argv[])
{
if(argc != 3)
{
cout << "Usage: " << argv[0] << "-signum pid" << endl;
exit(1);
}
int signum = stoi(argv[1]+1);
int sigpid = stoi(argv[2]);
int n = kill(sigpid, signum);
if(n < 0)
{
cout << "kill fail, errno: " << strerror(errno) << endl;
exit(2);
}
return 0;
}
3️⃣raise——对自己发送任意信号
int main()
{
int cnt = 0;
while(true)
{
cout << "cnt: " << cnt++ << endl;
sleep(1);
if(cnt == 5)
{
cout << "send 9 to caller" <<endl;
raise(9);
}
}
return 0;
}
4️⃣设置闹钟
设置一个简单的闹钟,接口alarm,注意,闹钟设置的时间只是什么时候响,但是只响一次。如果函数的参数设置为0,就是取消闹钟
一、
int main()
{
alarm(1);
int cnt = 0;
while(true)
{
cout << "cnt: " << cnt++ <<endl;
}
return 0;
}
二、
int cnt = 0;
void handler(int sig)
{
cout << "sig num: " <<sig << ", cnt: " << cnt << endl;
exit(0);
}
int main()
{
signal(SIGALRM, handler);
alarm(1);
while(true)
{
cnt++;
}
return 0;
}
第二种的cnt比第一种更大,第二种是内存级,较快;第一种是IO级,较慢。
如何让闹钟一直响:在handler方法里面再设置闹钟
int cnt = 0;
void handler(int sig)
{
cout << "sig num: " <<sig << ", cnt: " << cnt << endl;
int n = alarm(2);
}
int main()
{
signal(SIGALRM, handler);
alarm(1);
while(true)
{
sleep(1);
cout << "cnt: " << cnt++ <<endl;
}
return 0;
}
5️⃣异常
一、除零
void handler(int sig)
{
cout << "get a sig: " << sig <<endl;
exit(0);
}
int main()
{
signal(SIGFPE, handler);
int a=10;
a/=0;
while(true) sleep(1);
return 0;
}
二、野指针
void handler(int sig)
{
cout << "get a sig: " << sig <<endl;
exit(0);
}
int main()
{
signal(SIGSEGV, handler);
int *p = nullptr;
*p = 100;
while(true) sleep(1);
return 0;
}