Linux_进程信号_7

文章目录

  • 1.什么是信号
  • 2.信号列表
  • 3.信号处理常见方式
  • 4.信号的存储
  • 5.信号产生前-中-后
    • 1.信号产生前
    • 2.信号产生中
  • 6产生信号
    • 1.signal
    • 2.kill
    • 3.raise
    • 4.abort
    • 5.alarm
    • 6.硬件异常
  • 7.core dump
  • 8.信号产生中
    • 1. sigset_t(数据类型)
    • 2.信号集操作函数
      • 1.sigprocmask
      • 2.sigpending
      • 3.sigaction
  • 9.内核态和用户态


1.什么是信号

生活中有哪些信号?
红绿灯,铃声,闹钟…

我们是如何得知这些东西?
有人教(能够认识这些场景下的信号以及所表示的含义)——识别信号

我们找到对应的信号产生时,要做什么?
我们早就知道了,信号产生之后,要做什么,即便当前信号还没有产生。——知道信号的处理方法

只有具备以上两种能力才具有——处理信号的能力

信号是给进程发送的,进程要具备处理信号的能力
1.该能力一定是预先已经早就有了的
2.进程能够识别对应的信号
3.进程能够处理对应的信号

对于进程来讲,即便是信号还没有产生,我们进程已经具有识别和处理这个信号的能力了

2.信号列表

用kill -l 命令可以查看系统定义的信号列表
在这里插入图片描述
信号被分成两批,1~31是普通信号,34 ~64是实时信号。我们要学习就是1 ~ 31。

3.信号处理常见方式

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

4.信号的存储

进程是如何记住信号的,在哪里保存信号的?
1.有没有产生 —— 比特位的内容1/0
2.什么信号产生——比特位的位置
在进程的PCB中信号的位图中保存
struct task_struct{
uint32_t sig; //位图, 0000 0010 ——号2号信号
}
PCB是不是内核数据结构?
是的,所以只有OS有这个权利,能直接修改这个task_struct内的数据位图。

5.信号产生前-中-后

信号其他相关常见概念
实际执行信号的处理动作称为信号递达(Delivery)
信号从产生到递达之间的状态,称为信号未决(Pending)。
进程可以选择阻塞 (Block )某个信号。
被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作.
注意,阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。

1.信号产生前

用户层产生信号的方式
1.键盘产生——谁给进程发送的信号?OS把进程的PCB里的sig第几位置为1,就完成信号发送。
2.通过系统接口完成对进程发送信号的过程
3.软件条件
4.硬件产生信号

2.信号产生中

信号在内核中的表示
在这里插入图片描述

6产生信号

1.signal

在这里插入图片描述

作用:设置对信号的处理


原型: sighandler_t signal(int signum, sighandler_t handler);


参数:
signum: 要捕捉的信号
handler:函数指针,当捕捉到信号,就调用该函数


返回值:


样例:对SIGINT(2)(按ctrl+c就是向进程发送2号信号)设置

#include<iostream>
#include<signal.h>
#include<unistd.h>
using namespace std;void handler(int signo)
{cout<<"我是一个进程,刚刚获取了一个信号:"<<signo<<endl;
}int main()
{signal(SIGINT,handler);sleep(3);cout<<"进程已经设置完了"<<endl;sleep(3);while(true){cout<<"我是一个正在运行的进程:"<<getpid()<<endl;sleep(1);}return 0;
}

在这里插入图片描述
如果不向进程发送2号信号,就不会调用自定义函数。
ctrl+c :本质就是给前台进程产生了2号信号发送给目标进程,目标进程默认对2号信号的处理是终止自己,刚刚代码更改了对2号信号的处理,设置了用户自定义处理方法。
注:9号信号不能被设置。

2.kill

在这里插入图片描述
作用:向指定进程发送指定信号


原型: int kill(pid_t pid, int sig);


参数:
pid :进程id
sig:要发送的信号


返回值:
成功返回0
失败返回-1


样例:写一个程序,对进程发送信号

#include<iostream>
#include<signal.h>
#include<unistd.h>
#include<sys/types.h>
using namespace std;int main(int argc,char* argv[])
{if(argc!=3){cout<<argv[0]<<" -信号  进程pid"<<endl;return 1;}pid_t pid = atoi(argv[2]);int sig = atoi((argv[1]+1));ifkill (pid,sig)==-1){cerr<<"kill error"<<endl;}return 0;
}

