linux万字图文学习进程信号

news2025/1/12 7:46:26

1. 信号概念

信号是进程之间事件异步通知的一种方式,属于软中断。

1.1 linux中我们常用Ctrl+c来杀死一个前台进程

  • 1. Ctrl-C 产生的信号只能发给前台进程。一个命令后面加个&可以放到后台运行,这样Shell不必等待进程结束就可以接受新的命令,启动新的进程。
  • 2. Shell可以同时运行一个前台进程和任意多个后台进程,只有前台进程才能接到像 Ctrl-C 这种控制键产生的信号。
  • 3. 前台进程在运行过程中用户随时可能按下 Ctrl-C 而产生一个信号,也就是说该进程的用户空间代码执行到任何地方都有可能收到 SIGINT 信号而终止,所以信号相对于进程的控制流程来说是异步(Asynchronous)的。

2.常见的信号

用kill -l命令可以察看系统定义的信号列表

  • 每个信号都有一个编号和一个宏定义名称,这些宏定义可以在signal.h中找到
  • 编号34以上的是实时信号
  • 这些信号各自在什么条件下 产生,默认的处理动作是什么,在signal(7)中都有详细说明: man 7 signal

3. 信号本质

        信号是软件中断,是在软件层次上对中断机制的一种模拟,在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。信号是异步的,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底什么时候到达。

        信号是进程间通信机制中唯一的异步通信机制,可以看作是异步通知,通知接收信号的进程有哪些事情发生了。信号机制经过POSIX实时扩展后,功能更加强大,除了基本通知功能外,还可以传递附加信息。

4. 信号来源

4.1  通过终端按键产生信号

SIGINT的默认处理动作是终止进程,SIGQUIT的默认处理动作是终止进程并且Core Dump

4.1.1 什么是Core Dump

当一个进程要异常终止时,可以选择把进程的用户空间内存数据全部 保存到磁 盘上,文件名通常是core,这叫做Core Dump。进程异常终止通常是因为有Bug,比如非法内存访问导致段错误, 事后可以用调试器检查core文件以查清错误原因,这叫做Post-mortem Debug(事后调试)。一个进程允许 产生多大的core文件取决于进程的Resource Limit(这个信息保存 在PCB中)。默认是不允许产生core文件的, 因为core文件中可能包含用户密码等敏感信息,不安全。

在开发调试阶段可以用ulimit命令改变这个限制,允许 产生core文件。 首先用ulimit命令改变Shell进程的Resource Limit $ ulimit -c 1024 允许core文件最大为1024K

        

 

4.1.2终端按键的信号捕捉 

#include <stdio.h>
#include <signal.h>
int count = 0;
void handler(int sig)
{
    printf("the catched count is %d  ",++count);

    printf("catch a sig : %d\n", sig);
}
int main()
{
    signal(2, handler); // 信号是可以被自定义捕捉的,siganl函数就是来进行信号捕捉的

    while (1)
        ;
    return 0;
}

4.2. 调用系统函数向进程发信号

常用的kill 还有pkill和killall等

kill命令是调用kill函数实现的。kill函数可以给一个指定的进程发送指定的信号。raise函数可以给当前进程发送指定 的信号(自己给自己发信号)。

#include <signal.h>
int kill(pid_t pid, int signo);
int raise(int signo);
这两个函数都是成功返回0,错误返回-1。

#include <stdlib.h>
void abort(void);
就像exit函数一样,abort函数总是会成功的,所以没有返回值。
abort函数使当前进程接收到信号而异常终止。

4.3  由软件条件产生信号

SIGPIPE是一种由软件条件产生的信号,在管道中读端被关闭了,写端一直写

Broken pipe: write to pipe with no readers.
管道破裂。这个信号通常在进程间通信产生,比如采用FIFO(管道)通信的两个进程,
读管道没打开或者意外终止就往管道写,
写进程会收到SIGPIPE信号。此外用Socket通信的两个进程,
写进程在写Socket的时候,读进程已经终止。
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
调用alarm函数可以设定一个闹钟,也就是告诉内核在seconds秒之后给当前进程发SIGALRM信号, 该信号的默认处理动作是终止当前进程。
可以重复设置,新的会覆盖旧的并返回上次闹钟剩余的时间,可以设置为0,意思就是取消闹钟,并返回上次闹钟剩余的时间

4.4 硬件异常产生信号

