当前位置: 首页 > article >正文

【Linux】信号的捕捉

文章目录

    • 信号的捕捉
      • ∞图
    • sigaction函数

信号的捕捉

当我们在执行主流程的时候,可能因为某些情况而陷入内核,当内核处理完毕准备返回用户态时,就需要进行信号pending的检查,(此时仍处于内核态,有权力查看当前进程的pending位图)

在查看pending位图时,如果发现有未决信号,并且该信号没有被阻塞,那么此时就需要该信号进行处理

  • 如果待处理信号的处理动作是默认或者忽略,则执行该信号的处理动作后清除对应的pending标志位,如果没有新的信号要递达,就直接返回用户态,从主控制流程中上次被中断的地方继续向下执行即可

image-20220817110851338


  • 如果待处理信号是自定义捕捉的,即该信号的处理动作是由用户提供的,那么处理该信号时就需要先返回用户态执行对应的自定义处理动作,执行完后再通过特殊的系统调用sigreturn再次陷入内核并清除对应的pending标志位,如果没有新的信号要递达,就直接返回用户态,继续执行主控制流程的代码

image-20220817110916237

注意:handler和main函数使用不同的堆栈空间,它们之间不存在调用和被调用的关系,是两个独立的控制流程


∞图

可以借助下图进行记忆待处理信号是自定义捕捉时的情况

image-20220817111901358

其中:该图形与直线有几个交点就代表在这期间有几次状态切换,箭头的方向就代表着此次状态切换的方向,图形中间的圆点就代表着检查pending表


问:为什么识别到信号的处理动作是自定义时,不能直接在内核态执行用户空间的代码

理论上来说是可以的,因为内核态是一种权限非常高的状态,但是不能这样设计,

如果允许在内核态直接执行用户空间的代码,那么用户就可以在代码中设计一些非法操作,比如清空数据库等,虽然在用户态时没有足够的权限做到清空数据库,但是如果是在内核态时执行了这种非法代码,那么数据库就真的被清空了,因为内核态是有足够权限清空数据库的

换句话说:不能让操作系统直接去执行用户的代码,因为操作系统无法保证用户的代码是合法代码,即操作系统不信任任何用户


sigaction函数

捕捉信号除了用前面用过的signal函数,我们还可以使用sigaction函数对信号进行捕捉

#include<signal.h>
int sigaction(int signum, const struct sigaction* act, struct sigaction* oldact)

函数作用:

可以读取和修改与指定信号相关联的处理动作

参数解析:

signum代表指定信号的编号

若act指针非空,则根据act修改该信号的处理动作 若oldact指针非空,则通过oldact传出该信号原来的处理动作


这里的参数act和oldact都是结构体指针变量

image-20220817112435514

struct sigaction
{
    void(*sa_handler)(int);
    void(*sa_sigaction)(int, siginfo_t *, void *);
    sigset_t  sa_mask;
    int     sa_flags;
    void(*sa_restorer)(void);
};

成员解释:

1.sa_handler:

  • 如果sa_handler为:SIG_IGN 传给sigaction函数:表示忽略信号
  • 如果sa_handler为:SIG_DFL 传给sigaction函数:表示执行系统默认动作
  • 如果sa_handler为:自定义的函数指针 : 表示用自定义函数捕捉信号,或者说向内核注册了一个信号处理函数

注意: 所注册的信号处理函数的返回值为void,参数为int,通过参数可以得知当前信号的编号,这样就可以用同一个函数处理多种信号.显然这是一个回调函数,不是被main函数调用,而是被系统所调用


2.sa_sigaction : 实时信号的处理函数

3.sa_mask

  • 当某个信号的处理函数被调用,内核自动将当前信号加入进程的信号屏蔽字,当信号处理函数返回时自动恢复原来的信号屏蔽字,这样就保证了在处理某个信号时,如果这种信号再次产生,那么它会被阻塞到当前处理结束为止
  • 如果在调用信号处理函数时,除了当前信号被自动屏蔽之外,还希望自动屏蔽另外一些信号,则用sa_mask字段说明这些需要额外屏蔽的信号,当信号处理函数返回时,自动恢复原来的信号屏蔽字

注意:需要先对sa_mask使用sigemptyset函数进行初始化

4.sa_flags : 包含一些选项,这里直接将sa_flags设置为0即可


函数返回值说明

该函数调用成功返回0,出错返回-1


小例子:用sigaction函数对2号信号进行了捕捉,将2号信号的处理动作改为了自定义的打印动作,并在执行一次自定义动作后将2号信号的处理动作恢复为原来默认的处理动作

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
struct sigaction act, oact;

void handler(int signo)
{
	printf("get a signal:%d\n", signo);
	sigaction(2, &oact, NULL);//恢复2号信号的默认处理动作!
}

int main()
{
	//先把两个结构体变量的成员都初始化为0
	memset(&act, 0, sizeof(act));
	memset(&oact, 0, sizeof(oact));

	act.sa_handler = handler;
	act.sa_flags = 0;
	sigemptyset(&act.sa_mask);//初始化结构体变量act里的成员位图结构
	sigaction(2, &act, &oact);//捕捉2号信号
	while (1)
	{
		printf("I am a process\n");
		sleep(1);
	}
	return 0;
}
image-20220817113644624

第一次向进程发送2号信号,执行我们自定义的打印动作,然后2号信号的默认处理动作恢复,当我们再次向进程发送2号信号,就执行该信号的默认处理动作了,即终止进程


http://www.kler.cn/a/7595.html

相关文章:

  • 从零开始学习 sg200x 多核开发之 uboot 网络功能使能
  • uniapp h5地址前端重定向跳转
  • Gitcode文件历史记录查看和还原
  • Spring Boot框架:构建可扩展的网上商城
  • 关系型数据库和非关系型数据库详解
  • FingerprintSimilarity和BulkTanimotoSimilarity的区别
  • 先移动后旋转,先旋转后移动的区别
  • 【Django网络安全】跨站点请求伪造保护,CSRF如何正确使用
  • day18 二叉树遍历总结
  • ArrayList与LinkList的区别
  • minikube apiserver无法启动问题解决
  • C++并发与多线程笔记八:async、future、packaged_task、promise
  • 刷题记录|Day48 ● 198.打家劫舍 ● 213.打家劫舍II ● 337.打家劫舍III
  • arm系列交叉编译器各版本区别
  • 如何选择理想的三相浪涌保护器?
  • 【Ruby学习笔记】13.Ruby 迭代器及文件的输入与输出
  • 【vSphere | Python】vSphere Automation SDK for Python Ⅲ—— vCenter Datacenter APIs
  • 为什么无法跨centos、ubuntu、rocky linux 发行版本进行系统升级?
  • xinput1_3.dll缺失了如何去修复?xinput1_3.dll解决方法分享
  • 释放AIoT商业价值 | 2023高通广和通智能物联网技术开放日圆满落幕
  • srs流媒体录制视频
  • 22.SSM-JdbcTemplate总结
  • 贯穿设计模式第二话--开闭职责原则
  • 区块链学习笔记(3)BTC协议
  • 运算符重载
  • 亚马逊管理的14条领导力准则