
文章目录
- [一、人眼中的信号 VS 进程眼中的信号](#一、人眼中的信号 VS 进程眼中的信号)
 - [二、ctrl+c 终止一个前台进程](#二、ctrl+c 终止一个前台进程)
 - 三、查看信号信息
 - 
- [3.1 Core dump------核心转储功能验证](#3.1 Core dump——核心转储功能验证)
 
 - 四、信号的处理方式
 - [五、ctrl+c 被解释成2号信号验证](#五、ctrl+c 被解释成2号信号验证)
 - 
- [5.1 signal------设置自定义捕捉方法](#5.1 signal——设置自定义捕捉方法)
 
 - [六、ctrl+c 是如何变成信号的?](#六、ctrl+c 是如何变成信号的?)
 - 七、异步、软中断
 - 八、结语
 
一、人眼中的信号 VS 进程眼中的信号
人眼中的信号:
- 以人为例,我们认识信号,一定是因为有人教我们,我们记住了。**认识信号包括:识别信号和知道信号的处理方法。**例如,我们可以识别红灯这个信号,并且知道红灯亮了就不能再过马路了。
 - 即便是现在没有信号产生,我们也知道产生信号后,我们应该做什么。
 - 信号产生了,我们可能并不立即处理这个信号,而是在合适的时机去处理,因为我们当前可能正在做更重要的事情,所以在信号产生后一直到信号处理前,有一个时间窗口,在这个时间窗口内,我们必须记住信号的到来。
 
进程眼中的信号:
- 进程必须能够识别、处理信号,即使信号没有产生,也要具备处理信号的能力。信号的处理能力,属于进程内置功能的一部分。
 - 进程即便是没有收到信号,也能知道哪些信号该怎么处理。
 - 当进程真的收到了一个具体的信号的时候,进程可能并不会立即处理这个信号,在合适的时候处理。
 - 一个进程必须当信号产生了,到信号开始被处理,就一定会有时间窗口,进程具有临时保存哪些信号已经发生了的能力。
 
二、ctrl+c 终止一个前台进程
现象 :ctrl+c 可以杀掉一个前台 进程。Linux 中,一次登陆中,一个终端,一般会配上一个 bash ,每一个登陆,只允许,值允许一个进程是前台进程,可以允许多个进程是后台进程。bash 也是进程,在向 bash 输入指令之前,bash 就是前台进程,此时如果在 baash 中输入一个指令 ./myprocess,以我们自己写的可执行程序为例,该程序中有一个死循环,一直向显示器打印,那么该程就会变成前台进程,bash 就会变成后台进程,此时再向 bash 中输入指令是没有任何反应的。前台进程和后台进程的一个本质区别是,谁来获取键盘输入,只有前台进程才能拿到键盘输入。 ./myprocess & 此时 myprocess 是一个后台进程。ctrl+c **本质是被进程解释为收到了 2 号信号。**进程收到2号信号的默认动作就是终止自己。
三、查看信号信息
pidof myprocess:显示操作系统中所有 myprocess 进程的 pid。
pidof myprocess | xargs kill -9:杀掉当前操作系统启动的所有 myprocess 进程。
kill -l:查看系统的信号列表。

没有0、32、33号信号,一共有62个信号。1-31号称为普通信号;34-64号称为实时信号,进程一旦收到实时信号,必须立即处理。信号本质上就是数字,右边的字符是信号的名称,在操作系统的内核中,信号一定是以宏定义的方式提供的,这些宏定义在 signal.h 中可以找到。
man 7 signal:查看信号的详细说明。

其中 Term、Core 表示终止;Ign 标记忽略;Cont 表示继续;Stop 表示暂停。

其中 core dump 标志就是用来区分 Term 和 Core 的。云服务器的 Core dump 功能默认是关闭的,可以通过 ulimit -a 指令来查看当前系统的所有资源限制。

可以通过 ulimit -c size、 去设置它的大小为 size,如果 size > 0  就表示开启 Core dump 功能。
3.1 Core dump------核心转储功能验证
            
            
              cpp
              
              
            
          
          #include <iostream>
#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, my pid is: " << getpid() << endl;
            sleep(1);
        }
    }
    else if(id > 0)
    {
        // father
        int status = 0;
        pid_t ret = waitpid(id, &status, 0);
        if(ret == id)
        {
            cout << "wait " << id << "success, " << "exit code: " << WEXITSTATUS(status) << ", exit signalnum: " << WTERMSIG(status) << ", core dump: " << ((status >> 7)&1) << endl; 
        }
    }
    return 0;
}
        
可以看到,Term 对应的 core dump 标志位是 0;Core 对应的 core dump 标志位是 1。前者表示正常终止,后者表示异常终止。打开系统的 core dump 功能,一旦进程出现异常,操作系统会将进程在内存中的运行信息,给 dump (转储)到进程的当前目录中,形成 core.pid 文件,core dump 被称作核心转储 。core.pid 文件中详细记录了程序的异常原因,可以直接帮我们定位到出错行。
            
            
              cpp
              
              
            
          
          #include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
using namespace std;
int main()
{
    int a = 10;
    int b = 0;
    a /= b;
    return 0;
}
        
四、信号的处理方式
收到信号有以下三种处理方式:
- 默认动作:人看到红灯,默认动作就是站在原地,等待红灯结束。
 - 忽略:人看到红灯之后,不管红灯,继续往前走。
 - 自定义动作(信号的捕捉):人看到红灯后,开始唱歌。
 
进程收到一个信号后,它的处理动作只能在上面这三个动作中进行三选一。
五、ctrl+c 被解释成2号信号验证
5.1 signal------设置自定义捕捉方法
signal------设置进程对某个信号的自定义捕捉方法 :当进程收到 signum 信号的时候,去执行 handler 方法。

signum:信号的编号。handler:一个函数指针,指向信号的自定义捕捉动作。sighandler_t:一个函数指针类型,指向一个返回值为void参数为int的函数,这个参数值就是收到的信号。为什么要有这个参数呢?因为同一个自定捕捉方法,可以捕捉多个不同的信号,所以在该方法中需要知道是收到哪个信号才来执行的该方法。
signal 函数只需要设置一次,往后都有效。sighandler_t 函数只会在收到对应的信号后才会执行,假如程序一直没有收到对应的信号,那么该函数就一直不会被调用。
            
            
              cpp
              
              
            
          
          // mysignal
#include <iostream>
#include <signal.h>
#include <unistd.h>
using namespace std;
void myhandler(int signo)
{
    cout << "process get a signal: " << signo << endl;
}
int main()
{
    signal(2, myhandler);
    while(true)
    {
        cout << "Hello Linux" << endl;
        sleep(1);
    }
    return 0;
}
        
进程收到信号后的默认动作和自定义捕捉动作只能执行一个,设置了自定义捕捉动作,默认动作就不再执行了。
六、ctrl+c 是如何变成信号的?
由于进程无法直接访问硬件资源,所以键盘被按下,一定是操作系统先知道的。
OS 怎么知道键盘上有数据了?
最简单的方法就是,操作系统定期的去检查键盘文件。把键盘数据读到操作系统当中,本质上是将键盘外设(文件)中的数据,拷贝到内核中的文件页缓冲区中。因为操作系统中有大量的外设,如果都采用定期检查的方法,去判断外设中是否有数据是非常浪费时间的,因此这种方法不可取。在数据层面,CPU 是不会直接和外设打交道的,但是在控制层面,当外设有数据了,外设可以给 CPU 发送硬件中断 。 中断有自己的中断号,CPU 通过中断号来判断该中断是哪个外设发送的。之后 CPU 会以这个中断号为下标,去操作系统的中断向量表中执行对应的方法。 操作系统在将键盘文件中的数据拷贝到操作系统之前,会先判断输入的是数据还是控制,如果是控制,ctrl+c 就是控制,会被操作系统转换成2号信号发送给进程。

键盘和显示器有各自独立的文件页缓冲区:

七、异步、软中断
信号的产生和代码的执行是异步的,所谓异步就是在我们代码执行的任何时候都有可能收到信号。信号是进程之间事件异步通知的一种方式,属于软中断。
八、结语
今天的分享到这里就结束啦!如果觉得文章还不错的话,可以三连支持一下,春人的主页还有很多有趣的文章,欢迎小伙伴们前去点评,您的支持就是春人前进的动力!