硬件异常被硬件以某种方式被硬件检测到并通知内核,然后内核向当前进程发送适当的信号。例如当前进程执行了除以0的指令,CPU的运算单元会产生异常,内核将这个异常解释 为SIGFPE信号发送给进程。再比如当前进程访问了非法内存地址(野指针),MMU会产生异常,内核将这个异常解释为SIGSEGV信号发送给进程。

4.4.1 硬件异常产生信号的捕获

并且硬件异常后程序将并不会继续向下执行

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
int count = 0;
int count2=0;
void handler(int sig)
{
    printf("the catched count is %d count2:%d ",++count,count2);

    printf("catch a sig : %d\n", sig);
}
int main()
{
    signal(11, handler); // 信号是可以被自定义捕捉的,siganl函数就是来进行信号捕捉的

    while (1){
        sleep(1);
        count2++;
        int *p=NULL;
        *p=100;
    }
    return 0;
}

5. 信号的分类

1、可靠信号与不可靠信号

不可靠信号:

Linux信号机制基本上是从Unix系统中继承过来的。早期Unix系统中的信号机制比较简单和原始,后来在实践中暴露出一些问题,因此,把那些建立在早期机制上的信号叫做"不可靠信号",信号值小于SIGRTMIN(Red hat 7.2中,SIGRTMIN=32,SIGRTMAX=63)的信号都是不可靠信号

不可靠信号主要的问题是:

  • 进程每次处理信号后,就将对信号的响应设置为默认动作。在某些情况下,将导致对信号的错误处理;因此,用户如果不希望这样的操作,那么就要在信号处理函数结尾再一次调用signal(),重新安装该信号。
  • 信号可能丢失,后面将对此详细阐述。 如果在进程对某个信号进行处理时,这个信号发生多次,对后到来的这类信号不排队,那么仅传送该信号一次,即发生了信号丢失。
    因此,早期unix下的不可靠信号主要指的是进程可能对信号做出错误的反应以及信号可能丢失。

Linux支持不可靠信号,但是对不可靠信号机制做了改进:在调用完信号处理函数后,不必重新调用该信号的安装函数(信号安装函数是在可靠机制上的实现)。因此,Linux下的不可靠信号问题主要指的是信号可能丢失

可靠信号

信号值位于SIGRTMIN和SIGRTMAX之间的信号都是可靠信号,可靠信号克服了信号可能丢失的问题Linux在支持新版本的信号安装函数sigation()以及信号发送函数sigqueue()的同时,仍然支持早期的signal()信号安装函数,支持信号发送函数kill()。

       注:不要有这样的误解:由sigqueue()发送、sigaction安装的信号就是可靠的。事实上,可靠信号是指后来添加的新信号(信号值位于SIGRTMIN及SIGRTMAX之间);不可靠信号是信号值小于SIGRTMIN的信号信号的可靠与不可靠只与信号值有关,与信号的发送及安装函数无关。目前linux中的signal()是通过sigation()函数实现的,因此,即使通过signal()安装的信号,在信号处理函数的结尾也不必再调用一次信号安装函数。同时,由signal()安装的实时信号支持排队,同样不会丢失。

       对于目前linux的两个信号安装函数:signal()及sigaction()来说,它们都不能把SIGRTMIN以前的信号变成可靠信号(都不支持排队,仍有可能丢失,仍然是不可靠信号),而且对SIGRTMIN以后的信号都支持排队。这两个函数的最大区别在于,经过sigaction安装的信号都能传递信息给信号处理函数(对所有信号这一点都成立),而经过signal安装的信号却不能向信号处理函数传递信息。对于信号发送函数来说也是一样的。

6. 信号处理常见方式

  • 1. 忽略此信号。
  • 2. 执行该信号的默认处理动作。
  • 3. 提供一个信号处理函数,要求内核在处理该信号时切换到用户态执行这个处理函数,这种方式称为捕捉 (Catch)一个信号。

7. 信号集操作函数

7.1 信号一些相关概念

  • 实际执行信号的处理动作称为信号递达(Delivery)
  • 信号从产生到递达之间的状态,称为信号未决(Pending)
  • 进程可以选择阻塞 (Block )某个信号。
  • 被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作.
  • 注意,阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。

