Linux 信号

news2025/7/27 17:45:12

概念:信号不是信号量,信号量是进程间的一种通信方式,信号是系统中的软件中断,指一种事件通知机制,通知进程发生了某个事件,打断当前的操作,去处理这个事件。

种类:一共有62种信号,可以用kill -l 选项进行查看

 1-31是非可靠非实时信号。

34-64是可靠实时信号。

产生:

        硬件产生:ctrl + c   ctrl + z   ctrl + \

        ctrl + c 其实发送的就是2号信号 SIGINT 2号中断信号

        ctrl + z 其实发送的是19号信号SIGSTOP19号停止信号

        ctrl + \ 其实发送的是3号信号SIGQUIT3号退出信号

举个例子:

#include<stdio.h>
#include<string.h>
int main()
{
  char * ptr = NULL;
  strcpy(ptr,"hello");
  return 0;
}

NULL是一个不可读不可写的地址我们向其中写入数据肯定是会发生错误的,程序也会奔溃的那咋样操作系统通知和处理这个事件呢?就是信号!

 这里就是告诉你内存访问错误,发生了段错误。

软件产生:

                kill 杀死进程的原理就是给进程发送了一个15号信号SIGTERM终止信号

                

 

                 kill

                函数原型 int kill(pid_t pid, int sig);

                pid_t pid 要发送信号的进程,int sig 要发送几号信号

#include<stdio.h>
#include<unistd.h>
#include<signal.h>
int main()
{
  kill(getpid(),2);
  while(1)
  {
    printf("_________________\n");
    sleep(2);
  }
  return 0;
}

 程序一运行就直接退出了,因为刚刚跑起来就调用kill这个函数了,我们可以借用gdb看看最后的函数调用栈来看看。

 栈顶退出函数就是造成程序异常的函数,我main函数要等到return 了之后才算函数调用结束还没等到你return kill已经发送了一个二号信号来将你终止你此时只能就退出了。

                raise

                函数原型:int raise(int sig)

                给进程自身发送一个信号。

#include<stdio.h>
#include<unistd.h>
#include<signal.h>
int main()
{
  //kill(getpid(),2);
  raise(SIGINT);
  while(1)
  {
    printf("_________________\n");
    sleep(2);
  }
  return 0;
}

这个SIGINT其实也是一个宏

 

也中断了。gdb

                abort

                函数原型:void abort(void);

                给进程发送一个SIGABRT信号,异常信号。

#include<stdio.h>
#include<unistd.h>
#include<signal.h>
#include<stdlib.h>
int main()
{
  //kill(getpid(),2);
  //raise(SIGINT);
  abort();
  while(1)
  {
    printf("_________________\n");
    sleep(2);
  }
  return 0;
}

                 alarm

                函数原型: unsigned int alarm(unsigned int seconds);

                定义一个计时器,在seconds秒之后给进程发送一个SIGALRM信号

#include<stdio.h>
#include<unistd.h>
#include<signal.h>
#include<stdlib.h>
int main()
{
  //kill(getpid(),2);
  //raise(SIGINT);
  //abort();
  alarm(3);
  while(1)
  {
    printf("_________________\n");
    sleep(1);
  }
  return 0;
}

                 sigqueue

                函数原型:int sigqueue(pid_t pid, int sig, const union sigval value);

                通知pid进程发生了啥事情并且传递一个数据

#include<stdio.h>
#include<unistd.h>
#include<signal.h>
#include<stdlib.h>
int main()
{
  //kill(getpid(),2);
  //raise(SIGINT);
  //abort();
  //alarm(3);
  union sigval val;
  val.sival_int = 10;
  sigqueue(getpid(),SIGINT,val);
  while(1)
  {
    printf("_________________\n");
    sleep(1);
  }
  return 0;
}

 注册:

        让进程知道自己收到了信号

        在进程pcb中有一个pending位图,未决信号集合,也就是当前收到的但没有处理的信号集合。信号的注册也就是在这个位图中标出对应位置为1,并且在pcb中有一个sigqueue链表,在这个链表中添加对应的信号节点。

 可靠信号的注册:不管当前是否有相同信号已经注册,位图置1,添加一个节点。

