Linux信号一门搞定

news2024/11/13 18:57:38

1.信号是什么?

信号其实就是一个软件中断。

例:

  1. 输入命令,在Shell下启动一个前台进程。
  2. 用户按下Ctrl-C,键盘输入产生一个硬件中断。
  3. 如果CPU当前正在执行这个进程的代码,则该进程的用户空间代码暂停执行, CPU从用户态切换到内核态处理硬件中断。
  4. 终端驱动程序将Ctrl-C解释成一个SIGINT信号,记在该进程的PCB中(也可以说发送了一个SIGINT信号给该进程)。
  5. 当某个时刻要从内核返回到该进程的用户空间代码继续执行之前,首先处理PCB中记录的信号,发现有一个SIGINT信号待处理,而这个信号的默认处理动作是终止进程,所以直接终止进程而不再返回它的用户空间代码执行。

在这个例子中,由ctrl+c产生的硬件中断就是一个信号。Ctrl+C产生的信号只能发送给前台进程,命令后加&就可放到后台运行。
Shell可同时运行一个前台进程和任意多个后台进程,只有前台进程才能接受到像CTRL+C这种控制键产生的信号。

2.信号的种类

使用命令查看:

kill -l

非可靠信号:1~31号信号,信号可能会丢失
可靠信号:34~64号信号,信号不可能丢失

SIGHUP:1号信号,Hangup detected on controlling terminal or death of controlling process(在控制终端上挂起信号,或让进程结束),ation:term

SIGINT:2号信号,Interrupt from keyboard(键盘输入中断,ctrl + c ),action:term

SIGQUIT:3号信号,Quit from keyboard(键盘输入退出**,ctrl+ |** ),action:core,产生core dump文件

SIGABRT:6号信号,Abort signal from abort(3)(非正常终止,double free),action:core

SIGKILL:9号信号,Kill signal(杀死进程信号),action:term,该信号不能被阻塞、忽略、自定义处理

SIGSEGV:11号信号,Invalid memory reference(无效的内存引用,解引用空指针、内存越界访问),action:core

SIGPIPE:13号信号,Broken pipe: write to pipe with no readers(管道中止: 写入无人读取的管道,会导致管道破裂),action:term

SIGCHLD:17号信号,Child stopped or terminated(子进程发送给父进程的信号,但该信号为忽略处理的)

SIGSTOP:19号信号,Stop process(停止进程),action:stop

SIGTSTP:20号信号,Stop typed at terminal(终端上发出的停止信号,ctrl + z),action:stop

具体的信号采取的动作和详细信息可查看:man 7 signal

3.信号的产生

3.1硬件产生

硬件产生即通过终端按键产生的信号:

  1. ctrl + c:SIGINT(2),发送给前台进程,& 进程放到后台运行,fg 把刚刚放到后台的进程,再放到前台来运行
  2. ctrl + z:SIGTSTP(20),一般不用,除非有特定场景
  3. ctrl + | :SIGQUIT(3),产生core dump文件

产生core dump文件的条件:

当前OS一定不要限制core dump文件的大小,ulimit -a
磁盘空间要足够
如何产生:
3.1 解引用空指针,收到11号信号,产生core dump文件
3.2 内存访问越界,程序一旦崩溃,就会收到11号信号,也就会产生core dump文件
3.3 double free,收到6号信号,并产生core dump。
3.4 free(NULL),不会崩溃

3.2软件产生

软件产生即调用系统函数向进程发信号

  1. kill函数
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
参数解释:
pid:进程号
sig:要发送的信号值
返回值:成功返回0,失败返回-1,并设置错误
  1. kill命令:kill -[信号] pid,
  2. abort:void abort(void);,收到6号信号,谁调用该函数,谁就收到信号
  3. alarm:unsigned int alarm(unsigned int seconds);,收到14号信号,告诉内核在seconds秒后给进程发送SIGALRM信号,该信号默认处理动作为终止当前进程。

4.信号的注册

信号注册又分为可靠信号的注册和非可靠信号的注册。
信号注册实际上是一个位图和一个sigqueue队列。

4.1非可靠信号的注册

