【Linux】你一定要知道的31种进程间信号


请添加图片描述

1 总览信号

  1. 信号还没有产生的时候,对于进程来说,应该知道信号产生之后应该有怎样的对应默认行为
    1. 信号产生的时候,进程应该知道信号对应的含义,操作系统中已经内置了信号的处理方案
    2. 进程应该可以识别信号的种类,因为操作系统中已经内置了信号的种类
    3. 信号随时随地都有可能产生,而信号的产生和进程运行是一种“异步关系”
  2. 当信号产生后,进程不一定要马上去处理信号,因为可能有优先级更高的事情要做
    1. 信号已经到来,暂时没有处理,需要在“合适”的时候处理
    2. 信号已经到来,但是没有立刻处理,在这个“时间窗口”内,进程需要暂时保存信号
  3. 当准备处理信号的时候,有不同的处理方式
    1. 默认行为(终止进程,暂停进程,继续运行等操作系统中内置的处理方式)
    2. 自定义行为(自己定义信号到来的进程需要做哪些操作)
    3. 忽略行为(不处理这个信号了)

查看信号的种类

在Linux使用kill -l查看所有的信号

![[Pasted image 20220208102254.png]]

一共有62种信号,其中131是一组信号,称之为“普通信号”,3464是一组信号,称之为“实时信号”,没有32,33两种信号。

更详细的信号内容可以使用man 7 signal查看

信号是如何记录的?如何发送的?

信号记录在进程的task_struct进程控制块中。并且使用位图来进程“是否”接收信号。其中位图中比特位的位置表示信号编号,比特位的内容表示是否收到信号。

进程收到信号其实就是进程控制块内的信号位图被修改了。而操作系统是进程的管理者,所以虽然信号发送的方式有很多种(kill命令或者使用按键),但是信号位图只会被操作系统修改的因此信号发送的本质就是操作系统直接去修改目标进程task_struct中的信号位图。

补充:
有两个可以杀死进程的方式

  • 使用kill -9 进程号的方式(发9号信号)
  • 使用killall 进程名称的方式

2 产生信号的方式

2.1 键盘组合键

ctrl + c组合:发送2号信号,SIGINT
ctrl + \组合:发送3号信号,SIGQUIT
ctrl + z组合:发送20号信号,SIGTSTP

注意:

  1. shell中可以同时运行一个前台进程和多个后台进程,但是键盘组合键只能发送给前台进程。
  2. 运行一个进程时后加一个&可以将进程放在后台运行。

相关命令:
进程 &:将进程后台后台运行
jobs:查看后台任务
fg:将后台任务放到前台

2.2 程序异常导致硬件问题

核心转储

当代码正常运行完毕,可以通过查看进程对应的退出码判断运行中的情况。
但代码运行中出错而异常退出时,通常都是通过调试来定位错误位置。在Linux中还有另一种方式:核心转储。即将进程在内存中的核心数据转而存储在磁盘上,形成core.pid文件。

在使用gdb调试的时候,就可以使用core-file core.pid命令直接定位跳转到出错的位置。这种调试方式是“事后调试”。

如何打开核心转储?

如果使用的是虚拟机,默认核心转储功能是自动打开的。但是如果使用的线上服务器,默认该功能是关闭的。

可以使用**ulimit -a命令查看系统资源**。

![[Pasted image 20220208111234.png]]

然后使用ulimit -c xxx设置core file size大小,就相当于打开核心转储功能了。

![[Pasted image 20220208111400.png]]

为什么进程会崩溃,程序会异常?

本质是因为收到了信号。当发生除零,野指针,越界访问的时候,都是因为收到了信号。进而进程执行默认行为。

为什么出现错误会收到信号?

当出现错误的时候,一定会在硬件上有所表现,而操作系统是软硬件的管理者,所以操作系统会识别到硬件错误,最终导致操作系统会向进程发送信号。

如:

  1. 当发生除零错误的时候,cpu中的状态寄存器中的状态会变化,操作系统识别到后将错误包装成信号的形式发送给对应的进程,找到进程pcb修改信号位图的第8号比特位,就相当于给进程发送8号信号了,然后进程就会执行8号信号对应的默认行为。
  2. 当发生野指针或者越界的时候,会先通过MMU(一种硬件单元)和页表配合使得虚拟地址和物理内存相互映射,但是发生错误时,MMU映射出错就会改变其状态信息,此时会被操作系统识别到,然后操作系统就会修改发生错误的进程中的pcb中的信号位图的第11位,相当于给进程发送了11号信号。

补充:其实语言级别中的try catch机制底层其实是类似的,也就是当发生错误的时候,操作系统发送给进程的信号被捕捉了,因此进程就没有执行信号的默认行为。

2.3 系统调用函数

kill
#include <sys/types.h>
#include <signal.h>

int kill(pid_t pid, int sig);
  • 作用
    • 给pid号进程发送sig号信号

使用kill()函数,模拟实现kill命令

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

void Usage(const char*proc)
{
   
	printf("Usage: %s pid signo\n");
}

int main(int argc, char* argv[])
{
   
	if (argc != 3)
	{
   
		Usage(argv[0]);
		return 1;
	}
	pid_t pid = atoi(argv[1]);
	int signo = aoti(argv[2]);
	kill(pid, signo);
	return 0;
}
raise
#include <signal.h>

int raise(int sig);
  • 作用
    • 进程给自己发送sig号信号

示例:

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

int main()
{
   
	// 一秒钟后会接收到2号信号
	while (true)
	{
   
		printf("hello process\n");
		sleep(1);
		raise(2);
	}
	return 0;
}
abort
#include <stdlib.h>

void abort();
  • 作用
    • 进程给自己发送6号信号SIGABRT,也就是强制终止进程,就算信号被捕捉了也会终止当前进程

abort()函数和exit()函数类似都会使得进程终止。但是abort是通过发送信号的方式终止终止进程,并且一定会调用成功。但是exit()可能会调用失败。

2.4 软件条件

软件条件产生信号就是不满足某种条件而发送的信号。

  1. SIGPIPE

在使用匿名管道通信的时候,如果文件的读端关闭的话,此时写端再写入也没有意义了,所以此时操作系统就会发送SIGPIPE信号给写端进程,终止该进程。

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hyzhang_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值