Linux:信号保存与处理

使用kill -l命令查看信号:

信号量和信号确实一点关系没有

信号是操作系统发出的进程与进程之间的通知于中断,是进程之间时间异步通知的一种方式

先了解同步通信:同步通信是一种比特同步通信技术,要求发收双方具有同频同相的同步时钟信号,只需在传送报文的最前面附加特定的同步字符,使发收双方建立同步,此后便在同步时钟的控制下逐位发送/接收。

优点是效率高,缺点是硬件要求高

什么叫异步通信:异步通信是指发送方在向接收方发送数据的时候可以有任意的时间间隔,没有严格的时序要求,而接收方需要一直存在,来接收数据

优点是便宜,缺点是效率低

信号量是一个计时器,表示可用资源的数量

进程产生信号的方式有四种

通过终端按键发出信号

例如你输入ctrl c的时候可以中断进程,ctrl \是离开信号,ctrl z是暂停信号

这些信号分别对应了上图的2信号,3信号,20信号

通过系统调用函数产生信号

kill命令在实现的时候也是调用kill函数实现的,kill函数可以给指定进程发送指定的信号

#include<signal.h>

int kill(pid_t pid,int signo);

来写一个kill函数的调用:

mykill:

#include<stdio.h>
#include<signal.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>
int main(int argc,char *argv[]){
    if(argc!=3){
        perror("kill false");
        return 1;
    }
    pid_t pid=atoi(argv[2]);
    int sig=atoi(argv[1]);
    kill(pid,sig);//啊啊啊啊,一开始写反参数的顺序了,气死了
    return 0;
}

myprocess.c:

#include<stdio.h>
#include<signal.h>
#include<sys/types.h>
#include<unistd.h>
void handler(int sig){
    printf("get a sig form myskill,sig==%d",sig);
}

int main(){
    signal(2,handler);//signal是个系统调用函数,第一个参数是输入的信号是几,第二个参数有三种类型
    //此处的类型为一个函数指针,在执行该语句的时候执行一个handler函数,这个函数是要自己声明的
    //格式为int handler(int sig)
    while(1){
        printf("a process,pid==%d\n",getpid());
        fflush(stdout);
        sleep(1);
    }
    return 0;
}

除了kill函数还有abort函数,使当前进程接收到信号而异常终止

#include<stdlib.h>

void abort(void)

用go也写一个:

package main

import (
	"fmt"
	"os"
	"os/signal"
	"syscall"
)

func main() {
	sigs := make(chan os.Signal, 1)
	done := make(chan bool, 1)
	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) //将sigint和sigterm的信号传给sig
	//process start
	//get a sig interrupt,这个interrupt就是syscall.SIGINT返回的信号
	go func() {
		sig := <-sigs//将信号发送给sig
		fmt.Println("get a sig", sig)
		done <- true//标为结束
	}()
	fmt.Println("process start")
	<-done
}

myabort.c:

#include<signal.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>
void handler(int sig){
    printf("get a sig");
}
int main(){
    signal(3,handler);//捕获信号
    while(1){
        sleep(1);
        abort();//捕获之后就终止进程,因为abort就是终止
    }
    return 0;
}

那abort()函数收到信号会终止,那么是不是我让他捕捉所有的信号,就不能终止我的进程了?

void catchSig(int signum)
{
    printf("get a signal:%d",signum);
}
int main()
{
    for(int sig = 1; sig <= 31; sig++) {
        signal(sig, catchSig);
    }
    while(true){
        sleep(1);
    }
    return 0;
}

信号9不能被自动捕捉哦

但是我们还是可以用kill -9 pid来终止该进程:

还有raise,是给自己当前的进程发送信号:

#include<stdio.h>
#include<signal.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>
void handler(int sig){
    printf("get a sig,sig==%d\n",sig);
    fflush(stdout);
}
int main(){
    signal(3,handler);
    while(1){
        sleep(1);
        raise(3);//给当前进程发送信号3
    }
    return 0;
}

由软件条件产生的信号

