热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

linux信号---信号的产生

linux信号---信号的产生信号的本质操作系统给进程发送信号,本质上是给进程的PCB中写入数据,修改相应的PCB字段,进程在合适的时间去处理所接受的信号。我们先从熟悉的场景说起:

linux信号---信号的产生

吐舌头信号的本质

   操作系统给进程发送信号,本质上是给进程的PCB中写入数据,修改相应的PCB字段,进程在合适的时间去处理所接受的信号。我们先从熟悉的场景说起:

1)用户输入一个命令,在shell下启动一个前台进程。

2)用户按下Ctrl-C,通过键盘输入产生了一个硬件中断。

3)如果CPU当前正在运行此进程的代码,则该进程的用户空间代码暂停执行,CPU从用户态切换到内核态处理中断。

4)终端驱动程序将Ctrl-C解释为一个SIGINT信号,记在该进程的PCB中。

5)当某个时刻从内核返回该进程的用户空间代码继续执行之前,首先处理PCB中记录的信号。SIGINT信号默认处理动作为终止信号,所以直接终止进程而不再返回到它的用户空间代码。

※❤ Ctrl+C所产生的信号只能发送给前台进程,如果想让其在后台运行,需要在命令后面加上&。这样shell不必等待进程结束就可以接受新的命令,启动新的进程。

Shell可以同时运行一个前台进程和任意多个后台进程,只有前台进程才能接受到诸如Ctrl-C这样的信号,前台进程在运行过程中用户随时按下Ctrl-C而产生一个信号,也就是说该进程的用户空间代码执行到任何地方都可能受到SIGINT信号而被终止,因此信号相对于进程的控制流来说是异步的。

吐舌头普通信号与实时信号

我们使用kill -l命令可以查看系统定义的信号列表,每个编号都有一个宏与之对应,可以在/usr/include/asm/signal.h中查看,下图中1-31为普通信号,34-64为实时信号。



实时信号: 编号为34-64,它们与常规信号有很大的不同,因为它们必须排序以便发送多个信号能被接收到。但是同种信号的常规信号并不排序,尽管Linux内核并使用实时信号,它还是通过几个特定的系统调用完全实现了POSIX标准。

linux中采用位图存储

产生信号的条件:

1)用户在终端按下某些键时,终端驱动会发送信号给前台进程,例如Ctrl-C产生的SIGINT信号、Ctrl-\产生的SIGQUIT信号、Ctrl-Z产生的SIGTSTP信号。

2)硬件异常产生的信号,这些条件由硬件检测并通知内核,然后内核向当前进程发送适当的信号。比如当前进程访问了非法内存地址,MMU(内存管理单元)会产生异常,内核将这个异常解释为SIGSEGV信号发送给进程。

3)一个进程调用kill(2)函数可以发送信号给另一个进程,可以调用kill(1)命令发送信号给某个进程,kill(1)命令也是调用kill(2)函数实现的。如果不明确指定信号,则发送SIGTERM信号,该信号的默认处理动作是终止进程,当内核检测到软件条件发生时可以通过信号通知进程。

信号处理方式:

进程一般通过3种方式处理信号

1,忽略此信号

2,执行该信号的默认处理动作

3,提供一个信号处理函数,要求内核在处理该信号时切换到用户态执行这个处理函数,这种方式称为捕捉一个信号,这个函数称为信号捕捉函数


typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
参数signum:信号的编号。

参数handler:是一个函数指针,sighandler_t类型的函数指针,表示接受此信号要执行的函数的地址。也可以是  SIG_ING 代表忽略SIGINT信号 SIG_DFL代表执行系统默认操作

返回值:若成功则为指向前次信号处理程序的指针,若出错则为SIG_ERR。

 ----->对2号信号进行捕捉:Ctrl-C产生的SIGINT信号
  1 #include
  2 #include
  3 void myhandle()
  4 {
  5 printf("hello word !\n");
  6 }
  7 
  8 int main(){
  9  signal(2,myhandle);
 10  while(1);
 11  return 0;
 12 }                                                                                                                                                      
          
