【信号】信号的产生

信号的概念

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

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号信号来终止写端,这就是一个软件条件。

另一种软件条件:闹钟

调用alarm函数可以设定一个闹钟,也就是告诉内核在seconds秒之后给当前进程发SIGALRM信号(14号), 该信号的默认处理动作是终止当前进程

这个函数的返回值是0或者是以前设定的闹钟时间还余下的秒数。打个比方,某人要小睡一觉,设定闹钟为30分钟之后响,20分钟后被人吵醒了,还想多睡一会儿,于是重新设定闹钟为15分钟之后响,“以前设定的闹钟时间还余下的时间”就是10分钟。如果seconds值为0,表示取消以前设定的闹钟,函数的返回值仍然是以前设定的闹钟时间还余下的秒数

#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文件里,以后就可以通过这个文件来获取你进程的异常信息

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱敲代码的奇点

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值