产生信号的方式

1.通过终端按键产生信号

core:核心转储(代码运行时出错,也有办法判定是什么原因出错,调试+核心转储:把进程在内存中的核心数据转储到磁盘上,core.pid:目的是为了调试,定位)
一般云服务器,线上生产环境,默认是关闭的

查看系统资源命令:ulimit -a
在这里插入图片描述

改变core file size:ulimit -c 数字
再运行程序后,ls,会出现一个数字,为核心转储的进程号

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

==作用:==事后调试:程序运行崩溃后,利用gdb调试,会准确定位到哪一个信号出现错误,并且程序哪一行有问题(gdb中:core-file core.进程号)
在这里插入图片描述
在这里插入图片描述

另外:
(1)为什么C/C++进程会崩溃?
本质就是因为收到了信号
(2)除0、野指针、越界,为什么会收到信号?
信号都是OS发送的,错误最终一定会在硬件层面上有所表现,进而被OS识别到

2.调用系统函数给目标进程发信号

有些信号不能被捕捉,比如9,有些捕捉,组合键使用可以显示是哪一个信号
(1)系统调用给进程发送信号停止进程:./myproc 进程号 信号数字(对应数字代表信号就执行该信号)
makefile文件

.PHONY:all
all:mykill myproc 

myproc:test.c
	gcc -o $@ $^

mykill:mykill.c
	gcc -o $@ $^
.PHONY:clean
clean:
	rm -f myproc mykill

mykill.c文件

#include<stdio.h>
#include<sys/types.h>
#include<signal.h>
#include<stdlib.h>
void Usage(const char *proc)
{
  printf("Usage:%s pid signo\n",proc);
}
int main(int argc,char *argv[])   //argv[]是参数存放数组
{
  if(argc != 3)  //main函数的参数只有2个
  {
    Usage(argv[0]);  //main第一个参数的起始地址传给Usage
    return 1;
  }
//字符转换为整型
  pid_t pid = atoi(argv[1]);
  int signo = atoi(argv[2]);
  
  kill(pid,signo);
  return 0;
}

test.c文件

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{
  while(1)
  {
    printf("I am a process,pid:%d\n",getpid());
    sleep(1);
  }
}

在这里插入图片描述
若把环境变量导入当前可执行程序目录下,可直接运行文件名

export PATH=$PATH:/home/sxl/code/Linux/...   //pwd可直接看当前所处目录

(2)调用者自己给自己发信号:raise
test.c文件

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<signal.h>
void handler(int signo)
{
  printf("get a signo:%d\n",signo);
}
int main()
{
  signal(2,handler);
  while(1)
  {
    printf("I am a process,pid:%d\n",getpid());
    sleep(1);
    raise(2); //每隔一秒钟,自己给自己发送一个2信号
  }
}

在这里插入图片描述

(3)abort:终止进程(也是自己给自己发信号,SIGABRT)
test.c文件

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<signal.h>
#include<stdlib.h>
void handler(int signo)
{
  printf("get a signo:%d\n",signo);
}
int main()
{
  signal(6,handler);
  while(1)
  {
    printf("I am a process,pid:%d\n",getpid());
    sleep(1);
   // raise(2); //每隔一秒钟,自己给自己发送一个2信号
    abort();
  }
}

在这里插入图片描述

3.由软件条件产生信号

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

test.c文件

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<signal.h>
#include<stdlib.h>

int main()
{
  alarm(1);  //1秒发送多少
  int count = 0;
  while(1)
  {
    printf("count is:%d\n",count++);
  }
}

在这里插入图片描述
改进:不打印(没有IO),直接进行输出count,极大提高了效率
test.c文件

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<signal.h>
#include<stdlib.h>
int count = 0;
void handler(int signo)
{
  printf("count:%d\n",count);
}
int main()
{
  signal(SIGALRM,handler);//信号捕捉,自定义信号,遇到SIGALRM,则输出count
  alarm(1);
  while(1)
  {
    count++;
  }
}

在这里插入图片描述

4.硬件异常产生信号

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

总结:
发送信号(经过OS向目标发送信号):
1.键盘组合键,产生信号
2.异常,产生信号
3.系统或者其它调用接口,产生信号
4.软件条件,SIGPIPE,产生信号

阻塞信号(block):比特位的位置代表信号编号,比特位的内容代表是否阻塞该信号

  1. 信号递达:执行信号的处理动作,处理信号递达的三种方式:自定义、忽略、默认
  2. 信号未决:信号从产生到递达之间的状态
  3. 阻塞:被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作

保存信号:
阻塞和忽略是不同的,只要信号被阻塞就不会被递达,而忽略是在递达之后可选的一种处理动作
忽略:本质是处理信号的一种方式
阻塞:本质上让信号不要递达,直到解除阻塞

位图:unsigned int(pending),发送信号,比特位改为1

handler_t:内部是一个函数指针数组
SIG_DFL:默认方式
SIG_IGN:信号忽略

在这里插入图片描述

处理信号:
合适的时候:由内核态切换为用户态的时候
用户态:执行自己的代码,系统所处的状态
内核态:自己的代码中,可能包含了系统调用,本质是内核的代码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值