在这里插入图片描述

3.raise

在这里插入图片描述
作用:给自己发信号


原型: int raise(int sig);


参数:
sig:信号


返回值:
成功返回0
失败返回非零


发送2号信号

raise(2);

4.abort

在这里插入图片描述
作用:向自己发送SIGABRT(6),终止进程


原型:void abort(void);


abort();

注:6号信号可以被捕捉,但依旧要被终止进程。

5.alarm

在这里插入图片描述
作用:x秒后向进程发送SIGALRM(14)信号


原型: unsigned alarm(unsigned seconds);


参数:
seconds : 设定多少秒


alarm(1);

6.硬件异常

崩溃的本质是什么?
进程崩溃的本质,是该进程收到了异常信号!
为什么会崩溃?
因为硬件异常,而导致OS向目标进程发送信号,进而导致进程终止的现象!
除零错误:CPU内部有状态寄存器,当我们除0的时候,CPU内的状态寄存器会被设置成为有报错:浮点数越界
CPU的内部寄存器(硬件),OS就会识别到CPU内有报错:
1.谁干的
2.什么报错(OS->构建信号)->目标进程发送信号->目标进程在合适的时候->处理信号->终止信号

越界&&野指针:我们在语言层面使用的地址(指针),虚拟地址->物理地址->物理内存->读取对应的数据
如果虚拟地址有问题,地址转化的工作是由(MMU(硬件)+页表(软件)),转化过程就会引起问题,表现在硬件MMU上,OS发现硬件出现问题
1.谁干的
2.什么报错(OS->构建信号)->目标进程发送信号->目标进程在合适的时候->处理信号->终止信号

7.core dump

之前在进程等待提到,进程被有些信号所杀,会将core dump设为1,并产生核心转储。
在这里插入图片描述
注:如果没有被置1且没有生产核心转储,ulimit -a查看core文件大小,并设置core的大小
在这里插入图片描述
上图就是core文件为0,进程异常不会生产核心转储。

ulimit -c 100000

在这里插入图片描述

#include<iostream>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdio.h>
using namespace std;
int main()
{pid_t id = fork();if(id==0){int a  =10;a/=0;exit(1);}int status = 0;waitpid(id,&status,0);printf("exitcode:%d ,signo:%d, core dump flag: %d\n",(status>>8)&0xFF,status&0x7F,(status>>7)&0x1);return 0;
}

在这里插入图片描述
被信号所杀,core dump 被设置1后,会把进程在运行中,对应的异常上下文数据,core dump到磁盘上,方便调试(要debug版本编译带上-g)。
下面是调试的过程:
在这里插入图片描述

8.信号产生中

在这里插入图片描述
pending:表示是否收到信号
block:是否阻塞信号的抵达

每个信号都有两个标志位分别表示阻塞(block)和未决(pending),还有一个函数指针表示处理动作。信号产生时,内核在进程控制块中设置该信号的未决标志,直到信号递达才清除该标志。在上图的例子中,SIGHUP信号未阻塞也未产生过,当它递达时执行默认处理动作。

SIGINT信号产生过,但正在被阻塞,所以暂时不能递达。虽然它的处理动作是忽略,但在没有解除阻塞之前不能忽略这个信号,因为进程仍有机会改变处理动作之后再解除阻塞。

SIGQUIT信号未产生过,一旦产生SIGQUIT信号将被阻塞,它的处理动作是用户自定义函数sighandler。如果在进程解除对某信号的阻塞之前这种信号产生过多次,将如何处理?POSIX.1允许系统递送该信号一次或多次。Linux是这样实现的:常规信号在递达之前产生多次只计一次,而实时信号在递达之前产生多次可以依次放在一个队列里。本章不讨论实时信号

1. sigset_t(数据类型)

未决和阻塞标志可以用相同的数据类型sigset_t来存储,sigset_t称为信号集。

2.信号集操作函数

#include <signal.h>
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset (sigset_t *set, int signo);
int sigdelset(sigset_t *set, int signo);
int sigismember(const sigset_t *set, int signo);