当进程收到非可靠信号时:

  1. 将非可靠信号对应的比特位置为1
  2. 添加sigqueue节点到sigqueue队列当中,但是,在添加sigqueue节点的时候,队列当中已然有了该信号的sigqueue节点,则不添加

4.2可靠信号的注册

当进程所受到可靠信号时:

在sig位图中更改信号对应的比特位为1
不论之前sigqueue队列中是否存在该信号的sigqueue节点,都再次添加sigqueue节点到sigqueue队列当中去

5.信号的注销

5.1非可靠信号的注销

信号对应的比特位从1置为0
将该信号的sigqueue节点从sigqueue队列当中进行出队操作

5.2可靠信号的注销

将该信号的sigqueue节点从sigqueue队列当中进行出队操作
需要判断sigqueue队列当中是否还有相同的sigqueue节点:
①没有了:信号比特位从1置为0
②还有:不会更改sig位图中的比特位

6.信号阻塞

6.1信号是怎样阻塞的?

信号的阻塞,并不会干扰信号的注册。信号能注册,但不能被立即处理,
将block位图中对应的信号比特位置为1,表示阻塞该信号
进程收到该信号,还是一如既往的注册
当进程进入到内核空间,准备返回用户空间的时候,调用do_signal函数,就不会立即去处理该信号了
当该信号不被阻塞后,就可以进行处理了

6.2sigprocmask

函数原型:int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
参数解释:

how,该做什么样的操作
SIG_BLOCK:设置信号为阻塞
SIG_UNBLOCK:解除信号阻塞
SIG_SETMASK:替换阻塞位图
set:用来设置阻塞位图
SIG_BLOCK:设置某个信号为阻塞,block(new) = block(old) | set
SIG_UNBLOCK:解除某个信号阻塞,block(new)= block(old) &~set)
SIG_SETMASK:替换阻塞位图,block(new)= set
oldset:原来的阻塞位图

例:下述例子,信号全部被阻塞,采用kill -9,将该进程结束掉

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


void signcallback(int signumber)
{
  printf("change the signal %d\n",signumber);
}

int main()
{
  sigset_t set;
  sigset_t oldset;
  sigfillset(&set);//所有比特位全置为1,则信号全部会被阻塞
  sigprocmask(SIG_BLOCK,&set,&oldset);
  while(1)
  {
    sleep(1);
  }

  return 0;
}

结果: 此时发送信号是不会有作用的,采用kill -9强杀掉

7.信号未决

7.1 未决概念

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

7.2 sigpending

函数原型:int sigpending(sigset_t *set);
读取当前进程的未决信号集,通过set参数传出。调用成功返回0,出错返回-1.

例:

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

void signalcallback(int signumber)
{
  printf("chang signumber %d\n",signumber);
}
void printsigset(sigset_t *set)
{
  int i = 0;
  for(;i < 32;i++)
  {
    if(sigismember(set,i))
    {
      putchar('1');
    }
    else{
      putchar('0');
    }
  }
}

int main()
{
  signal(2,signalcallback);
  signal(10,signalcallback);
  sigset_t set;
  sigset_t oldset;
  sigset_t pending;
  sigfillset(&set);//所有比特位全部置为1,则信号会全部被阻塞
  sigprocmask(SIG_BLOCK,&set,&oldset);
  while(1)
  {
    sigpending(&pending);
    printsigset(&pending);
    sleep(1);
  }

  return 0;
}

结果:

8.信号的处理方式

每个信号都有两个标志位分别表示阻塞和未决,还有一个函数指针表示处理动作。

在上述例子中:

  1. SIGHUP信号未阻塞也未产生过,当它递达时执行默认处理动作。
  2. SIGINT信号产生过,但正在被阻塞,所以暂时不能递达。虽然它的处理动作是忽略,但在没有解除阻塞之前不能忽略这个信号,因为进程仍有机会改变处理动作之后再解除阻塞。
  3. SIGQUIT信号未产生过,一旦产生SIGQUIT信号将被阻塞,它的处理动作是用户自定义函数sighandler。

8.1signal函数

该函数可以更改信号的处理动作。

typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
参数解释:

