进程信号

news/2024/10/6 1:33:42/文章来源:https://www.cnblogs.com/kzx1019/p/18288520

1. 信号的产生

1.1 信号概念

在生活中有很多的信号在我们身边围绕,例如红绿灯,发令枪,上课铃等等

在接受到信号,我们可以做出三种动作 1.立马去做对应信号的事情 2.等一会再做,有自己的事情或者更重要的事情 3.直接忽视,不做

信号是给进程发送的 eg: kill -9 pid

进程本身是程序员编写的属性和逻辑的集合:所以进程有识别信号,认知到信号的功能

对于信号,进程必须要有保存信号的能力,就好比人记在脑子里一样

​ 由此可以引出对于信号,可以做出三种反应:

1.默认动作 2.忽略动作 3.自定义动作

  • 进程收到的信号保存在哪里呢 ?

如下图,可以发现信号有 1-31号(普通信号) ,34-64号(实时信号)

对于进程,收到一个信号时候,只有两种可能 : 是否收到信号,所以可以用0和1来表示是否收到信号

仔细观察则会发现信号有64个,那么我们可以用位图的形式用一个保存

进程收到的信号保存在pcb的一个变量里,用比特位来表示第几个信号

struct task_struct{......unsigned int singal;.......
}

给进程发送信号一定要经过系统调用,所以kill命令等都是OS提供的接口。

1.2 产生信号的方式

    1. 通过键盘发送

      在运行进程的时候,终止进程一般用Ctrl+c来终止当前进程,其实这个热键是OS进行对这个操作进行识别,然后再对对应的进程发送2号信号(SIGINT),从而终止当前进程

    1. 系统调用向目标进程发送信号

    int kill(pid_t pid, int sig) ;

    头文件#include <sys/types.h> #include <signal.h>

    功能: 对目标进程pid,发送sig号信号

    为此,我们可以实现一个自己的kill命令,eg:

    #include<iostream>
    #include<cstdio>
    #include<string>
    #include<unistd.h>
    #include<sys/types.h>
    #include<signal.h>
    void UsPage(const std::string& pro){//用户手册std::cout<<"\nplease input: "<<pro<<" pid signal"<<std::endl;
    }// 输入格式为  ./mysignal  pid  signal
    int main(int argc,char* argv[]){if(argc!=3){UsPage(argv[0]);exit(0);}int id=atoi(argv[1]);       //id为要杀死的进程的pid即为传的命令行的第二个参数int sign=atoi(argv[2]);     //对进程发送的信号signint res=kill(id,sign);if(res==-1){perror("kill: ");//打印错误信息exit(-1);}return 0;
    }
    
    1. 硬件产生信号

(1) 除0操作

当在代码中有除0操作的时侯,底层实际上是硬件计算溢出,CPU会异常,此时会给进程发送异常的信号,发送

发生除0操作系统会抛以下异常:Floating point exception

man 7 signal 查看信号时候可以发现为 : SIGFPE 8 Core Floating point exception,为8号信号

(2) 野指针异常

如果发生对于一个无效指针的访问,实际是进程地址空间发生对野指针的访问异常,进程地址空间是由页表映射到物理内存的,其中包括了一个MMU(内存管理单元),如果异常,那么会给进程发送信号

野指针等越界错误会发生 Segmentation fault

man 7 signal 查看:SIGSEGV 11 Core Invalid memory reference

..........例子不止这两个,所以硬件也可以导致信号的产生

    1. 软件产生信号

**(1)管道 **

在之前的管道学习中,假如有一个管道 : 关闭管道的读端,但是写端没有关,那么此时也会产生信号 ,因为操作系统不允许任何的资源浪费,所以这种关闭读,写不关就会造成信号的产生,从而终止程序(大部分信号都会终止)

(2) 闹钟

int alarm(unsigned int num) ;//num秒后关闭进程

其实这个闹钟底层就是在num秒后给进程发送一个14号信号,然后num秒后终止

SIGALRM 14 Term Timer signal from alarm(2)

..........例子不止这两个

1.3 signal ()函数

​ signal()函数:

sighandler_t signal(int signum, sighandler_t handler);