非可靠信号的注册:如果相同信号没有杯注册那就注册一下,否则直接返回。

非可靠这种方式就有可能存在事件丢失这种情况。

注销:在处理之前将信号的存在痕迹抹除。

                将sigqueue链表中的对应信号节点删掉,修改位图

                可靠信号:删除一个节点当没有相同信号节点时再去修改位图

                非可靠信号:删除信号的节点,直接位图置0

处理:处理一个信号就是调用信号的处理函数。

                

处理方式:

        默认处理方式:系统中定义好的处理方式。

        忽略处理方式:信号的处理方式就是不作为。

        自定义处理方式:用户自定义一个处理函数,然后替换掉内核中默认的处理方式。 

                signal

                函数原型: typedef void (*sighandler_t)(int);

                                   sighandler_t signal(int signum, sighandler_t handler);

                                   signum:指定的信号值;

                                    handler:新的处理方式  SIG_DFL 默认处理方式  SIG_IGN忽略处理方式

                                    返回值是原先函数的处理方式。

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

int main()
{
  signal(2,SIG_IGN);
  while(1)
  {
    printf("哎哟,你干嘛~~~~~\n");
    sleep(2);
  }
  return 0;

}

这下我ctrl + c就停止不了程序了

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

void sigcb(int signo)
{
  printf("收到%d号信号\n",signo);
  alarm(3);
}
int main()
{
  //signal(2,SIG_IGN);
  signal(SIGALRM,sigcb);
  alarm(3);
  while(1)
  {
    printf("哎哟,你干嘛~~~~~\n");
    sleep(2);
  }
  return 0;

}

 就给了一个自定义的处理方式。

测试一下返回值,我们知道signal处理完事之后返回的值是之前的处理方式。

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

__sighandler_t func;
void sigcb(int signo)
{
  printf("收到%d号信号\n",signo);
  //alarm(3);
  signal(signo,func);
}
int main()
{
  func =  signal(2,sigcb);
  //signal(SIGALRM,sigcb);
  //alarm(3);
  while(1)
  {
    printf("哎哟,你干嘛~~~~~\n");
    sleep(1);
  }
  return 0;

}

 第二次ctrl + c已经退出来了说明函数返回值的作用重新返回2号信号的处理方式!

自定义处理方式的信号捕捉流程:

        

 1.信号的处理是程序从内核态切换到用户态之前就已经处理完成的,

 2.信号的处理是一次性把所有信号处理完毕后才返回主控流程的。

阻塞:阻塞一个信号就是暂时先不处理这个信号。

        在pcb中有一个信号集合block信号阻塞集合,在集合中标记那个信号就表示要阻塞哪个信号,意味着收到这个信号暂时先不处理。

                 sigpromask

                 函数原型:int sigpromask(int how, sigset_t *set , sigset_t *old);

                how:要对阻塞集合进行的操作

                SIG_BLOCK:将set集合中的信号添加到block信号中去

                SIG_UNBLICK:将set集合中的信号从block中移除

                SIG_SETMASK:将set集合中的信号设置为block集合中的信号

                set:要操作的信号集合

                old:用于保存修改前block信号集合中的数据

                int sigemptyset(sigset_t *set);清空set集合

                int sigfillset(sigset t *set):填充所有信号到集合中

                int sigaddset(sigset t *set, int signum); 添加指定信号到集合中

                int sigdelset(sigset t *set, int signum):从指定集合中移除指定信号

                int sigismember(const sigset t *set, int signum);判断指定信号是否在集合中

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


void sigcb(int no)
{
  printf("收到了%d号信号\n",no);
}
int main()
{
  signal(2,sigcb);
  signal(40,sigcb);
  sigset_t set;
  sigset_t old;
  __sigemptyset(&set);
  sigaddset(&set,2);
  sigaddset(&set,40);
  sigprocmask(SIG_BLOCK,&set,&old);
  printf("enter continue!!\n");
  getchar();
  printf("continue!!\n");
  sigprocmask(SIG_SETMASK,&old,NULL);

  while(1)
  {
    sleep(1);
  }
  return 0;
}

 

 在信号中有两个信号比较特殊:SIGKILL -9 和 SIGSTOP-19,这两个信号不会被阻塞不会被忽略不会被自定义,也就是这两个信号的处理方式是无法被修改的

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

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

