Linux---进程信号

1. Linux信号的基本概念

1.1 生活角度的信号

这个生活的故事,其中包含了信号的方方面面,只要好好理解这个过程,就能够融会贯通到我们接下来的信号理解中。

  1. 你在网上买了很多件商品,再等待不同商品快递的到来。但即便快递没有到来,你也知道快递来临时,你该怎么处理快递。也就是你能“识别快递”
  2. 当快递员到了你楼下,你也收到快递到来的通知,但是你正在打游戏,需5min之后才能去取快递。那么在在这5min之内,你并没有下去去取快递,但是你是知道有快递到来了。也就是取快递的行为并不是一定要立即执行,可以理解成“在合适的时候去取”。
  3. 在收到通知,再到你拿到快递期间,是有一个时间窗口的,在这段时间,你并没有拿到快递,但是你知道有一个快递已经来了。本质上是你“记住了有一个快递要去取”
  4. 当你时间合适,顺利拿到快递之后,就要开始处理快递了。而处理快递一般方式有三种:1.执行默认动作(幸福的打开快递,使用商品)2. 执行自定义动作(快递是零食,你要送给你你的女朋友)3.忽略快递(快递拿上来之后,扔掉床头,继续开一把游戏)
  5. 快递到来的整个过程,对你来讲是异步的,你不能准确断定快递员什么时候给你打电话

1.2 注意

在一个bash当中,只允许一个前台进程

  1. Ctrl-C产生的信号只能发给前台进程。一个命令后面加个&可以放到后台运行,这样Shell不必等待进程结束就可以接受新的命令,启动新的进程。
  2. Shell可以同时运行一个前台进程和任意多个后台进程,只有前台进程才能接到像Ctrl-C这种控制键产生的信号
  3. 前台进程在运行过程中用户随时可能按下Ctrl-C而产生一个信号,也就是说该进程的用户空间代码执行到任何地方都有可能收到SIGINT信号而终止,所以信号相对于进程的控制流程来说是异步(Asynchronous)的。

1.3 用kill -l命令

可以察看系统定义的信号列表,其中前1-31是普通信号, 34-64为实时信号
在这里插入图片描述

  1. 每个信号都有一个编号和一个宏定义名称,这些宏定义可以在signal.h中找到,例如其中有定义#define SIGINT 2
  2. 编号34以上的是实时信号,只讨论编号34以下的信号,不讨论实时信号。

1.4 信号处理常见方式

  1. 忽略此信号。
  2. 执行该信号的默认处理动作。
  3. 提供一个信号处理函数,要求内核在处理该信号时切换到用户态执行这个处理函数,这种方式称为捕捉(Catch)一个信号

2. 信号产生的一般方式

信号产生以后,不是立即被处理的。 所以信号在产生时和信号处理时之间是有一个时间窗口的。
在这里插入图片描述

2.1 通过终端按键产生信号

通过键盘产生。

Core Dump(核心转储)
操作系统在你这个进程要崩溃之前,把重要的信息给你存储一份,然后放到磁盘上。简单点说:这个就好比喻windows下当你出现程序崩溃的时候给你谈出来的窗口,只不过这个是在Linux下的窗口。

但是在云服务器上是默认关闭的,可以通过ulimit -a来进行查看资源的上限,那么为什么会关闭这个core file size呢,那是应为你所生成的core.pid文件是占有一定的磁盘大小的,如果你一直不停的出错,那么这种方式将会消耗掉大量的磁盘资源。是否要生成core dump文件在waitpid哪里有一个statue的参数,其中最后一个字节的最有一个bite位为core dump标志位,如果为1则表示需要生成,如果为0表示不需要生成。
在这里插入图片描述

2.2 调用系统函数向进程发信号

#include <signal.h>
int kill(pid_t pid, int signo);第一个参数代表你要给那个进程发信号,第二个参数发多少号信号
int raise(int signo);
这两个函数都是成功返回0,错误返回-1。

kill命令是调用kill函数实现的。kill函数可以给一个指定的进程发送指定的信号。

#include<stdio.h>
#include<signal.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>
void handler(int signo)
{
  printf("catch a signal : %d\n",signo);
}
int main(int argc,char *argv[])
{

  signal(2,handler); //这个handler是一个函数指针,指向的是一个函数,这个函数就是对于这个信号的处理方法
  //这里需要加强强制转换和转换的概念
  //atoi可以把字符串转化为整型
  //argv[0] 代表的是可执行程序
  if(argc == 3)
  {
    kill(atoi(argv[1]),atoi(argv[2]));  
  }

  return 0;
}

在这里插入图片描述

raise函数可以给当前进程发送指定的信号(自己给自己发信号)。但是进过下面的代码发现,还是进程直接被杀掉了。所以9号信号不允许捕捉(不能够自定义)

#include<stdio.h>
#include<signal.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>
void handler(int signo)
{
  printf("catch a signal : %d\n",signo);
}
int main(int argc,char *argv[])
{

  signal(9,handler); //这个handler是一个函数指针,指向的是一个函数,这个函数就是对于这个信号的处理方法

  while(1)
  {
    sleep(1);
    raise(9);
  }
  return 0;
}

在这里插入图片描述

abort函数使当前进程接收到信号而异常终止。

#include <stdlib.h>
void abort(void);
就像exit函数一样,abort函数总是会成功的,所以没有返回值。

#include<stdio.h>
#include<signal.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>
void handler(int signo)
{
  printf("catch a signal : %d\n",signo);
}
int main(int argc,char *argv[])
{

  signal(6,handler); //这个handler是一个函数指针,指向的是一个函数,这个函数就是对于这个信号的处理方法

  while(1)
  {
    sleep(1);
    abort();
  }
  return 0;
}

在这里插入图片描述

2.3 由软件条件产生信号

这个条件在发生时没有什么硬件条件发生,也不是我们主动调用系统调用,而是或许时间片到了而触发的。

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

#include<stdio.h>
#include<signal.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>
void handler(int signo)
{
  printf("catch a signal : %d\n",signo);
}
int main(int argc,char *argv[])
{
  alarm(1);
  int count = 0;
  while(1)
  {
    printf("%d\n",count++);
  }
}

在这里插入图片描述

2.4 硬件异常产生信号

硬件异常被硬件以某种方式被硬件检测到并通知内核,然后内核向当前进程发送适当的信号。例如当前进程执行了除以0的指令,CPU的运算单元会产生异常,内核将这个异常解释 为SIGFPE信号发送给进程。再比如当前进程访问了非法内存地址,MMU会产生异常,内核将这个异常解释为SIGSEGV信号发送给进程。

指针访问异常时

int main(int argc,char *argv[])
{
  printf("begin...\n");

  int *p = NULL;
  *p = 100;
  printf("end...\n");

  return 0;
}

会报错Segmentation fault
在这里插入图片描述
除0操作时,会报错Floating point exception

int main(int argc,char *argv[])
{
  printf("begin...\n");

  int i = 1/0;

  printf("end...\n");

  return 0;
}

在这里插入图片描述

由此可以确认,我们在C/C++当中除零,内存越界等异常,在系统层面上,是被当成信号处理的。

所有的信号,都必须经过OS的手发出,上面的四种情况都是信号触发的条件,但是最终都要交给OS,来帮我们杀死进程,因为进程是由操作系统所管理的。

在每个PCB中都有一个信号位图,只需要将对应的bite位置1就可以触发相应的那个信号。这个就是信号的保存方法

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值