​ 其中第二个参数是一个函数指针类型,类型为 (*func)(int)

typedef void (*func)(int);  //其中这个函数的参数为int型

​ 设置处理信号的功能

​ 指定使用sig指定的信号编号处理信号的方法。 参数func指定程序可以处理信号的三种方式之一:

​ 这个就是自定义动作,我们自己定义改进程在遇到signnum的信号的时候做什么操作

eg:

void catchSign(int signal){std::cout<<"catach signal:"<<signal<<std::endl;sleep(1);
}
int main(){signal(11,catchSign);  int arr[100];arr[10000000]=666;return 0;
}

signal只是声明,本来遇到第num号信号应该是按系统指定的操作

signal函数之后就把遇到该信号的时候,做出自己定义的操作了,即上边的catchSign函数

所以当遇到改变操作的函数的信号的时候,才会触发自定义操作,并不是声明signal就会触发,如果没有遇到该信号甚至不会触发自己的自定义操作。

1.4 核心转储问题

以上 31个普通信号,CoreTerm类型的都会造成进程终止,但是不同的是,Core会产生核心转储

在云服务器上如果进程是Core退出的,没有明显的现象可以观察到,这是因为云服务器关闭了Core file的文件大小

如果要更改有效文件大小可以使用命令:ulimit -c 1024,1024是设置的文件大小,可以设置为其他,-c是corefile 其他的也可以用后边那个-字母设置

将core file设置大小后,再次遇到造成core退出的进程,会在进程目录下产生一个名为core.xxx的文件

用gdb下输入: core-file core.xxx会出现以下内容 要生成debug的可执行程序才可以

则gdb会显示该进程异常的原因,该原因是野指针问题

2. 信号的保存

2.1 阻塞信号

首先要理解信号的保存要引入以下概念:

  1. 实际执行信号的处理过程称为递达
  2. 信号从产生到递达之间的状态称为未决
  3. 信号可以被阻塞
  4. 被阻塞的信号将处于未决状态,直到解除阻塞,才会执行递达的动作

所以信号是可以被阻塞的 , 也可以说某个信号可以被屏蔽

其实在pcb中信号采用位图的思想保存,这些所有的信号被称为信号集

pcb中信号的保存分为三个部分:

  1. 未决信号集
  2. 信号屏蔽集
  3. 信号集操作函数表

  • pending : 表示到达pcb中的未决信号
  • bolck : 表示当前进程屏蔽的信号
  • handler :其实就是一个函数指针数组,该数组是信号的处理方式,所以假设用户自定义了信号的处理方式,那么这个函数表就会把自定义函数的指针替换到函数表的特定位置

当屏蔽信号存在时,这个信号将不会被递达

pending : 00000000000000000000000

block : 00000000000000000000010 不会被递达

当屏蔽信号存在时,未决信号也存在,这个信号也不会被递达

pending : 00000000000000000000010

block : 00000000000000000000010 不会被递达

只有当未决信号存在,并且没有被屏蔽,才能被递达

pending : 00000000000000000000010

block : 00000000000000000000000 递达

2.2 信号的捕捉

2.2.1 用户态和内核态

首先要理解信号的捕捉流程需要理解以下两个内容:

用户态和内核态

在操作系统中,内存以及硬件等资源是由操作系统所维护的,正所谓操作系统不相信任何人,当我们进行申请系统资源的时候,其实进程会切换到内核态去进行一系列操作,从而申请资源

实际执行系统调用的人是”进程“,但是身份是内核 , 系统调用往往比较费时间所以尽量少的进行系统调用

那么切换状态的过程是什么呢?

  1. 在之前的进程地址空间中,了解到了用户有0-3G的地址空间,进程地址空间通过页表映射到物理内存中,从而实现cpu与进程交互

  2. 操作系统在开机时候也会加载到内存中,管理着整个电脑,整个问题毋庸置疑,所以操作系统也在内存中

  3. 对于每个进程,剩余的3-4G地址空间,其实就是内核态的进程地址空间,称为内核空间,整个进程地址空间映射到物理内存中的操作系统的物理内存区。

  4. 由于每个进程固定的3-4G都是内核空间,而且操作系统只有一个,所以对于每个进程都会通过内核级页表映射到一个操作系统的物理内存处,内核级页表一个就够了。

  5. 当进程通过系统调用时候,系统调用会发出一个陷入指令,让cpu的CR3寄存器(该寄存器记录当前进程的运行级别0代表内核态,3代表用户态)标记为内核态,从而变为内核态,所以在用户态进行系统调用的时候,切换身份并且跳转在内核空间中执行有关操作,执行完后会发送指令再将CR3寄存器标记为用户态

