当前位置: 首页 > news >正文

【信号】信号的产生

信号的概念

什么是信号?我们生活中的红绿灯,闹钟,外面电话等等这些都是信号,我们是怎么认识这些信号的,我们认识这些信号,并且知道这些信号的处理方法,对于进程来说,也会认识相应的信号

1.进程必须有识别和处理信号的能力,信号没有产生也要具备处理信号的一部分,处理信号的能力属于内置功能的一部分

2.进程即使没有收到信号,也知道哪些信号怎么处理

3.当进程收到某个信号的时候,可能并不会马上处理这个信号,他会保存起来,合适的时候进行处理

信号的处理方式:

a.默认动作

b.忽略

c.自定义动作 (信号的捕捉)

信号的捕捉---Ctrl+C的理解

kill -l   查看信号

 

man   7  signal   查看信号的详细信息

 往下翻就会有下面内容

我们只研究1-31号信号,1-31号信号为普通信号,其他为实时信号 

当我们在键盘按下Ctrl+c时,运行的进程就退出了

代码:

int main()
{while(true){cout<<"hello signal"<<endl;sleep(1);}return 0;
}

为什么Ctrl+C可以使进程退出?Linux中,一次登录,一般会配上一个bash,每一次登录,只允许一个进程是前台进程,可以允许多个进程是后台进程,键盘输入首先是被前台进程所收到,所以Ctrl+C就被前台进程识别到,进程就退出了

假如把进程弄成后台进程

Ctrl+C是多少号信号呢? 2号

捕获信号的接口,信号本来是有默认动作的,用了这个函数可以自定义信号的动作

 

#include<iostream>
#include<signal.h>
#include<unistd.h>using namespace std;void myhander(int signal)
{cout<<"process get signal: "<<signal<<endl;
}int main()
{signal(2,myhander);while(true){cout<<"hello signal"<<endl;sleep(1);}return 0;
}

当我们按下Ctrl+c时进程不退出了,并且打印了那句话,说明说明2号信号被捕获了,实行了自定义的方法,就不在实行默认的方法了

键盘数据是如何给内核的,Ctrl+C如何变成信号的?

键盘被摁下肯定是OS先知道,OS怎么知道键盘上有数据呢?难道OS会实时看看键盘有没有数据?OS也很忙的,所以不会这样弄。其实CPU也可以和外设通过硬件中断交流的,CPU上有很多针脚,这些针脚用来接收外设信号的,当键盘有数据时,键盘会通过中断单元向CPU的某个针脚发送中断号,OS会维护一张中断向量表,就是数组,内容是某个中断号对应方法的地址,CPU拿到中断号,就找到对应的方法,OS就会知道,OS就会实现对应的方法,假如是读取键盘的方法,那么OS就会从键盘读取数据到键盘缓冲区里。

不是所有的信号都可以被捕捉,都可以实行自定义动作

1-31号信号中,9号和19号不能被捕捉

信号的产生

1.键盘组合键

Ctrl+C  2号信号

Ctrl+/   3号信号

2.kill命令

kill   -signo  pid

例如:kill -9  52020

3.函数调用

kill函数,向指定进程发送几号信号

 

 mykill.cc

#include <iostream>
#include <string>
#include <signal.h>
#include <unistd.h>using namespace std;void Usage(string proc)
{cout << "Usage:\n\t" << proc << " signum pid\n";
}int main(int argc,char* argv[])
{if(argc!=3){Usage(argv[0]);exit(1);}int signum=stoi(argv[1]);pid_t id=stoi(argv[2]);int n=kill(id,signum);if(n==-1){perror("kill");exit(2);}return 0;
}

 raise函数,向调用者发送信号

#include <iostream>
#include <signal.h>
#include <unistd.h>using namespace std;void myhander(int signal)
{cout << "process get signal: " << signal << endl;
}int main()
{signal(2, myhander);int cnt = 5;while (true){cout << "hello signal,mypid: " << getpid() << endl;cnt--;if (cnt == 0) raise(2);sleep(1);}return 0;
}

 

 abort函数,向本进程发送6号信号

#include <iostream>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>using namespace std;void myhander(int signal)
{cout << "process get signal: " << signal << endl;
}int main()
{signal(SIGABRT, myhander);int cnt = 5;while (true){cout << "hello signal,mypid: " << getpid() << endl;cnt--;if (cnt == 0) abort();sleep(1);}return 0;
}