相关文章

Linux之用户管理、权限管理、程序安装卸载

一. 用户管理 1. 查看账户 (1). 查看当前账号&#xff1a;whoami ​​(2). 查看系统当前登录的账号&#xff1a;who ​​补充常用选项&#xff1a; ​​(3). 查看系统所有的账号&#xff1a; cat /etc/passwd ​​2. exit&#xff1a;退出登录账户 如果是图形界面&#xff0c…

curl命令的常用操作

curl是非常实用的命令行工具&#xff0c;用来与服务器之间传输数据。它的命令行参数多达几十种。 在Linux环境中使用curl命令可以进行接口测试。利用curl对http协议发送Get/Post/Delete/Put请求,同时还可以携带header来满足接口的特定需求。 curl命令的语法 curl[options] [U…

Linux03-网络设置

一、说明 在上一节&#xff0c;咱使用VMware安装了虚拟机&#xff0c;网络设置选择了 “桥接模式” &#xff0c;本节咱们来具体讨论一下网络连接方式和网络设置。 实验环境&#xff1a;CentOS7 VMware 二、桥接模式 当我们设置桥接模式时&#xff0c;虚拟机是直接使用物理…

eNSP出现错误,错误代码40暴力解决方案

如果你和我一样&#xff0c;在eNSP中启动一个设备时发生了错误&#xff0c;错误代码为40&#xff0c;那么这篇文件可能会帮助你。 首先你可以仔细地按照这篇说明中的做法进行操作&#xff0c;如果你电脑也是win10&#xff0c;并且之前没有安装过wireshark&#xff0c;virtualb…

后端总说他啥也没动,我从线上调了一下测试接口,你再说一句动没动

◇ 不知道广大前端同学有没有过这样的经历&#xff0c;在做新需求联调的时候&#xff0c;原本上一个版本已经做的好好的功能&#xff0c;前后端已经联调好的。这次做需求的时候&#xff0c;测试发现好多地方都不对了。 ◇ 开发人员经常说的一句话就是&#xff1a;我啥也没动啊…

Java -- 每日一问:你了解Java应用开发中的注入攻击吗?

典型回答 注入式&#xff08;Inject&#xff09;攻击是一类非常常见的攻击方式&#xff0c;其基本特征是程序允许攻击者将不可信的动态内容注入到程序中&#xff0c;并将其执行&#xff0c;这就可能完全改变最初预计的执行过程&#xff0c;产生恶意效果。 下面是几种主要的注…

Web前端:2022年Web开发者的五大CSS工具

据相关数据统计&#xff0c;2018年至2028年&#xff0c;网络开发人员的就业预计将增长13%&#xff0c;这意味着网站开发者的需求量很大&#xff0c;而企业需要专业人员来构建网站&#xff0c;而高效制作优秀网站的最佳方法是拥有最好的web开发工具。 对优秀web开发工具的需求使…

设信号x(t)=cos(2π×50t)+2×cos(2π×400t),试将它的两个频率分量分离,并绘制它们的时域波形及频谱图

以下程序无需赋值&#xff0c;直接运行即可&#xff1a; &#xff08;已验证可以运行&#xff09; function [yl,yh]shiyan49 fs1600; %采样频率 Tt0.02; %信号周期 T04*Tt; %记录长度 [xn,wk,N]shiyan40(fs,T0); Mlength(wk); if M2 rp1;rs80; f1wk(1)*fs/N; f2wk(2…

升余弦滤波器的FPGA实现

目录 升余弦滤波器与无码间串扰&#xff08;一&#xff09; 升余弦滤波器与无码间串扰&#xff08;二&#xff09; 升余弦滤波器的FPGA实现 一、FIR ip核 成形滤波器采用vivado中的FIR ip核实现。滤波器的系数用matlab产生并转成coe文件。 wire m_axis_data_tvalid; rcos_f…