上图的信号13是一个管道中由软件产生的信号,读端关闭,写端一直写入的时候,os会直接啥的写端进程,怎么杀掉呢?通过向目标文件发送SIGPIPE(13)信号,终止目标进程,这就叫软件条件

由硬件异常生成的信号

硬件发生异常后以某种方式被检测到并且通知到内核,内核再向进程发送适当的信号(也是设计到软件条件产生的信号)

软件条件和硬件异常的产生的信号的区别:软件条件就是操作系统和应用程序内部生成的,硬件信号是物理设备(外设)通过中断机制发送

进程递达,阻塞和捕捉

一个信号在发送给一个进程的时候需要三个阶段:这个信号我收不收?这个信号我要不要写入?我写入/或者不写入用不用?

表现出来就是一个task_struct下的三个标志位:block(要不要阻塞,对应前面的这个信号我收不收),pending(bit位的位置表示编号,不同位置代表不同的信号,对应位置的信号写入,则置为1,否则置为0),例如,block 位图的最低一个比特位就表示是否对 1 号信号进行阻塞,pending 位图的最低一个比特位就表示是否收到 1 号信号。

bandler(这个信号我要不要忽视=这个信号我用不用)

阻塞和忽略是不同的,阻塞是不接收这个信号,忽略是接收这个信号,写入该信号,但是不执行这个信号;也就是说信号被阻塞就不能递达,递达之后才能选择你能不能忽略

信号递达:执行信号的处理动作称为信号递达(Delivery)

信号从产生到递达(从有信号到执行信号的状态)称为信号未决(Pending)

信号集

能表示多个信号的一种数据类型,叫信号集(signal set,信号集其实就是 sigset_t 类型数据结构)

在阻塞信号集下的每个信号的“有效”和“无效”指的是这个信号有没有被阻塞,在未决信号集下的有无效是指是否处于未决状态

阻塞信号集也叫信号屏蔽字(Signal Mask),这里的屏蔽的意思==阻塞

sigset_t下的每个信号都用每个bit来判定信号是有效还是无效

设置信号集

通过函数设置信号集

#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);

我们来解释一下这些函数

sigemptyset(),用于清空(初始化)信号集,使其中所有的信号对应的bit清零,表示该信号集不包含有效信号

sigfillset(),也是初始化信号集内的信号,但是与sigemptyset()的区别是sigfillset()是置位,全部置为有效信号

每次使用sigset_t类型的变量之前,一定要调用sigemptyset或sigfillset做初始化,初始化后就可以调用其他的如sigaddset或sigdelset做添加或删除某种有效信号

这四个函数的返回值都是成功返回0,失败返回-1

而sigismember是个bool类型的函数,用于判断该信号集是否包含某种信号,包含返回1,不包含返回0,出错返回-1

维护信号集

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

#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
// 返回值:若成功则为0,若出错则为-1

若oset是非空指针,则读取进程的当前信号屏蔽字通过oset参数传出(输出型参数)

如果set是非空指针,则更改进程的信号屏蔽字,参数how指示如何更改

如果oset和set都是非空指针,则将原来的信号屏蔽字备份到oset里,然后根据how和set来修改信号屏蔽字

若mask是我们当前的信号屏蔽字,这是how的可选选项

如果调用 sigprocmask 解除了对当前若干个未决信号的阻塞,则在sigprocmask返回前,至少将其中一个信号递达。

原理:一个信号被阻塞,也就是在信号屏蔽字内的时候,就处于未决状态;当调用过sigprocmask就意味着至少有一个信号得进入递达状态(内核至少递送一个信号)

读取当前进程的信号集

#include <signal.h>
sigpending(&s);

sigpending 函数可以读取当前进程的未决信号集,通过 s 参数传出。调用成功则返回0,出错则返回-1。

信号捕捉的过程

信号在内核态返回到用户态进行信号的检测和处理

如果处理信号的函数是用户自定义的函数,那么在信号递达的时候调用这个函数,称为捕捉信号。

在信号捕捉中,一共会涉及到四次状态转换

可重入函数

可重入函数(reentrant function)是指在多个执行流下,能够被同时调用而不会产生冲突或错误的函数。