2.2.2 信号的捕捉流程

信号的捕捉处理是在内核态返回用户态的时候进行的

首先我一定是进入了内核态,进入内核态的原因有很多 : 中断,系统调用,进程切换等。

当要返回到用户态时,既然进入内核态那么不容易,所以索性去检查一下进程是否收到了信号,因为是在内核态,所以pcb访问轻轻松松,然后检查是否有需要递达的信号

对于信号的处理有三种方式 : 1.默认动作 2.忽略动作 3.自定义动作

对于不同的处理方式也会有不同的流程

  • 默认动作和忽略动作

  • 自定义动作,假设这个自定义动作没有使用进程退出,假设是一个自动修补漏洞并且继续执行的处理

3. 信号集操作函数

pending和bolck都是由一个信号集组成的,也就是位图的思想类型为sigset_t

  typedef struct {unsigned long sig[_NSIG_WORDS];} sigset_t;

所以定义一个信号集,可以 sigset block;

下边介绍对信号集的有关函数:

#include <signal.h>int sigemptyset(sigset_t *set);
//初始化信号集,全置为0int sigfillset(sigset_t *set);
//初始化信号集,使所有的信号都有,即为全1int sigaddset (sigset_t *set, int signo);
//将信号集中添加signo号信号,即将set信号集中signo信号的位置置为1int sigdelset(sigset_t *set, int signo);
//将set信号集中的signo号信号由1置为0,即去掉int sigismember(const sigset_t *set, int signo);
//判断signo信号是否在set中出现,如果没有返回0,有的话返回1

sigprocmask函数 :

调用sigpromask函数可以读取或者更改当前进程中屏蔽信号集

#include<signal.h>
int sigprocmask(int how , const sigset_t *set , sigset_t *oset);

参数:

  • how : 当前想进行的操作类型,1. SIG_BLOCK 2. SIG_UNBLOCK 3. SIG_SETMASK

​ SIG_BLOCK : 希望添加的屏蔽字的信号,即 mask = mask|set

​ SIG_UNBLOCK : 希望解除屏蔽字的信号,即可mask = mask | ~set

​ SIG_SETMASK : 将当前进程的屏蔽字设置为set所指向的值

  • set: 输入型参数,根据how传给函数的信号集
  • oset : 输出型参数 ,将没有被更改之前的信号集保存到oset所指向的信号集

sigpending函数:读取当前进程的未决信号集,通过set参数传出

#include <signal.h>
int sigpending(sigset_t *set);
//读取当前进程的未决信号集,通过set参数传出。调用成功则返回0,出错则返回-1。 

下面用刚学的几个函数做个实验。程序如下:

#include<iostream>
#include<vector>
#include<string>
#include<unistd.h>
#include<signal.h>using namespace std;vector<int> Blocks={2};//想屏蔽多少,假如数组中即可string show_pending(sigset_t& pending){string s;int cnt=1;for(int i=31;i>=1;i--){//打印31个信号集if(sigismember(&pending,i)){//sigismember比较传参的信号集是否有第i个信号,有返回1,无返回0s+='1';}else s+='0';if(cnt++%4==0) s+=' ';}return s;
}void hander(int sign){//自定义捕捉函数cout<<"捕捉到信号"<<sign<<endl;
}int main(){//1.初始化信号集sigset_t block,oblock,pending;sigemptyset(&block);sigemptyset(&oblock);sigemptyset(&pending);//2.阻塞信号for(const auto& e:Blocks) sigaddset(&block,e);//3.设置屏蔽的信号sigprocmask(SIG_SETMASK,&block,&oblock);int num=1;for(const auto&e:Blocks) signal(e,hander);while(true){//获取当前进程的pending表sigpending(&pending);//打印当前进程表cout<<show_pending(pending)<<endl;sleep(1);if(num++==5){//假设5秒后取消阻塞sigprocmask(SIG_SETMASK,&oblock,&block);//oblock就是保存的原来的,代表取消屏蔽,为了安全,将原来的block备份一下}}return 0;
}

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

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