运行时发现,虽然调用了自定义方法(自定义方法中没有退出) ,但是进程退出了,说明abort函数内部除了有发送6号信号,还会有让进程退出的内容

4.异常

程序异常也会发送信号使进程退出

除0错误

#include <iostream>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>using namespace std;int main()
{int a=10;a/=0;return 0;
}

 

除0异常会发送几号信号呢 ?8号信号

自定义捕获8号信号

#include <iostream>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>using namespace std;void myhander(int signal)
{cout << "process get signal: " << signal << endl;
}int main()
{signal(8,myhander);int a=10;a/=0;return 0;
}

发现程序一直重复打印,为什么不是打印一条就退出呢?为什么呢?底层来说是因为OS一直在有这个除0错误(细节不讲了比较复杂),就一直在发8号信号,所以一直打印 

当我们用野指针异常来实现时,也是上面所对应的问题,野指针发送的是11号信号

5.软件条件

前面讲的管道,当我们读端关闭,写端打开,都没人读了,OS就发送13号信号来终止写端,这就是一个软件条件。

另一种软件条件:闹钟

给进程设置一个闹钟,时间一到闹钟就响,就会向进程发送14号信号,进程退出,返回值是闹钟的剩余时间,可以不用管

#include <iostream>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>using namespace std;int main()
{int n=alarm(5);while(true){cout<<"hello signal,pid: "<<getpid()<<endl;sleep(1);}}

 

 捕获14号信号

#include <iostream>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>using namespace std;void myhander(int signal)
{cout << "process get signal: " << signal << endl;
}int main()
{signal(SIGALRM,myhander);int n=alarm(5);while(true){cout<<"hello signal,pid: "<<getpid()<<endl;sleep(1);}}

就打印了一条,因为只设了一个闹钟,就响一次 

Core和Term的区别

man 7 signal时,我们发现有些信号是Term,有些是Core,Term就是直接退出,Core就是退出并把进程运行信息打包到一个文件里,供我们使用,那么OS怎么知道是哪个呢?

我们在进程等待那里看到status的比特位被划分成好多部分

 剩下的一个比特位就是来存放是Core还是Term的信息的

#include <iostream>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>using namespace std;int main()
{pid_t id = fork();if (id == 0) // child{int cnt = 500;while (cnt){cout << "i am child,pid: " << getpid() << " cnt: " << cnt << endl;cnt--;sleep(1);}exit(1);}// fatherint status = 0;pid_t rid=waitpid(id,&status,0);if(rid==id){cout << "child quit info, rid: " << rid << " exit code: " << ((status>>8)&0xFF) << " exit signal: " << (status&0x7F) <<" core dump: " << ((status>>7)&1) << endl; }return 0;
}

2号信号对应的是Term,Term比特位是0

3号信号对应的是Core,但是core dump还是0,为什么?默认云服务器上的core功能是被关闭的

 打开之后,在运行程序

当前目录下就自动生成了一个文件,这个core文件里面的内容是你的代码的运行信息,怎么使用这个文件呢?  

发现这个core文件很大,所以云服务器上是默认关闭的 

打开系统的core dump 功能,进程一旦异常,OS就会把进程的运行信息转储到当前目录的一个core文件里,以后就可以通过这个文件来获取你进程的异常信息


http://www.mrgr.cn/news/22331.html

相关文章:

  • LeetCode 热题 100 回顾3
  • 【C++】STL容器详解【下】
  • kubelet组件的启动流程源码分析
  • 【算法专场】模拟(下)
  • 【机器学习】和【人工智能】在物理学领域的应用以及代码案例分析
  • Spring 源码解读:自定义实现Bean定义的注册与解析
  • 位运算技巧总结
  • 【智能排班系统】缓存组件封装
  • lvgl8.3.6 控件垂直布局 label控件在image控件的下方显示
  • 35天学习小结
  • 掌握Hive函数[3]:从基础到高级应用
  • HCIA--实验十一:单区域OSPF路由实验
  • Leetcode第414周赛第二题:3281. 范围内整数的最大得分
  • 线程相关内容
  • Arrays.sort()方法在Java中的使用:理论与实践
  • exec与system的区别(C语言)
  • JS中给元素添加事件监听器的各种方法详解(包含比较和应用场景)
  • 国内顶尖的做LLM方向的大学实验室
  • B: 小球反弹
  • 利用TCP编程实现FTP功能