这种函数能够保证在任意时刻,无论被同一个还是不同执行流调用,都能正确地完成预期的功能。

如果一个函数符合以下条件之一则是不可重入的:

调用了malloc或free,因为malloc也是用全局链表来管理堆的。
调用了标准I/O库函数,标准I/O库的很多实现都以不可重入的方式使用全局数据结构。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

若依框架篇-若依框架搭建具体过程、后端源代码分析、功能详解(权限控制、数据字典、定时任务、代码生成、表单构建、接口测试)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 若依框架概述 1.1 若依构建 1.2 后端项目搭建 1.3 前端项目搭建 2.0 利用若依框架生成前后端代码案例 3.0 功能详解 3.1 功能详解 - 权限控制 3.1.1 使用权限控制…

Djang学习- URL反转

代码中url书写规范&#xff1a; 、 url反向解析 urls: path(test/url, views.test_url),path(test_result/<int:age>, views.test_result, name"rl") views: def test_url(request):return render(request, test_url.html)def test_result(request,age):re…

Lintcode 3686 · N 叉树的直径【中等 DFS/BFS java答案】

题目 题目链接&#xff1a;https://www.lintcode.com/problem/3686/ 思路 1.利用map创建图 2.找到直径的其中一个端点last,通过bfs可以实现 3.从last出发&#xff0c;再次bfs,有多少层&#xff0c;直径就是多少Java代码 /*** Definition for Undirected graph.* class Undir…

100. UE5 GAS RPG 显示范围魔法的攻击范围

在这一篇里&#xff0c;我们将制作一个范围魔法&#xff0c;释放魔法时&#xff0c;我们将在鼠标拾取位置绘制一个魔法光圈&#xff0c;用于显示技能释放时攻击的范围&#xff0c;然后再次点击可以释放技能。 创建贴花类 魔法范围标识的光圈&#xff0c;我们采用贴花实现&…

2014年国赛高教杯数学建模B题创意平板折叠桌解题全过程文档及程序

2014年国赛高教杯数学建模 B题 创意平板折叠桌 某公司生产一种可折叠的桌子&#xff0c;桌面呈圆形&#xff0c;桌腿随着铰链的活动可以平摊成一张平板&#xff08;如图1-2所示&#xff09;。桌腿由若干根木条组成&#xff0c;分成两组&#xff0c;每组各用一根钢筋将木条连接…

44 C 语言输入输出流、scanf 与 printf 函数详解、清除输入缓冲区

目录 1 文件基本介绍 1.1 文件的主要功能 1.2 输入输出流 2 C 语言中的输入与输出 2.1 输入 2.2 输出 2.3 标准文件与文件指针 3 scanf() 函数详解 3.1 功能描述 3.2 函数原型 3.3 常用格式说明符 3.4 返回值 3.5 注意事项 3.5.1 处理空白字符 3.5.2 防止缓冲区…

Linux命令进阶

grep 从文件中搜索字符串 grep "字符串" 文件 参数&#xff1a; -n 显示行号 -R 递归及子目录例如 grep "hello" log.c grep "main" * -nRfind 在指定路径下搜索文件 find 路径 -name 文件名find /home/linux -name hello.c //在/home/linux…

精选优质不收费数据恢复软件全解析

数据已经成为了我们生活和工作中无比珍贵的资产。然而我们在使用中总会因为各种意外导致数据丢失。今天&#xff0c;我们就来深入了解一些优秀的不收费的数据恢复软件&#xff0c;看看他们如果帮我们力挽狂澜。 1.福晰数据恢复 链接直达&#xff1a;https://www.pdf365.cn/fo…

基于Arduino的简易收音机

DIY FM收音机&#xff1a;使用Arduino和Si4703模块打造 引言 在本项目中&#xff0c;我们将使用Arduino Nano和Si4703 FM调谐模块来构建一个功能完备的FM收音机接收器。这个易于跟随的指南非常适合想要深入无线电频率和无线通信世界的业余爱好者和电子爱好者。 Si4703模块是…

西门子网络程序传输,无需开通网络驱动器直接接入底层,支持各类数控 如发那科、三菱 、新代、海德汉、广数、精雕、马扎克等等

