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

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

目录

  • 信号是什么?
  • 信号是如何产生的?
    • 1.键盘输入
    • 2.程序(进程)异常
    • 3.系统调用
    • 4. 由软件条件来产生信号
  • 信号捕获
  • 总结:

信号是什么?

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

比如说:

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

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

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

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

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

信号是如何产生的?

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

1.键盘输入

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

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

#include<iostream>


int main()
{
  while(1)
  {
    printf("hello linux\n");
  }
  return 0;
}

然后我们运行程序,按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<stdio.h>
#include<signal.h>
#include<stdlib.h>

int main(int argc , char* argv[])
{
  if(argc != 3)
  {
    printf("./mykill pid signal");
    return 1;
  }

  kill(atoi(argv[1]),atoi(argv[2]));
  return 0;
}

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

在这里插入图片描述

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

int raise(int signo) 演示

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

#include<stdio.h>
#include<unistd.h>
#include<signal.h>

int main()
{
  int count = 0;
  while(1)
  {
    printf("hello linux\n");
    sleep(1);
    count ++;
    if(count == 5)
    {
      raise(9);
    }
  }
  return 0;
}

然后我们运行这个程序

在这里插入图片描述

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

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

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

还有 alarm函数 产生 SIGALRM信号。

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

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

unsigned int alarm(unsigned int seconds)

我们先测试一下该函数。

#include<stdio.h>
#include<unistd.h>
#include<signal.h>

int main()
{
  alarm(5);
  while(1)
  {
    printf("hello linux1\n");
    printf("hello linux2\n");
  }
  return 0;
}

运行程序

在这里插入图片描述

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

那么我们取消闹钟试试

#include<stdio.h>
#include<unistd.h>
#include<signal.h>

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;
}

然后我们执行。

在这里插入图片描述

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

信号捕获

信号的捕获函数:

#include <signal.h> //头文件

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

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

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

举个例子:

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

#include<stdio.h>
#include<unistd.h>
#include<signal.h>
#include<stdlib.h>

//处理信号函数
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;
}

然后我们测试一下。

在这里插入图片描述

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

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

在这里插入图片描述

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

#include<stdio.h>
#include<unistd.h>
#include<signal.h>
#include<stdlib.h>

//处理信号函数
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;
}

然后我们运行一下。

在这里插入图片描述

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

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

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

#include<stdio.h>
#include<unistd.h>
#include<signal.h>
#include<stdlib.h>

//处理信号函数
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;
}

我们试验一下。

在这里插入图片描述

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

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

总结:

信号的四种产生方式:

1.终端键盘输入

2.进程异常

3.系统调用

4.软件条件产生

信号捕获函数:

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

特殊信号:

9号信号无法被捕获!文章来源地址https://www.uudwc.com/A/0kB9e/

原文地址:https://blog.csdn.net/Lin5200000/article/details/132953584

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请联系站长进行投诉反馈,一经查实,立即删除!

h
上一篇 2023年09月18日 00:29
ERROR: Could not build wheels for hdbscan, which is required to install pyproject.toml-based project
下一篇 2023年09月18日 00:30