7.2 信号在内核中的表示

 

  • 每个信号都有两个标志位分别表示阻塞(block)和未决(pending),还有一个函数指针表示处理动作。信号产生时,内核在进程控制块中设置该信号的未决标志,直到信号递达才清除该标志。在上图的例子中,SIGHUP信号未阻塞也未产生过,当它递达时执行默认处理动作。
  • SIGINT信号产生过,但正在被阻塞,所以暂时不能递达。虽然它的处理动作是忽略,但在没有解除阻塞之前不能忽略这个信号,因为进程仍有机会改变处理动作之后再解除阻塞。
  • SIGQUIT信号未产生过,一旦产生SIGQUIT信号将被阻塞,它的处理动作是用户自定义函数sighandler。 如果在进程解除对某信号的阻塞之前这种信号产生过多次,将如何处理?POSIX.1允许系统递送该信号一次或多次。Linux是这样实现的:常规信号在递达之前产生多次只计一次,而实时信号在递达之前产生多次可以依次放在一个队列里。注意此处不讨论实时信号。 

7.3 sigset_t

从上图来看,每个信号只有一个bit的未决标志,非0即1,不记录该信号产生了多少次,阻塞标志也是这样表示的。 因此,未决和阻塞标志可以用相同的数据类型sigset_t来存储,sigset_t称为信号集,这个类型可以表示每个信号 的“有效”或“无效”状态,在阻塞信号集中“有效”和“无效”的含义是该信号是否被阻塞,而在未决信号集中“有效”和“无效”的含义是该信号是否处于未决状态。

阻塞信号集也叫做当 前进程的信号屏蔽字(Signal Mask),这里的“屏蔽”应该理解为阻塞而不是忽略。

7.4 信号集操作函数

sigset_t类型对于每种信号用一个bit表示“有效”或“无效”状态,至于这个类型内部如何存储这些bit则依赖于系统 实现,从使用者的角度是不必关心的,使用者应该调用以下函数来操作sigset_ t变量,而不应该对它的内部数据做 任何解释,比如用printf直接打印sigset_t变量是没有意义的

#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); //在信号集查找是否有该信号

这四个函数都是成功返回0,出错返回-1。sigismember是一个布尔函数,用于判断一个信号集的有效信号中是否包含 某种 信号,若包含则返回1,不包含则返回0,出错返回-1。


 

7.5 调用函数sigprocmask可以读取或更改进程的信号屏蔽字(阻塞信号集)。

所有信号产生,最终都要有OS来进行执行,为什么?OS是进程的管理者 
信号的处理是否是立即处理的?在合适的时候 

信号如果不是被立即处理,那么信号是否需要暂时被进程记录下来?记录在哪里最合适呢? 
一个进程在没有收到信号的时候,能否能知道,自己应该对合法信号作何处理呢? 
如何理解OS向进程发送信号?
能否描述一下完整的发送处理过程?

7.6 sigpending

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

 7.7 信号阻塞时未决信号的打印 

#include<iostream>
#include<signal.h>
#include<unistd.h>

using namespace std;
void my_printf_sig(sigset_t* a){
    for(int i=0;i<65;i++){
        if(sigismember(a,i)){
            cout<<"1";
            //cout<<"         "<<i<<"**********";

        }
        else{
            cout<<"0";
        }
    }
}

int main(){
    sigset_t block,pending;
    sigemptyset(&block);
    for(int i=0;i<65;i++){//将所有的信号都屏蔽了,但是kill -9 还是可以生效。
        sigaddset(&block,i);
    }
    sigprocmask(SIG_BLOCK,&block,NULL);

    while(1){
        sigpending(&pending);
        my_printf_sig(&pending);
        cout<<endl;
        sleep(1);
        cout<<"my_pid is"<<getpid()<<endl;

    }
}

7.8 内核如何实现信号的捕捉 

如果信号的处理动作是用户自定义函数,在信号递达时就调用这个函数,这称为捕捉信号。由于信号处理函数的代码 是在用户空间的,处理过程比较复杂,举例如下: 用户程序注册了SIGQUIT信号的处理函数sighandler。 当前正在执行 main函数,这时发生中断或异常切换到内核态。 在中断处理完毕后要返回用户态的main函数之前检查到有信号 SIGQUIT递达。 内核决定返回用户态后不是恢复main函数的上下文继续执行,而是执行sighandler函 数,sighandler 和main函数使用不同的堆栈空间,它们之间不存在调用和被调用的关系,是 两个独立的控制流程。 sighandler函数返 回后自动执行特殊的系统调用sigreturn再次进入内核态。 如果没有新的信号要递达,这次再返回用户态就是恢复 main函数的上下文继续执行了。

7.9 sigaction

