Linux信号

一、信号

​ 是一种异步通知的方式,即信号的产生和代码的执行是异步的;

​ 信号的整个生命周期包括信号的产生、信号的保存和信号的处理;

1.1预备知识

​ 1.计算机能够识别信号是因为设置进程时内置了信号的实现,所以可以识别信号,并且对不同的信号做出不同的响应;

​ 2信号产生和信号处理之间的时间窗口一定能够对信号进行保存;

​ 3.信号的处理方式:默认动作、忽略动作、自定义动作(先对信号进行捕捉,然后跳转到自定义处理方式中去);

c++ 复制代码
#include <signal.h>
typedef void (*sighandler_t)(int);//定义一个函数指针类型;sighandler_t handler叫做函数指针定义;会将信号的编号传递给自定义函数;
sighandler_t signal(int signum, sighandler_t handler);//系统调用
//第一个参数是信号的编号,第二个参数是信号的处理方式;即先对信号进行捕捉,然后根据函数指针进行处理;
//默认只有main执行流,程序执行期间不涉及多线程只允许有一个执行流;
//默认动作会创建了一个执行流,然后跳转执行流去执行默认行为;忽略行为是一般不创建执行流继续执行;自定义动作是创建执行流,跳转执行流去执行自定义动作;

​ 4.信号处理方式三选一;调用一次函数,整个进程的生命周期都有效;信号捕捉满足条件后才会执行自定义处理方式;函数指针的参数必须是int是因为要支持不同的信号支持同一个处理方法,所以需要将参数传进来,用来区分信号;

1.2信号的使用场景

​ ctrl+c可以杀死前台进程;一次登陆中,一个终端会配一个bash,且只允许一个前台进程,多个后台进程;前台进程本质就是允许获取键盘输入;如果./程序 &之后就会将程序变成后台程序,而键盘输入发送给的是前台进程,程序就无法使用ctrl+c来杀死,只能用kill发送9号信号杀死;进程不&默认是以前台进程的方式执行,而&后就以后台方式执行,这是默认bash进程会变成前台进程,因为至少要保证存在一个前台进程来获取键盘输入;

​ bash对信号进行了特殊处理;其他进程获取了键盘输入识别为信号,对信号进行了处理;ctrl+c被识别成了2号信号,执行默认动作终止自己;

1.3外设与CPU数据交互

​ 键盘按下后,操作系统最先知道,操作系统将外设的数据刷新到内存中的文件页缓冲区,然后再刷新到应用层;键盘和CPU的针脚是直接相连的,是可以发送控制信号的; 冯诺依曼体系结构规定,数据必须先载入到内存里,才能进入CPU进行运算,所以需要外设发送硬件中断(发送高低电平),通知操作系统进行将数据拷贝到内存中去(此时CPU被中断,去执行其他行为,完成后然后返回从中断的地方执行),而不是操作系统轮询检查;每一个中断都有中断号的概念,这样操作系统就可以知道是哪个外设发送了中断,需要进行将数据拷贝到内存中去;CPU能够记录数据是因为有寄存器,而寄存器的保存数据本质上是对寄存器进行了充放电;这样触发了硬件中断,就会在CPU内部寄存器保存中断号;

​ 软件方面,操作系统启动时会在开头部分设置一个中断向量表,存放着直接访问外设的方法;当CPU寄存器收到了中断号,操作系统就会以中断号在向量表中查找方法,执行方法将外设的数据拷贝到内存中的文件页缓冲区当中;对于输入的数据操作系统会做出判断,正常输入就将数据刷到文件页缓冲区,如果是组合键ctrl+c就转换为2号信号发送给进程,而不是文件页缓冲区;

​ 信号的设计就是模拟的硬件中断;

​ 总结:1.外设是基于硬件中断工作的,会向CPU发送控制中断,有专门的中断号;2.操作系统根据中断号查找中断向量表的外设方法来将外设的数据拷贝到内存中打开的外设文件的文件页缓冲区,然后让进程使用;3.键盘输入,如一般的输入被操作系统识别为正常信息发送到文件缓冲区,而特殊的输入如ctrl+c被转换成了2号信号发送给了进程中内置的信号实现,这样就实现了外设的数据交互;

shell 复制代码
pidof 文本关键词
#显示含有文本关键词的所有进程pid
xargs 命令(kill -9)
#使用重定向多次执行命令;

1.4Linux中的信号

​ 没有32、33信号;

​ 其中1-31是普通信号,34-64为实时信号;普通信号不需要被立即处理,而实时信号必须立即处理;SIGINT(interrupt)一定是宏本质上是一个数字

二、信号的产生

2.1产生的方式

​ 都是有操作系统发送给进程的;

1.键盘组合键

​ 硬件中断,数据拷贝,被操作系统识别,发送信号;

​ ctrl+c---2号信号,ctrl+\---3号信号,ctrl+z---19号信号,注意不是所有的信号都可以被捕捉 ,信号9、19即杀进程和暂停进程不可以被捕捉;这样的设计是防止恶意程序破坏,但是信号全部被捕捉自定义处理,不可以杀死进程;

2.kill命令

​ 软件执行中,操作系统发信号;

shell 复制代码
kill -signo pid

3.系统调用

​ 软件执行中,操作系统发信号;

c++ 复制代码
#include <sys/types.h>
#include <signal.h>
//1.kill
int kill(pid_t pid, int sig);//给任意进程发送指定信号;
//2.raise
#include <signal.h>
int raise(int sig);//给调用方发送指定信号;相当于kill(getpid(),signo);
//3.abort
#include <stdlib.h>
void abort(void);//当前进程异常终止使用的是6号信号;
//除了发送6号信号的动作之外,还进行了特殊动作,等信号被自定义处理完,返回原执行流时,还会执行终止;