此时Ctrl-C是不能终止程序的,无奈的我们只能用9号信号来杀死该进程。

接下来我们对此程序进行改进:

  1 #include
  2 #include
  3 #include                                                                                                                                     
  4 typedef void (*sighandler_t) (int);
  5 sighandler_t _myhandle=NULL;
  6 void myhandle()
  7 {
  8 printf("hello word !\n");
  9 signal(2,_myhandle);
 10 }
 11 
 12 int main(){
 13 _myhandle = signal(2,myhandle);
 14  while(1);
 15  return 0;
 16 }

首先我们用Ctrl-C捕捉2号信号,并用_handler函数指针对象接受,在myhandler函数内,再次用Ctrl-C捕捉2号信号,并指向_handler捕捉成功,返回之前的信号处理函数,即恢复了默认处理,程序得以终止。

产生信号的方式:

1)通过终端按键产生信号(Core dump)。

2)调用系统函数向进程发送信号。

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

#include  
int kill(pid_t pid, int signum); //给任意进程发送任意信号
int raise(int signo); //给自己发送任意信号

参数pid:进程号。

参数signum:信号的编号。

这两个函数都是成功返回0,错误返回1.

abort可以使当前进程接收到信号而异常终止,但是abort会认为进程不安全。

3)由软件条件产生信号

进程可以通过调用alam向它自己发送SIGALRM信号,其函数原型如下:

#include 
unsigned int alam(unsigned int secs);

参数secs:alarm函数安排内核在secs秒内发送一个SIGALRM信号给调用进程。如果secs等于0,那么不会调度新的闹钟(alarm)。

返回值:前一次闹钟剩余的秒数,若以前没有设定闹钟,则为0。






