目录
前提知道信号与信号量没有任何关系就比如老婆和老婆饼一样;
生活例子--提炼基本结论
1. 比如说平时定闹钟,我今晚十一点睡觉,睡觉之前定个八点的闹钟,那么在闹钟响了在客观上就是闹钟给我发了个信号;
2. 平时我们过红绿灯,红灯亮了,红黄灯就给我发出一个信号那么此时我就应该等一等 ,绿灯亮了我就可以走了;
3. 在古代我是一个大头兵,远远望见狼烟升起,那么就知道有人要冲出来了就知道要打仗了;
4. 在家里呢,考试没考好,回家的时候发现爸妈往沙发上一座并且脸色阴沉给你传递了一个不好的信号,你此时就知道完蛋了;
1. 信号在生活中随时可以产生
你也不知道哪一天被分手了等叫做 信号的产生和我是:异步的
2. 你能认识这个信号(红绿灯,狼烟点起,脸色等)
3. 我们知道信号产生了,也知道信号触发了该怎么处理(红绿灯亮了我们知道该怎么办)
我们把2和3叫做 你能识别并处理这个信号
4. 我们可能正在做着更重要的事情,只能把到来的信号暂不处理
(就比如我点了个外卖,点好后我在打游戏,突然外卖小哥电话打来了,我就意识到了外卖到了。说明了我能认识外卖小哥电话这个信号,外卖小哥电话产生了我也知道该怎么处理下去拿外卖,但我此时正在做其他的事,那我可能说放楼下或者不管电话)
注意将上述的我换成进程即可;进程识别,进程处理,进程暂不处理等
信号概念的基本储备
信号:linux系统提供的一种向指定进程发送特定事件的方式;
比如说我要向进程发送9号信号表明要终止进程,比如说要发送19号要暂停一个信号等;
进程收到对应的信号后要对信号做识别和处理;
信号产生是异步的 :信号的到达不依赖于进程的执行状态,可能在进程执行的任何时刻被接收。
信号处理
信号的默认动作:
进行自定义处理,不想执行默认动作,想让进程收到信号后执行我设定的方法:
#include <iostream> #include <signal.h> #include <unistd.h> #include <sys/types.h> void hander(int sig) { // 这个sig就是传递过来的信号编号 std::cout << "get a sig: " << sig << std::endl; } int main() { // 对信号的自定义捕捉,我们只要捕捉一次,后续一直有效 signal(2, hander); // 对2号信号自定义,名字hander随便起//将来收到2号信号后,未来2号信号将以参数形式传递给hander方法 //如果一直不产生2号信号,那么hander就永远不会被调用 //我们也可以对更多的信号进行捕捉 signal(3, hander); signal(4, hander); signal(5, hander); while (true) { std::cout << "hello nihao,pid: " <<getpid()<< std::endl; sleep(1); } return 0; }
注意:2 SIGINT代表是终止进程,我们平常用的ctrl+c-----给目标进程发送2号信号,所以使用ctrl+c在命令行中使用直接结束进程
注意:3 SIGQUIT代表是退出进程,我们平常用的ctrl+\-----给目标进程发送3号信号,所以使用ctrl+\在命令行中使用直接退出进程
信号产生
1. 通过kill命令,向指定的进程发送特定的信号
2. 键盘可以产生信号!比如ctrl+c或者ctrl+\
3. 系统调用
int kill(pid_t pid, int sig);
kill(getpid(),xxx)==int raise(int sig);给自己发信号,但raise不如kill重要
void abort(void);表示用于立即异常终止当前程序的执行。。abort() 函数会导致程序异常终止。它通常用于在遇到无法恢复的错误时强制停止程序,并产生一个 SIGABRT 信号。
无论用户使用kill命令还是使用键盘打命令还是用户程序调用,但最终发送信号的还是os。
因为发送信号的本质就是修改pcb的位图,只有os才有资格修改;
如果我把所有信号都捕捉了呢?
4. 软件条件
就比如管道的时候,读端关闭而写端一直写---就会发送13 SIGPIPE。在软件层面上因为某些条件不满足,向你的进程发送信号;
这时候介绍alarm函数:
#include <iostream>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
int main(){
int cnt=1;
alarm(1);//设定一秒的闹钟 一秒后收到SIGALRM信号
while(true){
std::cout<<"cnt: "<<cnt<<std::endl;
cnt++;
}
return 0;
}
验证IO很慢:
只最后进行IO,不用循环里每次都IO,减少IO次数;
理解闹钟:就比如电脑关机了两天,断电断网,但是两天后把电脑打开后右下角的时间还是准确的。
这是因为我们的电脑,在主板上还有一个东西叫纽扣电池,很小,但他一直在维持电脑的时间的,虽然我们关机了,但是电脑中依旧有个电池有电。
闹钟的返回值:闹钟设置一次就默认触发一次;
5. 异常
除0崩溃:
#include <iostream> #include <signal.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> void hander(int sig) { std::cout << "get a sig: " << sig << std::endl; } int main() { signal(8,hander); while (true) { std::cout << "pid: " << getpid() << std::endl; sleep(2); int a = 10; a /= 0; // int *p=nullptr; // *p=100; } }
发现循环打印
一直在打印,捕捉到了,说明就是发送了信号。通过man 7 signal查到8号默认终止
野指针崩溃:#include <iostream> #include <signal.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> void hander(int sig) { std::cout << "get a sig: " << sig << std::endl; } int main() { signal(11, hander); int *p = nullptr; *p = 100; while (true) { std::cout << "pid: " << getpid() << std::endl; sleep(2); // int a = 10; // a /= 0; } }
发现也是循环打印,但是没在循环里面也循环打印
通过man 7 signal查到11号默认终止
崩溃了为什么会退出?因为发送的信号默认是终止进程;可以不退出么?可以,捕捉了异常,但不建议,推荐终止进程;
程序上出现异常最终会体现在硬件上,硬件上的问题又会被os识别到;