signum:更改的信号值
handler:函数指针,要更改的动作是什么

实际上,该函数内部也调用了sigaction函数。

8.2sigaction函数

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

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

参数解释:

signum:待更改的信号值

struct sigaction结构体:

void     (*sa_handler)(int);//函数指针,保存了内核对信号的处理方式
void     (*sa_sigaction)(int, siginfo_t *, void *);//
sigset_t   sa_mask;//保存的是当进程在处理信号的时候,收到的信号
int        sa_flags;//SA_SIGINFO,OS在处理信号的时候,调用的就是sa_sigaction函数指针当中
//保存的值0,在处理信号的时候,调用sa_handler保存的函数
void     (*sa_restorer)(void);

例:

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

void signcallback(int signumber)
{
  printf("change signumber %d\n",signumber);
}


int main()
{
  struct sigaction act;//act为入参
  sigemptyset(&act.sa_mask);
  act.sa_flags = 0;
  act.sa_handler = signcallback;

  struct sigaction oldact;//oldact为出参
  sigaction(3,&act,&oldact);
  while(1)
  {
    sleep(1);
  }
  return 0;
}

结果:

8.3 自定义信号处理的流程

  1. task_struct结构体中有一个struct sighand_struct结构体。
  2. struct sighand_struct结构体有一个**struct k_sigaction action[_NSIG]**结构体数组。
  3. 该数组中,其中的**_sighandler_t sa_handler**保存的是信号的处理方式,通过改变其指向,可以实现我们对自定义信号的处理。

9.信号的捕捉

9.1信号捕捉的条件

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

9.2信号捕捉流程

内核态返回用户态会调用do_signal函数,两种情况:

  1. 无信号:sys_return函数,返回用户态

  2. 有信号:先处理信号,信号返回,再调用do_signal函数
    例:

  3. 程序注册了SIGQUIT信号的处理函数sighandler。

  4. 当前正在执行main函数,这时发生中断或异常切换到内核态。

  5. 在中断处理完毕后要返回用户态的main函数之前检查到有信号SIGQUIT递达。

  6. 内核决定返回用户态后不是恢复main函数的上下文继续执行,而是执行sighandler函数, sighandler和main函数使用不同的堆栈空间,它们之间不存在调用和被调用的关系,是两个独立的控制流程。

  7. sighandler函数返回后自动执行特殊的系统调用sigreturn再次进入内核态。

  8. 如果没有新的信号要递达,这次再返回用户态就是恢复main函数的上下文继续执行了。

10.常用信号集操作函数

int sigemptyset(sigset_t *set);//将比特位图全置为0

int sigfillset(sigset_t *set);//将比特位图全置为1

int sigaddset(sigset_t *set, int signum);//将该set位图,多少号信号置为1

int sigdelset(sigset_t *set, int signum);//将该set位图,多少号信号置为0

int sigismember(const sigset_t *set, int signum);//信号signum是否是set位图中的信号

11.SIGCHLD信号

该信号是子进程在结束是发送给父进程的信号,但是该信号的处理方式是默认处理的。
父进程对子进程发送过来的SIGCHLD信号进行了忽略处理,就会导致子进程成为僵尸进程。

可以自定义该信号的处理方式:

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <sys/wait.h>
#include <stdlib.h>

void signcallback(int signumber)
{
  printf("change signal %d\n",signumber);
  wait(NULL);
}

int main()
{
  signal(17,signcallback);
  pid_t pid = fork();
  if(pid < 0)
  {
    perror("fork");
    return -1;
  }
  else if(pid == 0)
  {
    printf("I am child\n");
    sleep(1);
    exit(12);
  }
  else{
    while(1)
    {
      sleep(1);
    }
  }
  return 0;
}


指令查看后台:ps aux | grep ./fork

原文地址:
https://blog.csdn.net/w903414/article/details/109802539?utm_source=app&app_version=4.18.0&utm_source=app

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

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

相关文章

Linux | Liunx安装Tomcat(Ubuntu版)