7.9.1 siaction的定义 

 

  • sa_handler 是一个函数指针,指定信号处理函数,这里除可以是咱们自定义的处理函数外,还可以为SIG_DFL(采用默认的处理方式)或SIG_IGN(忽略信号)。它的处理函数只有一个参数,即信号值。
  •    sa_mask 是一个信号集,它可以指定在信号处理程序执行过程中哪些信号应当被屏蔽,在调用信号捕获函数前,该信号集要加入到信号的信号屏蔽字中。
  •    sa_flags 中包含了很多标志位,是对信号进行处理的各个选择项。它的常见可选值如下表所示

8. volatile:

volatile 作用:保持内存的可见性,告知编译器,被该关键字修饰的变量,不允许被优化,对该变量 的任何操作,都必须在真实的内存中进行操作 

9. SIGCHLD信号

父进程调 用sigaction将SIGCHLD的处理动作 置为SIG_IGN,这样fork出来的子进程在终止时会自动清理掉,不 会产生僵尸进程,也不会通知父进程。系统默认的忽 略动作和用户用sigaction函数自定义的忽略 通常是没有区别的,但这是一个特例。此方法对于Linux可用,但不保证 在其它UNIX系统上都可用。

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

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

相关文章

Git的ssh方式如何配置,如何通过ssh方式拉取和提交代码

git的ssh配置 HTTPS和SSH的区别设置SSH方式配置单个仓库配置账户公钥 大家通过git拉取代码的时候&#xff0c;一般都是通过http的方式&#xff0c;简单方便。但是细心的童鞋肯定也注意到Git也是支持ssh方式的。可能很多人也试过使用这个方式&#xff0c;但是好像没有那么简单。…

企业架构LNMP学习笔记57

MongoDB的安全设置&#xff1a; 安全&#xff1a; MongoDB的安全事件&#xff1a; 2017年年初&#xff1a; 利用SMB漏洞可以获得系统最高权限。wannacry勒索病毒。当年确实遇到过这个事情。 比特币的概念&#xff1a;开源软件 P2P网络 P2P形式的数字货币。交易记录公开透明。…

Mysql--Java的JDBC编程

Java的数据库编程&#xff1a;JDBC JDBC&#xff0c;即Java Database Connectivity&#xff0c;java数据库连接。是一种用于执行SQL语句的Java API&#xff0c;它是 Java中的数据库连接规范。 下载驱动包作为项目的依赖&#xff0c;数据库驱动包的版本要和数据库服务器的版本…

前端中的跨域请求及其解决方案

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 跨域&#xff08;Cross-Origin&#xff09;⭐CORS&#xff08;跨域资源共享&#xff09;⭐JSONP&#xff08;JSON with Padding&#xff09;⭐代理服务器⭐ WebSocket⭐服务器设置响应头⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a…

Spring + vue 项目部署(全网最详细教程_含内网穿透部署)

本项目以Springboot 2.7.11和vue2做参考示例 第一步 展示前后端代码的成品 前端Vue 后端Java 项目写完后&#xff0c;差不多就是这个样子&#xff0c;仅供参考&#xff01; 第二步 开始打包前后端项目 前端打包的方式有以下几种&#xff1a; 方法1: #直接打包&#xff0…

【数据结构】【C++】红黑树RBTree的模拟实现(平衡搜索二叉树)

