Linux:进程通信(三)信号的捕捉

news2024/11/17 11:21:43

目录

一、信号捕捉函数

1、signal函数

2、sigaction函数

二、用户态与内核态

1、用户态 

 2、内核态

用户态与内核态转换 

三、volatile关键字

四、SIGCHLD信号


一、信号捕捉函数

1、signal函数

signal函数是C语言标准库中的一个函数,用于处理Unix/Linux系统中的信号。信号是操作系统用于通知进程发生了某个事件的一种机制,比如用户按下Ctrl+C时发送的SIGINT信号,或者某些错误条件如除零错误产生的SIGFPE信号。signal函数允许程序定义对这些信号的响应方式,而不是采用默认行为(通常是终止进程)。

函数原型: 

#include <signal.h>

typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);

参数说明:

  • signum:要处理的信号的编号,例如SIGINT(2号中断信号,通常是Ctrl+C),以及其它信号。
  • handler:处理函数的地址。这可以是以下几种形式:
    • SIG_DFL:恢复该信号的默认处理行为(通常是终止进程)。
    • SIG_IGN:忽略该信号。
    • 自定义函数:一个用户定义的函数指针,当信号发生时将调用该函数。该函数原型应为void function_name(int signum),其中signum是接收到的信号编号。

返回值:

signal函数返回与信号关联的先前处理函数的指针。如果之前没有安装处理函数,则返回值可能是SIG_ERR(表示发生错误)或者在某些实现中是默认的处理行为。

 这个函数在前面文章也使用过很多次,前面也对一些常用信号进行了说明,可以参考一下这篇文章:Linux:进程信号(一)信号的产生

2、sigaction函数

sigaction函数是POSIX标准中用于管理信号的高级接口,相比signal函数提供了更强大和灵活的信号处理能力。它允许程序不仅指定信号处理函数,还能控制信号处理时的其他方面,如信号掩码和额外的信号处理选项。

 函数原型: 

#include <signal.h>

int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

参数说明:

  • signum:要操作的信号编号。
  • act:指向一个struct sigaction结构体的指针,用于设置新的信号处理行为。
  • oldact:指向另一个struct sigaction结构体的指针,用于存储以前的信号处理行为(如果不关心旧的行为,可以传入NULL)。

sigaction结构体:

struct sigaction {
    void     (*sa_handler)(int);      // 信号处理函数指针,兼容老版本
    void     (*sa_sigaction)(int, siginfo_t *, void *); // 新的、更强大的信号处理函数指针
    sigset_t   sa_mask;               // 处理该信号时需要阻塞的其他信号集合
    int        sa_flags;              // 信号处理的标志,如SA_RESTART, SA_NODEFER等
    // 可能还有其他平台相关的字段
};

信号处理函数:可以通过sa_handlersa_sigaction指定。如果使用sa_sigaction,则可以访问到更多关于信号的信息,如发送信号的进程ID和信号的具体原因。

