Linux_进程信号_7

news2024/11/20 19:36:37

文章目录

  • 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.coloradmin.cn/o/1570176.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vue3_2024_7天【回顾上篇watch常见的后两种场景】___续

Vue3中监听多条数据的两种使用 1.watch【使用上一章写法&#xff0c;监听两个属性&#xff0c;然后执行相应操作…】 2.watchEffect【相对于使用watch&#xff0c;watchEffect默认页面初始加载&#xff0c;有点类似加配置&#xff1a;立即执行 immediate】 代码&#xff1a; …

Python:百度AI开放平台——OCR图像文字识别应用

一、注册百度AI开放平台 使用百度AI服务的步骤为&#xff1a; 注册&#xff1a;注册成为百度AI开放平台开发者&#xff1b;创建AI应用&#xff1a;在百度API开放平台上创建相关类型的的AI应用&#xff0c;获得AppID、API Key和Secret Key&#xff1b;调用API&#xff1a;调用…

Word中插入Endnote参考文献时显示乱码

近期在写文章需要插入参考文献&#xff0c;使用Endnote插入时显示乱码&#xff0c;如下图所示&#xff1a; 文章末尾显示{ADDIN EN REFILIST } 解决方法 在网上找了诸多方法尝试也没有解决&#xff0c;最终找到一篇博客介绍了一种方法&#xff1a; word选项—高级&#xff1…

苍穹外卖07(缓存菜品,SpringCache,缓存套餐,添加购物车菜品和套餐多下单,查看购物车,清除购物车,删除购物车中一个商品)

目录 一、缓存菜品 1 问题说明 2 实现思路 3 代码开发&#xff1a;修改DishServiceImpl 4 功能测试 二、SpringCache 1. 介绍 2. 使用语法 1 起步依赖 2 使用要求 3 常用注解 4 SpEL表达式(了解备用) 5 步骤小结 3.入门案例 1 准备环境 2 使用入门 1 引导类上加…

四、MySQL读写分离之MyCAT

一、读写分离概述 1、什么是读写分离&#xff1a; 读写分离&#xff1a;就是将读写操作分发到不同的服务器&#xff0c;读操作分发到对应的服务器 &#xff08;slave&#xff09;&#xff0c;写操作分发到对应的服务器&#xff08;master&#xff09; ① M-S (主从) 架构下&…

【测试篇】测试用例

文章目录 前言具体设计测试用例等价类边界值场景设计法判定表&#xff08;因果图&#xff09;正交排列&#xff08;用的非常少&#xff09;错误猜测法 前言 什么是测试用例&#xff1f;&#xff1f; 测试用例是针对软件系统或应用程序的特定功能或场景编写的一组步骤&#xf…

记一次Cannot deploy POJO class [xxx$$EnhancerBySpringCGLIB$$xxx]的错误

最近项目上需要使用websocket做服务端&#xff0c;那好说啊&#xff0c;直接springboot集成的websocket 引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId><versi…

开源推荐榜【Pear Admin Flask 用python来创建后台管理系统】

最新技术高效快速开发&#xff0c;前后端分离模式&#xff0c;开箱即用。 核心模块包括&#xff1a;用户、角色、职位、组织机构、菜单、字典、日志、多应用管理、文件管理、定时任务等功能。 代码量少、学习简单、功能强大、轻量级、易扩展&#xff0c;轻松开发从现在开始&…

【OpenCV】图像像素的遍历

1 前言 介绍两种遍历像素的方法&#xff08;非指针、指针&#xff09;。注意&#xff1a;.at() .ptr()的作用、用法。相关API&#xff1a; Mat对象.ptr() Mat对象.at() 2 代码及内容 #include "iostream" #include "opencv2/opencv.hpp"using namespac…

聊聊测试用例评审流程

测试人员将需求熟悉完成后&#xff0c;开始编写相应的测试用例&#xff0c;待测试用例编写完成后只是测试用例完成前的第一步&#xff0c;后边的流程需要组织线上或线下评审会议等等。 首先要了解测试用例评审的最终目的是什么&#xff1a;提高测试用例的质量和覆盖率&#xff…

notification+Android笔记

notification通知应用UI之外的消息并显示即推送&#xff1b; NotificationManager负责管理通知&#xff0c;例如显示取消&#xff0c;删除等&#xff1b; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager;…

从0开始搭建基于VUE的前端项目(三) Vuex的使用与配置

准备与版本 vuex 3.6.2(https://v3.vuex.vuejs.org/zh/)概念 vuex是什么? 是用作 【状态管理】的 流程图如下 state 数据状态,成员是个对象 mapState 组件使用this.$store.state.xxx获取state里面的数据 getters 成员是个函数,方便获取state里面的数据,也可以加工数据 ma…

HackTheBox-Mist

整体思路 端口扫描->Pluck CMS组件文件读取漏洞->文件上传获取shell->创建指向exe的快捷方式来提权-> 信息收集&端口利用 namp -sSVC 10.10.11.17目标只开放了80端口&#xff0c;将mist.htb加入到hosts文件后&#xff0c;访问mist.htb Pluck CMS文件读取 在…

RH850P1X芯片学习笔记-Clocked Serial Interface H (CSIH)

文章目录 Features of RH850/P1x-C CSIHUnitsRegister Base AddressClock SupplyInterrupt RequestsHardware ResetExternal Input/Output Signals数据一致性检查 OverviewFunctional OverviewFunctional Overview DescriptionBlock Diagram RegistersList of RegistersCSIHnCT…

Leetcode - 127双周赛

目录 一&#xff0c;3095. 或值至少 K 的最短子数组 I 二&#xff0c;3096. 得到更多分数的最少关卡数目 三&#xff0c;3097. 或值至少为 K 的最短子数组 II 四&#xff0c;3098. 求出所有子序列的能量和 一&#xff0c;3095. 或值至少 K 的最短子数组 I 本题需要知道一个知…

武汉星起航电子商务有限公司挂牌展示,为合作伙伴提供全方位支持

随着跨境电商领域市场竞争愈演愈烈&#xff0c;武汉星起航亚马逊一站式孵化平台悄然崭露头角。从2017年起&#xff0c;武汉星起航便立足亚马逊自营店铺&#xff0c;积累了丰富的实战运营经验。2020年正式成立后&#xff0c;公司以跨境电商为核心&#xff0c;凭借专业的运营团队…

复杂度的讲解

1.算法效率 如何衡量一个算法的好坏&#xff1f;从两个维度&#xff0c;时间和空间&#xff08;算法运行的快慢&#xff0c;消耗的空间大不大&#xff09;。因为计算机硬件领域的高速发展&#xff0c;如今计算机的存储量已经达到了一个很高的程度&#xff0c;所以现在我们一般…

12种常见的软件架构风格

什么是软件架构&#xff1f; 软件架构是定义软件系统的高级结构和组织的过程。它涉及识别和选择正确的组件&#xff0c;决定它们之间如何交互&#xff0c;以及确定它们应该如何组织以实现特定的目标。软件架构的目标是创建一个可维护、可扩展和安全的系统&#xff0c;能够满足…

车载电子电器架构 —— 软件下载

车载电子电器架构 —— 软件下载 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明自己,无…

【Qt】:常用控件(三:按钮类)

常用控件&#xff08;三&#xff09; 一.Push Button二.Radio Buttion三.Check Box 一.Push Button 使⽤ QPushButton 表⽰⼀个按钮.这也是当前我们最熟悉的⼀个控件了.QPushButton继承⾃QAbstractButton .这个类是⼀个抽象类.是其他按钮的⽗类. QAbstractButton 中,和 QPushBu…