函数sigemptyset初始化set所指向的信号集,使其中所有信号的对应bit清零,表示该信号集不包含 任何有效信号。
函数sigfillset初始化set所指向的信号集,使其中所有信号的对应bit置位,表示 该信号集的有效信号包括系统支持的所有信号。
注意,在使用sigset_ t类型的变量之前,一定要调 用sigemptyset或sigfillset做初始化,使信号集处于确定的状态。初始化sigset_t变量之后就可以在调用sigaddset和sigdelset在该信号集中添加或删除某种有效信
上面四个函数都是成功返回0,出错返回-1。
sigismember是一个布尔函数,用于判断一个信号集的有效信号中是否包含某种 信号,若包含则返回1,不包含则返回0,出错返回-1

1.sigprocmask

作用:可以读取或更改进程的信号屏蔽字(阻塞信号集)


原型:int sigprocmask(int how, const sigset_t *set, sigset_t *oset);


参数:
how :做什么操作
在这里插入图片描述
set:要被设置的信号集
oset:老的信号集的返回


返回值:成功为0,失败为-1

2.sigpending

在这里插入图片描述

作用:获取当前进程的pengind信号集。


原型: int sigpending(sigset_t *set);


参数:
set:字符集


返回值:成功为0,失败为-1

#include<iostream>
#include <signal.h>
#include<unistd.h>
using namespace std;
static void showPending(sigset_t* pendings)
{ for(int sig=1;sig<=31;sig++){if(sigismember(pendings,sig)){cout<<"1";}else{cout<<"0";}}cout<<endl;
}void handler(int signo)
{cout<<"我是一个进程,刚刚获取了一个信号:"<<signo<<endl;
}int main()
{//屏蔽2号信号sigset_t bsig,osig;sigisemptyset(&bsig);sigisemptyset(&osig);sigaddset(&bsig,2);sigprocmask(SIG_SETMASK,&bsig,&osig);signal(2,handler);//不断的获取当前进程的pending信号集sigset_t pendings;while(true){//清空信号集sigemptyset(&pendings);//获取当前进程的pending信号集if(sigpending(&pendings)==0){//打印当前进程的pending信号集showPending(&pendings);}sleep(1);}return 0;
}

在这里插入图片描述

3.sigaction

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

#include<iostream>
#include <signal.h>
#include<unistd.h>
using namespace std;void handler(int signo)
{cout<<"我是一个进程,刚刚获取了一个信号:"<<signo<<endl;
}int main()
{struct sigaction act,oact;act.sa_handler =handler;act.sa_flags = 0;sigemptyset(&act.sa_mask);sigaction(2,&act,&oact);while(true){sleep(1);}return 0;}

在这里插入图片描述

9.内核态和用户态

进程处理信号,不是立即处理的,而是合适的时候?
合适的时候是指当进程从内核态,切换回用户态的时候,进行信号的检查和处理

每个进程都有进程地址空间,用户空间有着对应的页表是用户级的,而且大家用户级页表都不一样,地址空间里还有1G为内核空间,这个空间也有着对应的页表是内核级的,而且所有进程共享一份。

无论进程怎么切换,我们都可以找到内核的代码和数据,前提是你只要有能够有权利访问。
当前进程如何具备权利,访问这个内核页表,乃至访问内核数据?
要进行,身份切换。
进程如果是用户态的——只能访问用户级页表
进程如果是内核态——访问内核级和用户级页表

如何知道我是用户态还是内核态?
CPU内部有对应的状态寄存器CR3,有比特位标识当前进程状态
0:内核态
3:用户态

内核态VS用户态
内核态可以访问所有代码和数据——具备更高权限
用户态只能访问自己的

我们的程序会无数次直接或者间接的访问系统级软硬件资源(管理者是OS),本质上,你并没有自己去操作这些硬件资源,而是必须通过OS->无数次陷入内核(1.切换身份2.切换页表)->调用内核的代码->完成访问的动作->结果返回给用户(1.切换身份2.切换页表)->得到结果

信号的捕捉过程:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/593597.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

书生浦语训练营2期-第二节课笔记作业

目录 一、前置准备 1.1 电脑操作系统&#xff1a;windows 11 1.2 前置服务安装&#xff08;避免访问127.0.0.1被拒绝&#xff09; 1.2.1 iis安装并重启 1.2.2 openssh安装 1.2.3 openssh服务更改为自动模式 1.2.4 书生浦语平台 ssh配置 1.3 补充&#xff08;前置服务ok…

八数码(bfs做法)非常详细,适合新手服用