目录 一、下载并上传Tomcat压缩包到Ubuntu 1.1 下载并解压 1.2 执行 startup.sh 文件 二、验证Tomcat启动是否成功 2.1 查看启动日志 2.2 查看启动进程 三、Windows访问 Tomcat 服务 四、停止 Tomcat 服务 Tomcat是一款Web服务器&#xff0c;开发Web项目基本上都会用到…

应用篇|如何精准搜索一个答题考试小程序

应用篇|如何精准搜索一个答题考试小程序在线考试是一种非常节约成本的考试方式&#xff0c;考生通过微信扫码即可参加培训考试&#xff0c;不受时间、空间的限制&#xff0c;近几年越来越受企事业单位的青睐。比如有以下场景&#xff1a;为落实反电信网络诈骗普法宣传教育工作&…

【云原生-Docker】docker镜像制作、上传、dockerfile命令解析

场景 在实际业务场景中&#xff0c;需要制作多个不同版本进行镜像使用&#xff0c;如maven版本、JDK、openJDK不同使用等&#xff0c;所以需要做多个针对不同版本做不同的镜像。这里记录一下之前devops用的openJDK版本、某些部门需要用orcle JDK、特此需要做不同的镜像&#x…

C#学习笔记--泛型函数的==和Equals(看完你一定能学到!)

前言 工作的同事发现了这个问题&#xff0c;觉得实际游戏开发中会有这样的问题&#xff0c;所以在此记录 准备 开一个Unity项目&#xff0c;新建一个Test.cs脚本&#xff0c;并且生成一个Cube&#xff0c;直接把Test.cs挂在Cube上写一个Nulltest.cs脚本 using System.Colle…

【C++从入门到放弃】初识C++(基础知识入门详解)

&#x1f9d1;‍&#x1f4bb;作者&#xff1a; 情话0.0 &#x1f4dd;专栏&#xff1a;《C从入门到放弃》 &#x1f466;个人简介&#xff1a;一名双非编程菜鸟&#xff0c;在这里分享自己的编程学习笔记&#xff0c;欢迎大家的指正与点赞&#xff0c;谢谢&#xff01; C基础…

CentOS搭建博客typecho

Ubuntu搭建博客typecho_Dyansts的博客-CSDN博客 见过这样的文章展示页面吗&#xff1f; 详细视频安装教程&#xff1a; 9分钟快速搭建typecho博客&#xff0c;让你不再烦恼_哔哩哔哩_bilibili 现在就把他搭建出来 展示页面&#xff1a;Hello World 其他的插件&#xff1a;…

真的要用SaaS类产品做企业的移动办公平台吗?

面对越来越多的企业信息移动化解决方案&#xff0c;作为CIO该如何选择移动平台呢&#xff1f;先看看最常见的SaaS产品的情况。真的要用SaaS类产品做企业的移动门户吗&#xff1f;当前&#xff0c;热门SaaS类产品主要是企业微信和钉钉&#xff0c;适合小微初创企业。企业在不同成…

Chem. Sci.|AlphaFold加速了人工智能药物的发现:一种新型CDK20小分子抑制剂

​ 题目&#xff1a;AlphaFold accelerates artificial intelligence powered drug discovery: efficient discovery of a novel CDK20 small molecule inhibitor 文献来源&#xff1a;Chem. Sci., 2023, 14, 1443 代码&#xff1a;是一个很大的流程 https://www.pandaomics…

CountDownLatch的定义、使用 、原理

一、定义 CountDownLatch的作用很简单&#xff0c;就是一个或者一组线程在开始执行操作之前&#xff0c;必须要等到其他线程执行完才可以。我们举一个例子来说明&#xff0c;在考试的时候&#xff0c;老师必须要等到所有人交了试卷才可以走。此时老师就相当于等待线程&#xff…

《Terraform 101 从入门到实践》 Terraform在公有云Azure上的应用

《Terraform 101 从入门到实践》这本小册在南瓜慢说官方网站和GitHub两个地方同步更新&#xff0c;书中的示例代码也是放在GitHub上&#xff0c;方便大家参考查看。 简介 Azure是微软的公有云&#xff0c;它提供了一些免费的资源&#xff0c;具体可以查看&#xff1a; https:/…

从零开始学数据分析之数据分析概述