官网下载JAVA的JDK11版本(下载、安装、配置环境变量)

目录 前言&#xff1a; 一、下载JDK11 1.先去浏览器搜索 2.选择Products 3.Java ​编辑 4.往下滑找到Oracle JDK,然后点击 5.往下滑找到Java11&#xff0c;再选择自己的操作系统 6.选择exe这个直接安装的后缀 7.跳出来的弹窗勾选&#xff0c;并点击 8.弹出来一个甲骨文…

树莓派4b linux内核调试(jtag、kgdb)

1、-O0编译树莓派4b linux内核(linux-5.10.95) 内核源码分支: https://github.com/raspberrypi/linux/tree/rpi-5.10.y 参考其他网站&#xff0c;注释掉arch/arm64/include/asm/jump_label.h文件里面的内联汇编分支&#xff1b; 修改arch/arm64/include/asm/memory.h里面的MIN…

大一学生Web课程设计 美食主题网页制作(HTML+CSS+JavaScript)

&#x1f380; 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业…

传奇开服教程——传奇微端架设教程-GEE引擎

登录器和网站配置好后&#xff0c;我们进入游戏后会发现是黑屏的&#xff0c;更新不了地图和NPC这些&#xff0c;因为还没有做微端&#xff0c;会黑屏也是正常的。 有些GEE版本有配套的微端程序&#xff0c;有些版本没有&#xff0c;需要自己去gee官网下载更新时间比引擎还靠后…

多层固定分组计算

【问题】 I want to build opening closing report – ITEM_TRANSACTION TABLE- Consider below data– 1.When item is prepared entry is made into table wih NULL indicator.. 2. When Item is issued/sold entry is made into table with ISSUE indicator Date Iname I…

[附源码]SSM计算机毕业设计中学学生学籍管理JAVA

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

笔试强训第29天

单选 A选项&#xff1a;当内存访问越界的时候&#xff0c;线程会收到信号&#xff0c;进而进行信号处理。调用信号处理函数。 B选项&#xff1a;ACM时间。 A&#xff1a;最后访问时间&#xff0c;文件被读取而更新的时间 C&#xff1a;状态修改时间&#xff0c;文件的属性或者权…

遨博机械臂URDF功能包ROS仿真

文章目录知识目标1.机械臂URDF功能包文件结构2.URDF模型中的元素和参数3.任务描述3.1 修订launch启动文件参数3.2 URDF功能包编译知识目标 学习机械臂URDF功能包文件结构 掌握机械臂URDF模型文件各元素和参数概念 1.机械臂URDF功能包文件结构 2.URDF模型中的元素和参数 机械…

【mmDetection框架解读】入门篇三、VOC数据集转COCO数据集,在MMDetection中成功运行

目录一、voc数据集转coco数据集二、修改配置二、验证数据集是否转换成功相关源码文件一、voc数据集转coco数据集 执行命令&#xff1a; python tools/dataset_converters/pascal_voc.py ../../../data/devkit ../../../data/coco通过VOC的xml文件生成COCO的json标签文件。 再…

Linux下延时一定时间,又不挂起程序。

0.前言 这是一个解决篇Linux C语言怎么实现通过socket发送一秒数据&#xff0c;上次想要实现做一秒的事情没有写出来&#xff0c;这次解决掉。先推荐一部电影《猩球崛起》三部曲。 1.Linux下的sleep函数。 这个函数的确可以起到延时的作用&#xff0c;但是他会把整个进程挂起&a…

CentOS8替代盘点

常用的替代选项 国外替代 Rocky Linux AlmaLinux Oracle Linux UEK内核 CentOS Stream 国产替代 Anolis OS TencentOS Server(桌面版无法迁移) openEuler OpenCloudOS 宝塔面板 推荐 Rocky/Alma/Oracle linux 8 持保守态度 Centos 8 Stream Anolis&#xff08;Centos 8 Stream…