信号掩码(sa_mask在信号处理函数执行期间,指定的信号将被临时阻塞,以避免递归或干扰信号处理过程。

标志(sa_flags:控制信号处理的额外行为,例如SA_RESTART可以让某些系统调用在被信号中断后自动重启,而SA_NODEFER可以防止在处理该信号时该信号被阻塞。

我们使用以下代码来测试一下:

#include<signal.h>
#include<iostream>
#include <sys/types.h>
#include <unistd.h>

void printsigset(sigset_t *set)
{
    for(int i=31;i>=0;i--)
    {
        if(sigismember(set,i))
        {
            std::cout<<"1";
        }
        else
        {
            std::cout<<"0";
        }
    }
    std::cout<<std::endl;
}
void handler(int signo)
{
     std::cout << "get a sig: " << signo << std::endl;
     sleep(20);

}
int main()
{
    // sigset_t set,oset;
    // sigemptyset(&set);
    // sigemptyset(&oset);
    // sigaddset(&set,SIGINT);//屏蔽2号信号
    // sigaddset(&set,3);//屏蔽3号信号
    // sigaddset(&set,4);//屏蔽4号信号
    // sigaddset(&set,5);//屏蔽5号信号
    // sigprocmask(SIG_BLOCK,&set,&oset);
    
    struct sigaction act, oact;
    act.sa_handler=handler;
    sigemptyset(&act.sa_mask);
    sigaddset(&act.sa_mask,3);

    sigaction(2,&act,&oact);

    std::cout<<"pid"<<getpid()<<std::endl;

    while(true)
    {
        sigset_t pending;
        sigpending(&pending);
        printsigset(&pending);
        sleep(1);
        
    }
    return 0;
}

运行可以看到:

 

先发出2号信号,这个时候向它发出三号信号时会被阻塞,并不会执行,因为在前面添加了三号信号的阻塞,当2号信号对应的handler结束时,才会被处理。

如果在未处理2号信号时,直接发送3号信号,那么会直接终止:

如果有处理函数(通过上述方式注册),内核会执行以下操作:

保存现场:保存当前程序上下文,包括寄存器状态等,以便稍后恢复执行。

修改信号掩码通常,在执行信号处理函数之前,内核会临时修改进程的信号掩码,以防止在处理信号过程中被相同的或其他信号打断,除非这些信号被特别设置为不阻塞。

调用信号处理函数执行用户定义的信号处理函数。在此期间,进程处于用户态。

恢复现场:信号处理完毕后,内核恢复之前保存的程序上下文,进程从被中断的地方继续执行,或者根据信号处理函数的返回情况和系统调用的重启规则决定后续动作。

二、用户态与内核态

用户态(User Mode)和内核态(Kernel Mode),也称为用户空间和内核空间,是现代操作系统中的两种处理器执行模式,它们定义了程序运行时的不同权限级别和访问能力。

1、用户态 

在用户态下,程序(通常是应用程序)只能执行非特权指令,不能直接访问硬件资源或执行对系统稳定性有风险的操作。这意味着程序不能直接读写内存、操作外设、更改系统时间等。

应用程序的大部分时间都在用户态下运行,这样可以保证系统的安全性和稳定性,因为程序错误不会直接影响到操作系统的核心部分。

如果用户态程序需要执行如磁盘I/O、网络通信等操作,它必须通过系统调用(System Call)向操作系统请求服务,这时就会从用户态转换到内核态。

 2、内核态

内核态下,程序可以执行所有指令,包括特权指令,可以直接访问和控制所有系统资源,如内存、I/O设备等。操作系统内核及其相关模块(如设备驱动程序)在内核态下运行,负责管理系统资源、处理中断、调度进程等核心任务。

当系统调用发生时,CPU从用户态切换到内核态,操作系统内核执行所需的服务,并在完成后返回用户态。这一过程涉及到堆栈的切换、权限级别的变化和上下文的保存与恢复。

内核态还负责处理硬件中断和异常,无论当前处理器处于何种状态,一旦中断或异常发生,CPU都会立即进入内核态以处理这些事件。

用户态与内核态转换 

转换通常由硬件支持,并由操作系统控制。从用户态到内核态的转换通常是通过执行特定指令(如系统调用指令)、产生中断或异常来触发。

从内核态返回用户态通常发生在系统调用完成、中断处理结束或异常处理完成后,操作系统通过执行相应的返回指令来实现状态的恢复。

这种区分和转换机制是操作系统设计的基础之一,旨在提供隔离和保护,确保系统稳定性和安全性,同时允许用户程序有效利用系统资源。

三、volatile关键字

volatile关键字在C/C++等编程语言中是一个类型修饰符,用于指示编译器不要对被修饰的变量进行任何优化假设,确保程序的执行符合预期,特别是在多线程编程和硬件交互场景中。

四、SIGCHLD信号

        wait和waitpid函数清理僵尸进程,父进程可以阻塞等待子进程结束,也可以非阻塞地查询是否有子进程结束等待清理(也就是轮询的方式)。采用第一种方式,父进程阻塞了就不能处理自己的工作了;用第二种方式,父进程在处理自己的工作的同时还要记得时不时地轮询一 下,程序实现复杂。

        子进程在终止时会给父进程发SIGCHLD信号,该信号的默认处理动作是忽略,父进程可以自定义SIGCHLD信号的处理函数,这样父进程只需专心处理自己的工作,不必关心子进程了,子进程终止时会通知父进程,父进程在信号处理函数中调用wait清理子进程即可。

僵尸进程:

运行以下代码:

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include<sys/types.h>
#include<unistd.h>
void handler(int sig)
{
 pid_t id;
 while( (id = waitpid(-1, NULL, WNOHANG)) > 0){
 printf("wait child success: %d\n", id);
 }
 printf("child is quit! %d\n", getpid());
}
int main()
{
 signal(SIGCHLD, handler);
 pid_t cid;
 if((cid = fork()) == 0){//child
 printf("child : %d\n", getpid());
 sleep(3);
 exit(1);
 }
 while(1){
 printf("father proc is doing some thing!\n");
 sleep(1);
 }
 waitpid(-1, NULL, WNOHANG);
 return 0;
}
可以看到不会生成僵尸进程,因为一旦退出就立马回收
不产生僵尸进程还有另外一种办法 : 父进程调用 sigaction SIGCHLD 的处理动作置为SIG_IGN, 这样 fork 出来的子进程在终止时会自动清理掉 , 不 会产生僵尸进程 , 也不会通知父进程。系统默认的忽略动作和用户用sigaction 函数自定义的忽略通常是没有区别的, 但这是一个特例。此方法对于 Linux 可用 , 但不保证在其它UNIX 系统上都可用。
监视发现这样也不会产生僵尸进程:
信号捕捉的过程确保了信号能够灵活且有效地传递给进程,同时允许进程以自定义的方式响应这些信号,从而增强了程序的健壮性和灵活性。

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

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

相关文章

web自动化系列-selenium执行js脚本|截图|识别验证码(十五)

1.执行脚本 如果你定位的元素通过各种方法都无法完成页面操作&#xff0c;最后的方法就是通过操作js脚本来完成 。 在selenium中提供了一个方法 &#xff0c;这个可以方法可以自行JS的脚本 。具体为&#xff1a; execute_script(js脚本) &#xff1a; js脚本代表要执行的脚本…

OC类与对象(下)

OC类与对象&#xff08;下&#xff09; 文章目录 OC类与对象&#xff08;下&#xff09;不是包装类的NSValue 和NSNumber处理对象打印对象和description方法 和 isEqual方法 类别类别语法部分利用类别进行模块化设计使用类别来调用私有方法 类的扩展协议与委托规范&#xff0c;…

BLIP和BLIP2 论文讲解

文章目录 BLIPIntroductionMethod模型架构预训练目标字幕和过滤&#xff08;Capfilt&#xff09; BLIP2IntroductionMethod模型结构Q-Former预训练第一阶段Q-Former预训练第二阶段 BLIP 论文&#xff1a; 《BLIP: Bootstrapping Language-Image Pre-training for Unified Visio…

Unity打开安卓设备不同的设置面板

1&#xff0c;打开安卓设备不同的设置面板&#xff0c;我还贴心的把Android官网的链接放下面了 2&#xff0c;使用也很方便&#xff1a;unity按钮事件上拖这个脚本&#xff0c;注册MyOpenAndroidSettings方法&#xff0c;参数 填 和枚举值相应的数字 // 功能&#xff1a;打开…

【c++】线程池的原理及实现

&#x1f4bb;文章目录 &#x1f4c4;前言线程池的原理概念工作原理 线程池的实现线程池的基础结构任务队列的实现工作线程的实现 线程池的应用与拓展线程池的拓展 &#x1f4d3;总结 &#x1f4c4;前言 不知道各位是否有试过点进限时抽奖网站、抢票网站呢&#xff1f;你是否好…

静态分析-RIPS-源码解析记录-02

这部分主要分析scanner.php的逻辑&#xff0c;在token流重构完成后&#xff0c;此时ini_get是否包含auto_prepend_file或者auto_append_file 取出的文件路径将和tokens数组结合&#xff0c;每一个文件都为一个包含require文件名的token数组 接着回到main.php中&#xff0c;此时…

一款功能强大的网络安全综合工具-PotatoTool

一、 简介 这款工具是一款功能强大的网络安全综合工具&#xff0c;旨在为安全从业者、红蓝对抗人员和网络安全爱好者提供全面的网络安全解决方案。它集成了多种实用功能&#xff0c;包括解密、分析、扫描、溯源等&#xff0c;为用户提供了便捷的操作界面和丰富的功能选择。 二…

《Fundamentals of Power Electronics》——状态空间平均法

文献中出现了许多交流变换器建模技术&#xff0c;包括电流注入法、电路平均法和状态空间平均法。尽管给定方法的支持者可能更喜欢用特定形式表示最终结果&#xff0c;但几乎所有方法的最终结果都是等效的。所有人都会赞同&#xff0c;平均和小信号线性化是PWM变换器建模的关键步…

厚德提问大佬答4:AI绘画生成的心得

遇到难题不要怕&#xff01;厚德提问大佬答&#xff01; 厚德提问大佬答 你是否对AI绘画感兴趣却无从下手&#xff1f;是否有很多疑问却苦于没有大佬解答带你飞&#xff1f;从此刻开始这些问题都将迎刃而解&#xff01;你感兴趣的话题&#xff0c;厚德云替你问&#xff0c;你解…

Element-plus修改input的placeholder文字颜色

需求 代码 .el-input__inner::placeholder {color: #666f8d !important; }

图像处理--空域滤波增强(原理)

一、均值滤波 线性滤波算法&#xff0c;采用的主要是邻域平均法。基本思想是使用几个像素灰度的某种平均值来代替一个原来像素的灰度值。可以新建一个MN的窗口以为中心&#xff0c;这个窗口S就是的邻域。假设新的新的像素灰度值为&#xff0c;则计算公式为 1.1 简单平均法 就是…

堆的应用2——TOPK问题

TOPK问题 TOP-K问题&#xff1a;即求数据结合中前K个最大的元素或者最小的元素&#xff0c;一般情况下数据量都比较大。 比如&#xff1a;专业前10名、世界500强、富豪榜、游戏中前100的活跃玩家等。 情况1——数据量小 对于Top-K问题&#xff0c;能想到的最简单直接的方式就…

品鉴中的文化碰撞:如何理解和欣赏不同文化背景下的红酒

红酒作为世界各地广泛生产的产品&#xff0c;具有丰富的文化内涵。不同国家、地区和民族的红酒文化各具特色&#xff0c;反映了当地的历史、传统、习俗和生活方式。在品鉴云仓酒庄雷盛红酒时&#xff0c;理解和欣赏不同文化背景下的红酒是提升品鉴体验的重要一环。 首先&#x…

【Java orm 框架比较】九 新增wood框架对比

【Java orm 框架比较】九 新增wood框架对比 本次新增wood 框架测试 测试数据存储、分页查询&#xff0c;文档及框架比较稳定半天时间加入测试使用 迁移到&#xff08;https://gitee.com/wujiawei1207537021/spring-orm-integration-compare&#xff09; orm框架使用性能比较…

【STM32 |GPIO】GPIO结构、GPIO输出

目录 GPIO简介 GPIO的基本结构 GPIO位结构&#xff08;每一位的具体电路结构&#xff09; 输入 上拉和下拉电阻 斯密特触发器 ​编辑 输出 GPIO模式 ​编辑 浮空输入、上拉输入、下拉输入 模拟输入 开漏输出和推挽输出 复用开漏输出和复用推挽输出 LED和蜂鸣器…

代码随想录算法训练营第36期DAY22

DAY22 654最大二叉树 自己做的时候忽略了&#xff1a;nums.length>1的题给条件。所以每次递归都要判断是否size()>1&#xff0c;不要空的。 /** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *rig…

JSpdf,前端下载大量表格数据pdf文件,不创建dom

数据量太大使用dom》canvas》image》pdf.addimage方法弊端是canvas超出 浏览器承受像素会图片损害&#xff0c;只能将其切割转成小块的canvas,每一次调用html2canvas等待时间都很长累积时间更长&#xff0c;虽然最终可以做到抽取最小dom节点转canvas拼接数据&#xff0c;但是死…

[附源码+视频教程]暗黑纪元H5手游_架设搭建_畅玩三网全通西方3D世界_带GM

本教程仅限学习使用&#xff0c;禁止商用&#xff0c;一切后果与本人无关&#xff0c;此声明具有法律效应&#xff01;&#xff01;&#xff01;&#xff01; 教程是本人亲自搭建成功的&#xff0c;绝对是完整可运行的&#xff0c;踩过的坑都给你们填上了 一. 演示视频 暗黑纪…

Android 查看CUP占用率

查看每个进程CUP占用率的几种方式,由于自己充电界面老是导致整机温度过高&#xff0c;后面发现自己的线程一直在跑&#xff0c;相当于死循环&#xff0c;后面加上sleep才得以改善&#xff1b;先看看几种查询方式吧。 1、adb shell top 2、adb shell busybox top 3、adb shell …

C++类和对象中篇

&#x1f407; &#x1f525;博客主页&#xff1a; 云曦 &#x1f4cb;系列专栏&#xff1a;[C] &#x1f4a8;路漫漫其修远兮 吾将而求索 &#x1f49b; 感谢大家&#x1f44d;点赞 &#x1f60b;关注&#x1f4dd;评论 文章目录 &#x1f4d4;前言&#x1f4d4;1、类的六个…