相关文章

24-暑假软件工程日报(7_7)

工作时间:7月7日 14:00-17:00 工作内容: 基本完成第二阶段大程序构建 代码:#include <cstring> #include <iostream> #include <list> #include <math.h> #include <queue> #include <stack> #include <stdio.h> #include <st…

[CISCN2019 华北赛区 Day2 Web1]Hack World

进入题目 输入数字1数字20对select 空格 union or 等等测试发现没有过滤select 空格也被过滤 注意不能单独测试用亦或运算 1^0为真 尝试0^if((ascii(substr((select(flag)from(flag)),1,1))=100),0,1) 回显正常根据回显判断正误 编写脚本爆破,由于该网站请求太快会报429,请求…

CubeMx的部分配置显示不出来

现象描述:CubeMx的部分配置显示不出来 处理方法:(1)继续进行其他配置,给工程起名字,并生成代码;(2)关闭CubeMx后再次打开

[CISCN2019 华东南赛区]Double Secret

进入题目由于请求不能过快,目录扫描工具失效可写脚本, 根据题目两个secret,猜测有serect目录 访问猜测还有一个secret参数随意输入发现源码泄露 注意到有flask,考虑python模板注入 注意到发现rc4加密 找师傅的加密脚本 import base64 from urllib.parse import quote def rc4…

[CISCN2019 华东南赛区]Web11

进入题目注意到xff 在url处随意输入目录xff随之变化 注意下放smarty是php模板 猜测xff为模板注入点 如下用if标签看到回显得到flag flag{6efda977-94fb-4d30-8668-fe28458ec2bf}

game1

进入题目发现是一个游戏发现有一个score.php的发包 发现有分数等 对比不同分数的包发现sign值都有ZM后疑似为base64于是将分数改为较高的分,ZM+base64 尝试要补一个=得到flag

[CISCN 2019 初赛]Love Math

进入题目,直接源码 代码审计、看师傅wp有黑名单字符过滤,有白名单函数过滤 于是用编码绕过,利用eval函数执行命令,system(cat /flag.txt) base_convert(a,b,c) //将数值a按b进制转换为c进制 dechex //将10进制转成16进制 hex2bin() //16进制转成字符串 base_convert(37907…

babyweb国赛华东北

进入题目目录扫描 扫出ssrf.php 发现url尝试用file协议访问/flag.txt拿到flag

人脸识别签到系统一站式开发【基于Pyqt5的CS架构软件】

人脸识别签到系统:课堂签到,上班打卡,进出门身份验证。 功能:人脸录入,打卡签到,声音提醒,打卡信息导出,打包exe文件人脸识别签到系统 1、运用场景 课堂签到,上班打卡,进出门身份验证。 2、功能架构 人脸录入,打卡签到,声音提醒,打卡信息导出:3、技术栈 python3.8…

本地管理员

进入题目 随意输入密码发现提示ip,则尝试xff伪造ip右键检查发现base64编码 解码得猜测为用户admin密码 抓包发包得到flag

rust学习-记录第一个完成的rust算法题

给你一个下标从 0 开始的 8 x 8 网格 board ,其中 board[r][c] 表示游戏棋盘上的格子 (r, c) 。棋盘上空格用 . 表示,白色格子用 W 表示,黑色格子用 B 表示。 游戏中每次操作步骤为:选择一个空格子,将它变成你正在执行的颜色(要么白色,要么黑色)。但是,合法 操作必须满…

Grafana+Loki+Promtail 日志监控

目录前置工作用户组(按需创建)GrafanaLokiLoki 配置文件Promtail配置文件配置数据源创建仪表盘添加查询项 前置工作Centos 7 关闭防火墙 (systemctl stop firewalld) 独立的用户组(可以不创建)用户组(按需创建) # 创建用户组 groupadd grafana # 新建一个家目录为`/home/graf…