4.异常:(和语言的异常没关系)

​ 硬件问题被操作系统识别,发送信号;

​ 终止分为:Term、Core;如果进程发生异常没有退出,操作系统会不停地向进程发送信号;

​ 除零异常发送8号信号,段错误(越界访问)异常发送11号信号;

​ 要注意在CPU中各种寄存器的值属于进程的上下文,进程切换就将数据都带走了,不会影响其他进程,从调度上保证了进程的独立性,而且操作系统管理的是软硬件资源,对CPU也要进行管理。

​ 除零错误:CPU中有进行运算的寄存器和表示状态的寄存器;状态寄存器有一个溢出标志位,默认是0,当除零错误发生,溢出标志位由0变1,就可以被操作系统识别异常;

​ 野指针问题:页表中是虚拟地址和物理地址的映射,操作系统并不会直接查表,比较费时间;而是使用MMU内存管理单元来完成查表;根据虚拟地址,页表,MMU来查找此虚拟地址对应的物理地址是否存在,最终无法转换成物理地址就是野指针问题;即内存的访问必须能够转换成物理地址才允许访问;转换失败会将虚拟地址存放到一个寄存器中,操作系统识别发送信号;

​ 如果异常自定义处理,进程没有退出,进程切换但是上下文不变,即硬件问题还在,就会导致操作系统不停地发信号;

​ 总结:1.出现异常,会转化成为硬件问题,然后被操作系统识别到,然后进行处理;2.虽然是硬件问题,但是是进程的上下文,所以进程切换后并不会CPU的正常运行;3.硬件问题没有权限进行修改,所以循环发送信号;4.自定义处理只是为了和面向对象的异常(对信号进行了封装)一样使得上层能够知道存在问题,且问题处于什么位置,并不会让上层进行处理;

5.软件行为/异常

​ 软件资源出现异常或者软件资源达成某种条件,操作系统发送信号;

​ 1.管道写端正常,读端关闭,操作系统就会发送13号信号;而有的是返回错误码的形式;

​ 2.alarm设定一个闹钟;时间一到就会发送14信号;操作系统会对闹钟进行管理,其结构体中必须要进程控制块的指针来表示是哪一个进程设置的,闹钟结束时间,使用时间戳和秒数表示未来结束时间,通过比较当前时间和结束时间来决定是否发送信号;使用堆来适配容器进行升序排序,使得快要结束的闹钟优先和当前时间比较;

c++ 复制代码
#include <unistd.h>
unsigned int alarm(unsigned int seconds);//设置一次就发送一次,设置定时任务应该自定义捕捉,处理完之后继续设置闹钟;
//参数表示多少秒之后发送14信号,默认是终止动作;
//返回值表示本次闹钟设置时上次闹钟剩余的时间;

2.2core

​ 进程等待waitpid的输出型参数使用了低16位,其中从低到高,第8位为core dump标志位,用来表征是Term还是Core(终止加核心转储)方式来终止;0/1表示Term/Core的方式i终止;

​ 默认云服务器的core功能是被关闭的;使用ulimit -a可以查看,ulimit -c 10240将大小修改开启core功能;这时可以看到core dump标志位为1;一旦打开了core功能,就会在使用core终止时生成core.pid文件,即将进程在内存中的运行信息dump(转储)下来,此文件较大;核心转储就是指,将进程异常退出的信息保存在当前目录下。运行时错误,方便定位错误位置,可以使用gdb调试获取,如进入gdb,然后输入core-file core.pid,就会直接定位到错误位置,这种方式叫做事后调试;

​ 云服务器关闭core功能是因为,代码异常挂掉通常是由运维工程师将服务重新启动,但是如果大公司服务器集群挂了,由运维工程师手动启动会导致服务很长一段时间内无法响应,所以服务挂掉会进行很多自动化运维手段,1.自定检测服务出问题;2.赶紧恢复服务,然后基于日志查看问题;如果core功能开启,这些运维手段在重启时会产生core文件,如果重启多次就导致了问题从服务挂掉转变成了磁盘空间不足的问题,进而导致更严重的问题操作系统直接挂掉,所以core功能在线上一般都要被禁掉,保证服务重启有效;

相关推荐
C-cat.1 分钟前
Linux|环境变量
linux·运维·服务器
yunfanleo16 分钟前
docker run m3e 配置网络,自动重启,GPU等 配置渠道要点
linux·运维·docker
糖豆豆今天也要努力鸭1 小时前
torch.__version__的torch版本和conda list的torch版本不一致
linux·pytorch·python·深度学习·conda·torch
烦躁的大鼻嘎1 小时前
【Linux】深入理解GCC/G++编译流程及库文件管理
linux·运维·服务器
乐大师1 小时前
Deepin登录后提示“解锁登陆密钥环里的密码不匹配”
运维·服务器
ac.char1 小时前
在 Ubuntu 上安装 Yarn 环境
linux·运维·服务器·ubuntu
敲上瘾1 小时前
操作系统的理解
linux·运维·服务器·c++·大模型·操作系统·aigc
长弓聊编程1 小时前
Linux系统使用valgrind分析C++程序内存资源使用情况
linux·c++
cherub.1 小时前
深入解析信号量:定义与环形队列生产消费模型剖析
linux·c++
梅见十柒2 小时前
wsl2中kali linux下的docker使用教程(教程总结)
linux·经验分享·docker·云原生