信号的概念
什么是信号?我们生活中的红绿灯,闹钟,外面电话等等这些都是信号,我们是怎么认识这些信号的,我们认识这些信号,并且知道这些信号的处理方法,对于进程来说,也会认识相应的信号
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);
}
// father
int 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文件里,以后就可以通过这个文件来获取你进程的异常信息