【数据结构】&&【C】红黑树的模拟实现(平衡搜索二叉树&#xff09; 一.红黑树的性质二.红黑树的模拟实现1.结点的定义2.搜索树的插入3.变色向上处理4.旋转变色 三.红黑树与AVL树的差别四.完整代码 一.红黑树的性质 1.什么是红黑树&#xff1f; 红黑树是一种搜索二叉树…

页面上下左右滑动事件

1.下载插件 npm install vue-touchnext -S 2.main.js加入以下代码 import VueTouch from vue-touch Vue.use(VueTouch, { name: v-touch }) VueTouch.config.swipe { threshold: 50 //设置左右滑动的距离 } 3.完整代码 <template><div><v-touch swipe…

Java 基础——运行第一个Java程序

【学习笔记】Java 基础——运行第一个Java程序 关键词&#xff1a;Java、Spring Boot、Idea、数据库、一对一、培训、教学本文主要内容是在IDEA中运行第一个Java程序&#xff1a;Hello World计划30分钟完成&#xff0c;请同学尽量准备工具有学习需要请联系&#xff1a;xujian_…

css多个物体椭圆旋转

实现效果 html代码 <div class"background-img"><div class"area"><div class"ball ball1"></div><div class"ball ball2"></div><div class"ball ball3"></div><div …

道路空间功率谱密度与时间功率谱密度(笔记)

1.先上代码其中之一 clc clear close all %% SimTime200; dt0.01;%仿真步长 time0:dt:SimTime; sim_step length(time); Ntlength(time); % 采样点&#xff08;可能要修改&#xff09; u10; % m/s df1/(Nt*dt); % 采样频率间隔 f0:df:1/(2*dt); % 采用频率一…

数据脱敏的风险量化评估介绍

1、背景介绍 当前社会信息化高速发展&#xff0c;网络信息共享加速互通&#xff0c;数据呈现出规模大、流传快、类型多以及价值密度低的特点。人们可以很容易地对各类数据实现采集、发布、存储与分析&#xff0c;然而一旦带有敏感信息的数据被攻击者获取将会造成个人隐私的严重…

NTIRE2023-RTSR-Track2 冠军方案详解

编辑 | Happy 首发 | AIWalker 链接 | https://mp.weixin.qq.com/s/JQ5g9yn_OdjR8hi_tWc4jA arXiv:2305.02126 , cvpr2023 , code , video 本文了一个实时&轻量图像超分方案Bicubic&#xff0c;它通过下采样模块降低图像分辨率以减少计算量&#xff0c;在网络尾部采用…

R语言绘制染色体变异位置分布图,RIdeogram包

变异位点染色体分布图 今天分享的内容是通过RIdeogram包绘制染色体位点分布图&#xff0c;并介绍一种展示差异位点的方法。 在遗传学研究中&#xff0c;通过测序等方式获得了基因组上某些位置的基因型信息。 如下表&#xff0c;第一列是变异位点的ID&#xff0c;第二列是染色体…

Python 字典的定义

视频版教程 Python3零基础7天入门实战视频教程 字典dict 字典也是Python提供的一种常用的数据结构&#xff0c;它用于存放具有映射关系的数据。 字典的定义 比如有成绩表数据——语文:68&#xff0c;数学:98&#xff0c;英语: 76&#xff0c;这组数据看上去像两个列表&…

每天几道Java面试题:集合(第四天)

目录 第四幕 、第一场&#xff09;大厦楼下门口第二场&#xff09;大门口 友情提醒 背面试题很枯燥&#xff0c;加入一些戏剧场景故事人物来加深记忆。PS:点击文章目录可直接跳转到文章指定位置。 第四幕 、 第一场&#xff09;大厦楼下门口 【面试者老王&#xff0c;门卫甲…

浅谈C++|STL之stack+queue+priority_queue篇

一.stack基本概念 栈&#xff08;Stack&#xff09;是一种常见的线性数据结构&#xff0c;遵循后进先出&#xff08;Last-In-First-Out&#xff0c;LIFO&#xff09;的原则。类似于我们在现实生活中堆叠书本或盘子的方式&#xff0c;最后放入的元素最先被取出。 在栈中&#…

【基于多输出方向的同步异步日志系统】

本项目涉及的到所有源码见以下链接&#xff1a; https://gitee.com/ace-zhe/wz_log 一、项目简介 1.日志的概念&#xff08;白话版&#xff09; 日志类似于日记&#xff0c;通常是指对完成某件事情的过程中状态等的记录&#xff0c;而计算机中的日志是指日志数据&#xff0c…

Python实现的mqtt客户端工具分享,小巧且超轻量级(python+tkinter+paho.mqtt)

mqtt协议调试时需要个客户端工具&#xff0c;但网上找的体积包都很大&#xff0c;都不够小巧和便携。于是趁周末时间用python搞出来了个客户端工具&#xff0c;使用pythontinkerpaho.mqtt实现。源码量很少但功能不弱&#xff0c;相当的轻量级。分享给有需要的小伙伴&#xff0c…

论文写作指导手册

TIPS&#xff1a;最近我们上线了“AI写作大师”&#xff0c;后续会继续围绕论文、文章、文案写相关的文章&#xff0c;来验证写作大师能力如何&#xff0c;敬请持续关注&#xff08; openrabbit.net&#xff09;&#xff5e; 一、论文选题 选题是论文研究的第一步&a…

Prometheus+Grafana可视化监控【Nginx状态】

文章目录 一、安装Docker二、安装Nginx(Docker容器方式)三、安装Prometheus四、安装Grafana五、Pronetheus和Grafana相关联六、安装nginx_exporter七、Grafana添加Nginx监控模板 一、安装Docker 注意&#xff1a;我这里使用之前写好脚本进行安装Docker&#xff0c;如果已经有D…