有关西门子的程序传输问题&#xff0c;大家一般是通过文件共享、ftp、网络驱动器等方式&#xff0c;其中828D还需要授权开通网络启动器 下面介绍一种方式直接进入西门子Linux底层系统实现和NCK的文件交互功能 软件截图如下 功能表如下 机床程序上载至电脑 电脑程序下传…

2. MySQL数据库基础

一、数据库的操作 1. 显示当前的数据库 SHOW DATABASES;2. 创建数据库 语法&#xff1a; CREATE DATABASE [IF NOT EXISTS] db_name [create_specification...];//create_specification包括&#xff1a;[DEFAULT] CHARACTER SET charset_name[DEFAULT] COLLATE collation_n…

性能测试最佳实践的思考

性能测试是软件开发和应用过程中至关重要的环节。它是评估系统性能、稳定性和可扩展性的有效手段&#xff0c;可以确保软件在真实环境中高效运行。在现代技术快速发展的时代&#xff0c;性能测试的重要性愈发显著。 性能测试在软件开发和应用过程中的重要性不可低估。它是保障…

RabbitMQ消息队列MQ脑裂(网络分区)整理分析

文章目录 RabbitMQ 的集群架构基础什么是MQ脑裂检测网络分区RabbitMQ 网络分区导致脑裂的原因• 多个节点认为自己是主节点&#xff1a;• 节点间状态不一致&#xff1a;• 集群的不可用性和错误恢复&#xff1a; RabbitMQ 网络分区引发脑裂的常见场景队列镜像不同步HA&#xf…

【H2O2|全栈】JS入门知识(二)

目录 JS 前言 准备工作 运算符 算数运算符 比较运算符 自增、自减运算符 逻辑运算符 运算符的优先级 分支语句 if-else语句 switch语句 三元表达式 结束语 JS 前言 本系列博客主要分享JavaScript的基础语法知识&#xff0c;本期为第二期&#xff0c;包含一些简…

网络变压器在楼宇电梯控制器中的重要作用

Hqst盈盛&#xff08;华强盛&#xff09;电子导读&#xff1a;今天分享的是网络变压器在楼宇电梯控制器中的重要作用... 网络变压器在楼宇电梯控制器中起着至关重要的作用,工程师总结有以下是其主要应用方面&#xff1a; 一、信号隔离与增强 络变压器可以实现信号的隔离&#…

Qt-界面优化选择器的用法(70)

目录 描述 使用 类型选择器 ID 选择器 并集选择器 子控件选择器 伪控制器 描述 QSS 的选择器⽀持以下⼏种 选择器⽰例说明全局选择器*选择所有的 widget.类型选择器 (type selector)QPushButton选择所有的 QPushButton 和其⼦类的控件.类选择器 (class selector).QPus…

【Golang】关于Go语言中的定时器原理与实战应用

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

QRTCN区间预测 | Matlab实现QRTCN时间卷积神经网络分位数回归区间预测

区间预测 | Matlab实现QRTCN时间卷积神经网络分位数回归区间预测 目录 区间预测 | Matlab实现QRTCN时间卷积神经网络分位数回归区间预测预测效果基本介绍模型特性程序设计参考资料预测效果 基本介绍 Matlab实现QRTCN时间卷积神经网络分位数回归区间预测 QRTCN(Quantile Regres…

2.mybatis-plus3.x的使用

官网&#xff1a;简介 | MyBatis-Plushttps://baomidou.com/introduce/ 3.X版本插件使用、 1. 分页插件 配置插件&#xff08;不能用的情况去官网看看最新的&#xff09; Configuration MapperScan("scan.your.mapper.package") public class MybatisPlusConfig …

Django 定义使用模型,并添加数据

教材&#xff1a; Python web企业级项目开发教程&#xff08;黑马程序员&#xff09;第三章 模型 实验步骤&#xff1a; 1.创建项目和应用 前置步骤可看前文&#xff0c;进入到指定文件位置后创建 django-admin startproject mysite python manage.py startapp app01 2.注册…