题目描述&#xff1a; 在一个 33 的网格中&#xff0c;1∼8这 8 个数字和一个 x 恰好不重不漏地分布在这 33 的网格中。 例如&#xff1a; 1 2 3 x 4 6 7 5 8在游戏过程中&#xff0c;可以把 x 与其上、下、左、右四个方向之一的数字交换&#xff08;如果存在&#xff09;。 我…

注解(Annotation)

文章目录 1 注解概述1.1 什么是注解1.2 注解与注释1.3 注解的重要性 2 常见的Annotation作用3 三个最基本的注解3.1 Override3.2 Deprecated3.3 SuppressWarnings 4 元注解5 自定义注解的使用5.1 声明自定义注解5.2 使用自定义注解5.3 读取和处理自定义注解 6 JUnit单元测试6.1…

【图论】【基环内向树】【广度优先】【深度优先】2127. 参加会议的最多员工数

作者推荐 视频算法专题 本文涉及知识点 图论 基环内向树 LeetCode2127. 参加会议的最多员工数 一个公司准备组织一场会议&#xff0c;邀请名单上有 n 位员工。公司准备了一张 圆形 的桌子&#xff0c;可以坐下 任意数目 的员工。 员工编号为 0 到 n - 1 。每位员工都有一位…

设计模式——工厂模式01

工厂模式 定义&#xff1a;工厂模式是创建子类实例化对象的一种方式&#xff0c;屏蔽了创造工厂的内部细节。把创建对象与使用对象进行拆分&#xff0c;满足单一职责。如果需要向工厂中添加新商品&#xff0c; 只需要扩展子类再重写其工厂方法&#xff0c;满足开闭原则。 设计…

【科研笔记】知识星球不可选择内容爬虫

知识星球不可选择内容爬虫 1 背景2 实现3 拓展遗留问题1 背景 针对与知识星球中,电脑打开网页不可选择复制粘贴的问题,进行爬虫处理,获取网页的内容,并保存在本地 2 实现 需要下载python,和爬虫的第三方库selenium,可以查看博客中有关selenium的内容进行回顾。当前使用…

LeetCode-207. 课程表【深度优先搜索 广度优先搜索 图 拓扑排序】

LeetCode-207. 课程表【深度优先搜索 广度优先搜索 图 拓扑排序】 题目描述&#xff1a;解题思路一&#xff1a;拓扑排序&#xff0c;检查图是否有环。有环代表不能完成&#xff0c;返回False。解题思路二&#xff1a;深度优先遍历解题思路三&#xff1a;广度优先搜索&#xff…

Pillow教程10:设计博文的文字背景封面图,再也不担心找不到不素材了

---------------Pillow教程集合--------------- Python项目18&#xff1a;使用Pillow模块&#xff0c;随机生成4位数的图片验证码 Python教程93&#xff1a;初识Pillow模块&#xff08;创建Image对象查看属性图片的保存与缩放&#xff09; Pillow教程02&#xff1a;图片的裁…

C# WPF编程-元素绑定

C# WPF编程-元素绑定 将元素绑定到一起绑定表达式绑定错误绑定模式代码创建绑定移除绑定使用代码检索绑定多绑定绑定更新绑定延时 绑定到非元素对象Source属性RelativeSource属性DataContent属性 数据绑定是一种关系&#xff0c;该关系告诉WPF从源对象提取一下信息&#xff0c;…

JAVA八股--redis

JAVA八股--redis 如何保证Redis和数据库数据一致性redisson实现的分布式锁的主从一致性Redis脑裂现象及解决方案介绍I/O多路复用模型undo log 和 redo log&#xff08;没掌握MyISAM 和 InnoDB 有什么区别&#xff1f; 如何保证Redis和数据库数据一致性 关于异步通知中消息队列…

高并发高性能接口中,异步打印并采集业务日志的实现方案

一、背景 高并发接口中&#xff0c;为了提高接口的高性能&#xff0c;在需要保存审计及操作记录的时候&#xff0c;往往有以下常见方案&#xff1a; 保存到redis数据库异步保存到mysql/mongodb/es等数据库logger打印业务日志&#xff0c;采集与展示则交由elk模块 对于第一种…

吃豆豆 经典的区间DP 好题典题

这里很巧妙的注意一点是&#xff0c;你最后要把所有的豆子都吃掉&#xff0c;所以你只要看你多增加的尽量的少就好了 然后维护一段区间&#xff0c;表示的是吃掉这段区间里面的所有豆子的最小代价&#xff0c;然后发现最后一个是左端点或者右端点 你吃一段新的区间的同时会把…