推荐阅读
  • Linux环境变量函数getenv、putenv、setenv和unsetenv详解
    本文详细解释了Linux中的环境变量函数getenv、putenv、setenv和unsetenv的用法和功能。通过使用这些函数,可以获取、设置和删除环境变量的值。同时给出了相应的函数原型、参数说明和返回值。通过示例代码演示了如何使用getenv函数获取环境变量的值,并打印出来。 ... [详细]
  • CentOS 7部署KVM虚拟化环境之一架构介绍
    本文介绍了CentOS 7部署KVM虚拟化环境的架构,详细解释了虚拟化技术的概念和原理,包括全虚拟化和半虚拟化。同时介绍了虚拟机的概念和虚拟化软件的作用。 ... [详细]
  • 使用C++编写程序实现增加或删除桌面的右键列表项
    本文介绍了使用C++编写程序实现增加或删除桌面的右键列表项的方法。首先通过操作注册表来实现增加或删除右键列表项的目的,然后使用管理注册表的函数来编写程序。文章详细介绍了使用的五种函数:RegCreateKey、RegSetValueEx、RegOpenKeyEx、RegDeleteKey和RegCloseKey,并给出了增加一项的函数写法。通过本文的方法,可以方便地自定义桌面的右键列表项。 ... [详细]
  • 本文介绍了基于c语言的mcs51单片机定时器计数器的应用教程,包括定时器的设置和计数方法,以及中断函数的使用。同时介绍了定时器应用的举例,包括定时器中断函数的编写和频率值的计算方法。主函数中设置了T0模式和T1计数的初值,并开启了T0和T1的中断,最后启动了CPU中断。 ... [详细]
  • 本文介绍了UVALive6575题目Odd and Even Zeroes的解法,使用了数位dp和找规律的方法。阶乘的定义和性质被介绍,并给出了一些例子。其中,部分阶乘的尾零个数为奇数,部分为偶数。 ... [详细]
  • 本文介绍了指针的概念以及在函数调用时使用指针作为参数的情况。指针存放的是变量的地址,通过指针可以修改指针所指的变量的值。然而,如果想要修改指针的指向,就需要使用指针的引用。文章还通过一个简单的示例代码解释了指针的引用的使用方法,并思考了在修改指针的指向后,取指针的输出结果。 ... [详细]
  • 本文介绍了PE文件结构中的导出表的解析方法,包括获取区段头表、遍历查找所在的区段等步骤。通过该方法可以准确地解析PE文件中的导出表信息。 ... [详细]
  • C++中的三角函数计算及其应用
    本文介绍了C++中的三角函数的计算方法和应用,包括计算余弦、正弦、正切值以及反三角函数求对应的弧度制角度的示例代码。代码中使用了C++的数学库和命名空间,通过赋值和输出语句实现了三角函数的计算和结果显示。通过学习本文,读者可以了解到C++中三角函数的基本用法和应用场景。 ... [详细]
  • Android源码深入理解JNI技术的概述和应用
    本文介绍了Android源码中的JNI技术,包括概述和应用。JNI是Java Native Interface的缩写,是一种技术,可以实现Java程序调用Native语言写的函数,以及Native程序调用Java层的函数。在Android平台上,JNI充当了连接Java世界和Native世界的桥梁。本文通过分析Android源码中的相关文件和位置,深入探讨了JNI技术在Android开发中的重要性和应用场景。 ... [详细]
  • 在CentOS/RHEL 7/6,Fedora 27/26/25上安装JAVA 9的步骤和方法
    本文介绍了在CentOS/RHEL 7/6,Fedora 27/26/25上安装JAVA 9的详细步骤和方法。首先需要下载最新的Java SE Development Kit 9发行版,然后按照给出的Shell命令行方式进行安装。详细的步骤和方法请参考正文内容。 ... [详细]
  • 本文介绍了一个题目的解法,通过二分答案来解决问题,但困难在于如何进行检查。文章提供了一种逃逸方式,通过移动最慢的宿管来锁门时跑到更居中的位置,从而使所有合格的寝室都居中。文章还提到可以分开判断两边的情况,并使用前缀和的方式来求出在任意时刻能够到达宿管即将锁门的寝室的人数。最后,文章提到可以改成O(n)的直接枚举来解决问题。 ... [详细]
  • 3.223.28周学习总结中的贪心作业收获及困惑
    本文是对3.223.28周学习总结中的贪心作业进行总结,作者在解题过程中参考了他人的代码,但前提是要先理解题目并有解题思路。作者分享了自己在贪心作业中的收获,同时提到了一道让他困惑的题目,即input details部分引发的疑惑。 ... [详细]
  • 本文介绍了C++中的引用运算符及其应用。引用运算符是一种将变量定义为另一个变量的引用变量的方式,在改变其中一个变量时,两者均会同步变化。引用变量来源于数学,在计算机语言中用于储存计算结果或表示值抽象概念。变量可以通过变量名访问,在指令式语言中引用变量通常是可变的,但在纯函数式语言中可能是不可变的。本文还介绍了引用变量的示例及验证,以及引用变量在函数形参中的应用。当定义的函数使用引用型形参时,函数调用时形参的改变会同时带来实参的改变。 ... [详细]
  • 开发笔记:实验7的文件读写操作
    本文介绍了使用C++的ofstream和ifstream类进行文件读写操作的方法,包括创建文件、写入文件和读取文件的过程。同时还介绍了如何判断文件是否成功打开和关闭文件的方法。通过本文的学习,读者可以了解如何在C++中进行文件读写操作。 ... [详细]
  • Tomcat安装与配置教程及常见问题解决方法
    本文介绍了Tomcat的安装与配置教程,包括jdk版本的选择、域名解析、war文件的部署和访问、常见问题的解决方法等。其中涉及到的问题包括403问题、数据库连接问题、1130错误、2003错误、Java Runtime版本不兼容问题以及502错误等。最后还提到了项目的前后端连接代码的配置。通过本文的指导,读者可以顺利完成Tomcat的安装与配置,并解决常见的问题。 ... [详细]
author-avatar
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有