• 【Linux操作系统】信号的产生&&捕获


           🔥🔥 欢迎来到小林的博客!!
          🛰️博客主页:✈️林 子
          🛰️博客专栏:✈️ Linux
          🛰️社区 :✈️ 进步学堂
          🛰️欢迎关注:👍点赞🙌收藏✍️留言

    信号是什么?

    什么是信号? 信号是一种抽象概念,在我们的生活中就有各种各样的信号。 例如红绿灯,凌晨六点的鸡鸣声,你考试成绩不好时你爸妈的脸色。 这些都是信号。 而信号有一重要的特征就是,当这些信号出现时,你知道接下来会发生什么!然后采取相应的措施。

    比如说:

    红灯来了,你知道你不能过马路,所以你停止前进。

    凌晨六点鸡叫了, 你知道已经天亮了,你可以起床,也可以继续睡觉,这都是处理信号的一种方式。

    你考试成绩不好回家,你爸妈的脸色也可以告诉你,你爸妈现在心情很不开心,所以你知道你不能惹你爸妈生气。

    以上都是现实中的信号,那么在我们的计算机程序中,也有信号!

    而信号在产生之前,我们的程序就知道如何处理这个信号。

    信号是如何产生的?

    那么我们程序中的信号是如何产生的呢?

    1.键盘输入

    我们在键盘中有一些快捷键,比如 ctrl + c,会强制关掉当前前台运行的程序,本质是利用键盘输入,让OS向目标进程发送了2号信号。

    我们可以写个死循环程序演示一下。

    #include
    
    
    int main()
    {
      while(1)
      {
        printf("hello linux\n");
      }
      return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    然后我们运行程序,按ctrl + c ,程序会被终止。实际上是产生了信号。

    在这里插入图片描述

    2.程序(进程)异常

    当程序出现异常 , 例如 **空指针解引用,除0,等异常都会产生信号。 而本质是程序在执行中,发生了硬件错误。空指针解引用会发生内存错误, 除0会发生cpu计算错误。 所以就会产生信号,再由OS来处理这些信号。**这种情况就不演示了,因为这种异常在学习C语言的过程中想必大家都遇见过。

    3.系统调用

    我们可以用一些系统调用接口来产生信号,例如 我们命令行上的 kill 命令 。 本质就是系统调用int kill(pid_t pid, int signo) 函数来产生信号。还有 int raise(int signo) 函数来自己产生信号, 还有 void abort(void) 来产生 3号信号。

    **int kill(pid_t pid, int signo) 演示 **

    这个函数的第一个参数是一个进程的pid ,第二个参数是发送的参数,类似于我们 kill命令行。所以我们需要以命令行的方式 获取被发送信号进程的pid。

    我们写一个kill 函数,参数用我们命令传进去的 argv[1] 和 argv[2]

    #include
    #include
    #include
    
    int main(int argc , char* argv[])
    {
      if(argc != 3)
      {
        printf("./mykill pid signal");
        return 1;
      }
    
      kill(atoi(argv[1]),atoi(argv[2]));
      return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    然后我们执行一个进程,每秒打印一次 hello linux。然后用 mykill 对 mypro 发送9号信号!

    在这里插入图片描述

    这样我们就模拟实现了一个 kill 命令。本质就算调用了系统调用接口 kill。

    int raise(int signo) 演示

    我们让程序打印5次 hello linux 后调用这个函数,并传参数9进去。看看进程会不会收到9号信号。

    #include
    #include
    #include
    
    int main()
    {
      int count = 0;
      while(1)
      {
        printf("hello linux\n");
        sleep(1);
        count ++;
        if(count == 5)
        {
          raise(9);
        }
      }
      return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    然后我们运行这个程序

    在这里插入图片描述

    不难发现,五秒过后收到了9号信号,这就是自己产生信号的一种方式。

    4. 由软件条件来产生信号

    SIGPIPE是一种由软件条件产生的信号, 当我们在管道传输中,关闭了读端,而写端还在继续写入。那么就会产生SIGPIPE信号。

    还有 alarm函数 产生 SIGALRM信号。

    SIGPIPE信号暂且不演示,我们演示一下 alarm函数。

    alarm函数是一个闹钟函数,参数传一个秒数,表示定时多少秒,时间到了之后则会收到alarm信号。返回值则是剩余的秒数,就是当闹钟被中断时。返回剩余的秒数,参数为0时则终端闹钟。

    unsigned int alarm(unsigned int seconds)

    我们先测试一下该函数。

    #include
    #include
    #include
    
    int main()
    {
      alarm(5);
      while(1)
      {
        printf("hello linux1\n");
        printf("hello linux2\n");
      }
      return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    运行程序

    在这里插入图片描述

    我们发现五秒后收到信号。

    那么我们取消闹钟试试

    #include
    #include
    #include
    
    int main()
    {
      alarm(30); //注册闹钟
      int count = 0;
      while(1)
      {
        printf("hello linux1\n");
        printf("hello linux2\n");
        sleep(1);
        count++;
        if(count == 5)
        {
          int ret = alarm(0); //取消闹钟
          printf("ret = %d\n",ret); //打印剩余秒数
        }
      }
      return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    然后我们执行。

    在这里插入图片描述

    5s后闹钟取消了,返回了剩余的秒数,25秒。

    信号捕获

    信号的捕获函数:

    #include //头文件

    typedef void (*sighandler_t)(int); // 对函数指针typedef

    sighandler_t signal(int signum, sighandler_t handler); //函数

    signal函数的作用就是,如果接收到 signum , 那么就用handler函数去处理该信号。

    举个例子:

    我们执行下面代码,然后尝试用 kill命令对该进程发送2号信号。

    #include
    #include
    #include
    #include
    
    //处理信号函数
    void handler(int sign)
    {
      printf("get a sign :  %d\n", sign);
      exit(0);
    }
    
    int main()
    {
      signal(2,handler); //如果收到二号信号,那么用handler来处理
      while(1)
      {
        printf("hello world\n");
        sleep(1);
      }
      return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    然后我们测试一下。

    在这里插入图片描述

    我们发现最后输出了 get a sign 2 。 说明信号被捕捉了。

    我们用 kill -l 可以查看系统中的信号。

    在这里插入图片描述

    我们只看前面31个,那么我们可以把前面31个都用 handler 处理。然后进行一个 除0 操作,会收到几号信号。

    #include
    #include
    #include
    #include
    
    //处理信号函数
    void handler(int sign)
    {
      printf("get a sign :  %d\n", sign);
      exit(0);
    }
    
    int main()
    {
      for(int i = 1;  i<= 31 ; i++)
        signal(i,handler); //如果收到二号信号,那么用handler来处理
     
      sleep(5) ; 
      int a = 5 / 0;  //5秒之后制造一个信号
    
      return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    然后我们运行一下。

    在这里插入图片描述

    5 秒过后,我们程序没有报错,相反是打印了 收到 8号信号的信息。而 8 号对应的就是SIGFPE信号,浮点数错误。在这里插入图片描述

    现在知道信号可以捕获,那么问题来了, 9 号信号能否捕获呢? 我们可以用下面代码试一下。

    我们写了一个死循环代码,然后用命令行发送9号信号,看看是否会被捕获。

    #include
    #include
    #include
    #include
    
    //处理信号函数
    void handler(int sign)
    {
      printf("get a sign :  %d\n", sign);
      exit(0);
    }
    
    int main()
    {
      for(int i = 1;  i<= 31 ; i++)
        signal(i,handler); //如果收到二号信号,那么用handler来处理
     
      while(1)
      {
        sleep(1);
        printf("hello world\n");
      }
      return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    我们试验一下。

    在这里插入图片描述

    最后我们发现, 9 号信号没有被捕获,进程还是被杀死了。

    所以得出结论: 9号信号无法被捕获,操作系统不允许有金刚不坏的进程存在!

    总结:

    信号的四种产生方式:

    1.终端键盘输入

    2.进程异常

    3.系统调用

    4.软件条件产生

    信号捕获函数:

    sighandler_t signal(int signum, sighandler_t handler),其中 handler 是一个函数指针类型

    特殊信号:

    9号信号无法被捕获!

  • 相关阅读:
    代码随想录刷题| 多重背包理论基础、背包问题的总结
    快速幂算法
    给Python项目创建一个虚拟环境(enev)
    Day16--购物车页面-演示效果并创建编译模式
    解读省钱兄情侣飞行棋小程序系统的魅力所在
    Arctic——流式湖仓系统
    linux 安装R 环境(最新)
    《昇思25天学习打卡营第5天|10使用静态图加速》
    Ubuntu20详细安装步骤
    Go语言超全详解(入门级)
  • 原文地址:https://blog.csdn.net/Lin5200000/article/details/132953584