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

目录

一、信号捕捉函数

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){//childprintf("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.rhkb.cn/news/324047.html

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

相关文章

RK3568 学习笔记 : 精简 u-boot env 默认复杂的多种引导启动设置

前言 环境&#xff1a; 正点原子 Atompi-CA1 RK3568 开发板、正点原子 DLRK3568 开发板&#xff0c;&#xff08;一时脑热买了两块 RK3568 开发板&#xff09;&#xff0c;Atompi-CA1 RK3568 开发板比较小巧&#xff0c;利于一些前期的嵌入式 Linux 开发学习与实践。 RK3568 开…

Android 11 输入系统之InputDispatcher和应用窗口建立联系

InputDispatcher把输入事件传给应用之前&#xff0c;需要和应用窗口建立联系&#xff0c;了解了这个过程&#xff0c;就清楚了APP进程和InputDispatcher线程也就是SystemServer进程之间是如何传输数据了 我们向窗口addView的时候&#xff0c;都会调用到ViewRootImpl的setView方…

热爱电子值得做的电子制作实验

加我zkhengyang&#xff0c;进嵌入式音频系统研究开发交流答疑群(课题组) AM/FM收音机散件制作&#xff0c;磁带随声听散件&#xff0c;黑白电视机散件制作&#xff0c;功放散件制作&#xff0c;闪光灯散件制作&#xff0c;声控灯散件&#xff0c;等等&#xff0c;可提高动手能…

kali linux更新卡在libc6:amd64 (2.37-15)

适配于linux的windows子系统&#xff0c;wsl2&#xff0c;安装kali linux&#xff0c;运行 sudo apt update 卡在&#xff1a;Setting up libc6:amd64 (2.37-15) … 关机重启、重新修复执行也不行 解决办法&#xff1a;kill当前apt进程或者关机重启kali-linux&#xff0c;然…

【Linux】学习笔记

文章目录 [toc]第一章&#xff1a;基础篇01|课程介绍02|内容综述03|什么是Linux04|Linux的内核版本及常见发行版内核版本发行版本Red Hat Enterprise LinuxFedoraCentOSDebianUbuntu 05|安装VirtualBox虚拟机VirtualBox下载url 06|在虚拟机中安装Linux系统Linux安装镜像下载 07…

SpringBoot+vue实现右侧登录昵称展示

目录 1. 定义User数据 1.1.在created方法获取数据 1.2.头部导航栏绑定User数据 1.3.在data中定义User数据 2. 获取数据 2.1.接收父组件传递的值 2.2.展示数据 3. 页面效果 在SpringBoot和 Vue.js 结合的项目中实现右侧登录昵称展示&#xff0c;通常涉及到前端的用户界面…

ROS机器人入门:机器人系统仿真【学习记录】——2

承接上一篇博客&#xff1a; ROS机器人入门&#xff1a;机器人系统仿真【学习记录】——1-CSDN博客 我们先前结束了&#xff08;上一篇博客中&#xff09;&#xff1a; 1. 概述 2. URDF集成Rviz基本流程 3. URDF语法详解 4. URDF优化_xacro 下面让我们继续学习ROS机器人…

会话劫持攻击就在我们身边,我们要如何防范

会话劫持攻击&#xff08;Session Hijacking&#xff09;是一种网络攻击方式&#xff0c;攻击者通过某种手段获取到用户的会话标识&#xff08;Session ID&#xff09;&#xff0c;然后使用这个会话标识冒充合法用户进行恶意操作。这种攻击方式允许攻击者以合法用户的身份访问受…

FFmpeg常用命令详解与实战指南

下载地址&#xff1a;Releases BtbN/FFmpeg-Builds (github.com) 1. 获取视频信息 使用FFmpeg获取视频信息是最基本的操作之一。你可以使用-i选项指定输入文件&#xff0c;然后使用FFmpeg内置的分析器来获取视频的各种信息&#xff0c;包括视频编解码器、音频编解码器、分辨…

《Mask2Former》算法详解

文章地址&#xff1a;《Masked-attention Mask Transformer for Universal Image Segmentation》 代码地址&#xff1a;https://github.com/facebookresearch/Mask2Former 文章为发表在CVPR2022的一篇文章。从名字可以看出文章像提出一个可以统一处理各种分割任务&#xff08;…

智慧公厕,运用数据提升公共厕所管理水平!

随着城市人口的增加和生活水平的提高&#xff0c;公共厕所的管理变得越来越重要。传统的厕所管理方式已经无法满足人们对卫生、便利和舒适的需求。而智慧公厕作为新一代公厕管理方式&#xff0c;通过运用数据技术和大数据分析手段&#xff0c;彻底改变了公厕管理的模式&#xf…

远程桌面连接不上怎么连服务器,原因是什么?如何解决?

远程桌面连接不上怎么连服务器&#xff0c;原因是什么&#xff1f;如何解决&#xff1f; 面对远程桌面连接不上的困境&#xff0c;我们有办法&#xff01; 当你尝试通过远程桌面连接服务器&#xff0c;但遭遇连接失败的挫折时&#xff0c;不要慌张。这种情况可能由多种原因引起…

26、Qt使用QFontDatabase类加载ttf文件更改图标颜色

一、图标下载 iconfont-阿里巴巴矢量图标库 点击上面的链接&#xff0c;在打开的网页中搜索自己要使用的图标&#xff0c;如&#xff1a;最大化 找到一个自己想用图标&#xff0c;选择“添加入库” 点击“购物车”图标 能看到刚才添加的图标&#xff0c;点击“下载代码”(需要…

4D 成像毫米波雷达:新型传感器助力自动驾驶

1 感知是自动驾驶的首要环节&#xff0c;高性能传感器必不可少 感知环节负责对侦测、识别、跟踪目标&#xff0c;是自动驾驶实现的第一步。自动驾驶的实现&#xff0c;首先要能够准确理解驾驶环境信息&#xff0c;需要对交通主体、交通信号、环境物体等信息进行有效捕捉&#x…

数据可视化训练第二天(对比Python与numpy中的ndarray的效率并且可视化表示)

绪论 千里之行始于足下&#xff1b;继续坚持 1.对比Python和numpy的性能 使用魔法指令%timeit进行对比 需求&#xff1a; 实现两个数组的加法数组 A 是 0 到 N-1 数字的平方数组 B 是 0 到 N-1 数字的立方 import numpy as np def numpy_sum(text_num):"""…

一起深度学习(AlexNet网络)

AlexNet神经网络 代码实现&#xff1a; 代码实现&#xff1a; import torch from torch import nn from d2l import torch as d2lnet nn.Sequential(# 采用了11*11的卷积核来捕捉对象&#xff0c;因为原始输入数据比较大#步幅为4 &#xff0c;可减少输出的高度核宽度。#输出通…

全栈开发之路——前端篇(6)生命周期和自定义hooks

全栈开发一条龙——前端篇 第一篇&#xff1a;框架确定、ide设置与项目创建 第二篇&#xff1a;介绍项目文件意义、组件结构与导入以及setup的引入。 第三篇&#xff1a;setup语法&#xff0c;设置响应式数据。 第四篇&#xff1a;数据绑定、计算属性和watch监视 第五篇 : 组件…

基于springboot实现疾病防控综合系统项目【项目源码+论文说明】

基于springboot实现疾病防控综合系统演示 摘要 在如今社会上&#xff0c;关于信息上面的处理&#xff0c;没有任何一个企业或者个人会忽视&#xff0c;如何让信息急速传递&#xff0c;并且归档储存查询&#xff0c;采用之前的纸张记录模式已经不符合当前使用要求了。所以&…

正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-11.1,11.2-BSP文件目录组织

前言&#xff1a; 本文是根据哔哩哔哩网站上“正点原子[第二期]Linux之ARM&#xff08;MX6U&#xff09;裸机篇”视频的学习笔记&#xff0c;在这里会记录下正点原子 I.MX6ULL 开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了正点原子教学视频和链接中的内容。…

Labels and Databases for Mac:强大的标签与数据库管理工具

Labels and Databases for Mac是一款集标签制作与数据库管理于一体的强大工具&#xff0c;专为Mac用户打造&#xff0c;旨在提供高效、便捷的标签制作与数据管理体验。 这款软件拥有丰富的内置标签格式&#xff0c;用户可轻松创建各种标签、信封和卡片&#xff0c;满足个性化需…