当今世界对信息技术的依赖程度在不断加深&#xff0c;每天都会有大量的数据产生&#xff0c;我们经常会感到数据越来越多&#xff0c;但是要从中发现有价值的信息却越来越难。 这里所说的信息&#xff0c;可以理解为对数据集处理之后的结果&#xff0c;是从数据集中提炼出的可…

11- 聚类算法 (KMeans/DBSCAN/agg) (机器学习)

聚类算法 聚类算法和降维算法那都属于无监督算法。KMeans 是以一个值为中心, 然后所有其他点到该点距离最小值的累积和。 kmeans KMeans(n_clusters3) # n_clusters 分类数量 kmeans.fit(data.iloc[:,1:]) # 无监督&#xff0c;只需要给数据X就可以 DBSCAN 算法是…

GAMES101作业7及课程总结(重点实现多线程加速,微表面模型材质)

目录闲言碎语最终全部效果展示&#xff08;均为10241024512ssp&#xff09;课程总结与理解&#xff08;Path Tracing&#xff09;框架梳理任务一&#xff1a;迁移相关代码任务二&#xff1a;实现path tracing任务三&#xff1a;多线程加速&#xff08;包括其他加速的小trick&am…

Cocos Creator 3.x开发《切水果3D》

今天跟大家分享一个Cocos Creator 3D切水果的实战案例&#xff0c;帮助大家掌握Cocos Creator开发3D微信抖音小游戏&#xff0c;开发工具我们采用的是Cocos Creator 3.6。先上一波游戏操作效果图&#xff0c;接下来通过本文来讲解这个游戏的一些核心的技术点。 对啦&#xff0…

使用sqlmap + burpsuite sql工具注入拿flag

使用sqlmap burpsuite sql工具注入拿flag 记录一下自己重新开始学习web安全之路③。 目标网站&#xff1a;http://mashang.eicp.vip:1651/7WOY59OBj74nTwKzs3aftsh1MDELK2cG/ 首先判断网站是否存在SQL注入漏洞 1.找交互点 发现只有url这一个交互点&#xff0c;搜索框和登录…

Springboot扫描注解类

Springboot扫描注解类的入口在AbstractApplicationContext的refresh中&#xff0c;对启动步骤不太了解的&#xff0c;可参考https://blog.csdn.net/leadseczgw01/article/details/128930925BeanDefinitionRegistryPostProcessor接口有多个实现类&#xff0c;扫描Controller、Se…

【VictoriaMetrics】VictoriaMetrics单机版批量和单条数据写入(Prometheus格式)

VictoriaMetrics单机版支持以Prometheus格式的数据写入,写入支持单条数据写入以及多条数据写入,下面操作演示下如何使用 1、首先需要启动VictoriaMetrics单机版服务 2、使用postman插入单机版VictoriaMetrics,以当前时间插入数据 地址为 http://victoriaMetricsIP:8428/api…

ISYSTEM调试实践10-实时数据采集工具daqIDEA

本文介绍一种实时数据采集的工具daqIDEA&#xff0c;该软件整合在了winIDEA内&#xff0c;可以直接通过winIDEA启动。 daqIDEA类似于jlink的jscop&#xff0c;stlink也有类似功能。原理就是利用仿真探头&#xff0c;将程序运行的变量实时采集出来&#xff0c;并通过曲线的方式显…

记录次数 | V1.1.0版本变动说明

版本内容1、新增词条数据报告---统计累计次数、最早时间、最晚时间等等 2、词条加入内容文本审核功能---创建/修改词条先经过微信文本安全接口审查&#xff0c;审查通过的才能分享公开数据&#xff0c;否则只能自己可见 3、新增分享版本思考这个小程序是有自然流量的&#xff0…

跨域小样本系列4:finetune方法解决CDFSL

来源&#xff1a;投稿 作者&#xff1a;橡皮 编辑&#xff1a;学姐 带你学习跨域小样本系列1-简介篇 跨域小样本系列2-常用数据集与任务设定详解 跨域小样本系列3&#xff1a;元学习方法解决CDFSL以及两篇SOTA论文讲解 跨域小样本系列4&#xff1a;finetune方法解决CDFSL以及…