c/c++ signal(信号)解析_c++ signal-程序员宅基地

技术标签: 操作系统  

什么是信号(signal)

信号是一种软件中断,一种向进程传递有关其他进程,操作系统和硬件状态的信息的方法。信号是一种中断,因为它可以改变程序的流程。当信号传递给进程时,进程将停止其执行的操作,处理或忽略信号,或者在某些情况下终止,取决于信号。

由于信号可能源自当前正在执行的过程之外的事实,信号也可能以不可预测的方式传递,与程序不一致。查看信号的另一种方法是一种处理异步事件的机制。与同步事件相反,同步事件是标准程序执行迭代,即一行代码跟随另一行。当程序的某些部分按顺序执行时,就会发生异步事件。异步事件通常由于源自硬件或操作系统的外部事件而发生;信号本身是操作系统将这些事件传递给进程的方式,以便进程可以采取适当的操作。

如何使用它们

信号在Unix编程中用于各种各样的目的,我们已经在较小的上下文中使用它们。例如,当我们在shell中工作并希望“杀死所有cat程序”时,我们输入命令:

#> killall cat

killall命令将向所有名为cat的进程发送一个信号,表示“终止”。发送的实际信号是SIGTERM,其目的是将终止请求传送给给定进程,但该进程实际上不必终止…稍后将详细说明。

我们还在终端信令的上下文中使用和查看信号,这是程序停止,启动和终止的方式。我们输入Ctrl-c与发送SIGINT信号相同,输入Ctrl-z与发送SIGTSTP信号相同,我们输入fg或bg与发送SIGCONT信号相同。

这些信号中的每一个都描述了该过程应该采取的响应动作。此操作超出了程序的正常控制流程,事件异步到达,要求进程中断其当前操作以响应事件。对于上述信号,响应是明确的 - SIGTERM终止,SIGSTOP停止,SIGCONT继续 - 但对于其他信号,程序员可以选择正确的响应,这可能只是简单地忽略信号。

常用的Signals

每个信号都有一个名称,它以SIG开头,以描述结束。我们可以在手册页的第7节中查看所有信号,下面是您可能与之交互的标准Linux信号:

Signal Value Action Comment
SIGHUP 1 Term Hangup detected on controlling terminal or death of controlling process
SIGINT 2 Term Interrupt from keyboard
SIGQUIT 3 Core Quit from keyboard
SIGILL 4 Core Illegal Instruction
SIGABRT 6 Core Abort signal from abort(3)
SIGFPE 8 Core Floating point exception
SIGKILL 9 Term Kill signal
SIGSEGV 11 Core Invalid memory reference
SIGPIPE 13 Term Broken pipe: write to pipe with no readers
SIGALRM 14 Term Timer signal from alarm(2)
SIGTERM 15 Term Termination signal
SIGUSR1 30,10,16 Term User-defined signal 1
SIGUSR2 31,12,17 Term User-defined signal 2
SIGCHLD 20,17,18 Ign Child stopped or terminated
SIGCONT 19,18,25 Cont Continue if stopped
SIGSTOP 17,19,23 Stop Stop process
SIGTSTP 18,20,24 Stop Stop typed at tty
SIGTTIN 21,21,26 Stop tty input for background process
SIGTTOU 22,22,27 Stop tty output for background process

信号的name和value关联

每个信号都有名称,值和默认操作。信号名称应该开始变得更加熟悉,信号的值实际上与信号本身相同。实际上,信号名称只是一个#defined值,我们可以通过查看sys / signal.h头文件来看到:

#define SIGHUP  1       /* hangup */
#define SIGINT  2       /* interrupt */
#define SIGQUIT 3       /* quit */
#define SIGILL  4       /* illegal instruction (not reset when caught) */
#define SIGTRAP 5       /* trace trap (not reset when caught) */
#define SIGABRT 6       /* abort() */
#define SIGPOLL 7       /* pollable event ([XSR] generated, not supported) */
#define SIGFPE  8       /* floating point exception */
#define SIGKILL 9       /* kill (cannot be caught or ignored) */

信号的 Action

每个信号都有一个默认动作。表中描述了四种:

Term : The process will terminate
Core : The process will terminate and produce a core dump file that traces the process state at the time of termination.
Ign : The process will ignore the signal
Stop : The process will stop, like with a Ctrl-Z
Cont : The process will continue from being stopped

对于某些信号,我们可以更改默认操作。一些信号,即控制信号,不能改变它们的默认动作,包括SIGKILL和SIGABRT,这就是为什么“kill 9”是最终的kill语句。

处理和生成信号

1. Hello信号处理世界

信号处理的主要系统调用是signal(),它给出信号和功能,只要信号被传送就会执行该功能。此函数称为信号处理程序,因为它处理信号。 signal()函数有一个奇怪的声明:

int signal(int signum, void (*handler)(int))

也就是说,signal有两个参数:第一个参数是信号编号,例如SIGSTOP或SIGINT,第二个参数是对第一个参数为int并返回void的处理函数的引用。

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

#include <signal.h> /*for signal() and raise()*/

void hello(int signum){
  printf("Hello World!\n");
}

int main(){
  //execute hello() when receiving signal SIGUSR1  
  signal(SIGUSR1, hello);

  //send SIGUSR1 to the calling process  
  raise(SIGUSR1);
}

上述程序首先为用户信号SIGUSR1建立信号处理程序。信号处理函数hello()按预期执行:打印“Hello World!”到stdout。程序然后发送SIGUSR1信号,这是通过raise()完成的,执行程序的结果是漂亮的短语:

#> ./hello_signal
Hello World!

2.异步执行

从hello程序中取消的一些关键点是signal()的第二个参数是一个函数指针,一个对要调用的函数的引用。这告诉操作系统无论何时将此信号发送到此进程,都要将此函数作为信号处理程序运行。

此外,信号处理程序的执行是异步的,这意味着程序的当前状态将在信号处理程序执行时暂停,然后执行将从暂停点恢复,就像上下文切换一样

让我们看看另一个示例hello world程序:

/* hello_loop.c*/
void hello(int signum){
  printf("Hello World!\n");
}

int main(){
  //Handle SIGINT with hello
  signal(SIGINT, hello);

  //loop forever!
  while(1);

}

上面的程序将为SIGINT设置一个信号处理程序,键入Ctrl-C时生成的信号。问题是,当我们执行这个程序时,键入Ctrl-C会发生什么?

首先,让我们考虑一下程序的执行情况。它将注册信号处理程序,然后进入无限循环。当我们按下Ctrl-C时,我们都同意信号处理程序hello()应该执行并且“Hello World!”打印到屏幕上,但程序处于无限循环中。为了打印“Hello World!”一定是它打破循环执行信号处理程序的情况,对吗?所以它应该退出循环以及程序。让我们来看看:

#> ./hello_loop
^CHello World!
^CHello World!
^CHello World!
^CHello World!
^CHello World!
^CHello World!
^CHello World!
^\Quit: 3

如输出所示,每次我们发出Ctrl-C“Hello World!”打印,但程序返回无限循环。只有在用Ctrl- \发出SIGQUIT信号后,程序才真正退出。

虽然循环将退出的解释是合理的,但它没有考虑信号处理的主要原因,即异步事件处理。这意味着信号处理程序的行为超出了程序控制的标准流程;事实上,整个程序都保存在一个上下文中,并且只为信号处理程序创建了一个新的上下文。如果你再考虑一下,你会发现这很酷,也是一种全新的方式。查看编程。

3.进程间通信

信号也是进程间通信的关键手段。一个进程可以向另一个进程发送信号,指示应该采取措施。要向特定进程发送信号,我们使用kill()系统调用。功能声明如下。

int kill(pid_t pid, int signum);

与命令行版本非常相似,kill()接受一个进程标识符和一个信号,在这种情况下,信号值为int,但值为#defined,因此您可以使用该名称。让我们看看它在使用中。

/*ipc_signal.c*/
void hello(){
  printf("Hello World!\n");
}

int main(){

  pid_t cpid;
  pid_t ppid;

  //set handler for SIGUSR1 to hello()
  signal(SIGUSR1, hello);

  if ( (cpid = fork()) == 0){
    /*CHILD*/

    //get parent's pid
    ppid = getppid();

    //send SIGUSR1 signal to parrent
    kill(ppid, SIGUSR1);
    exit(0);

  }else{
    /*PARENT*/

    //just wait for child to terminate
    wait(NULL);
  }

}

在这个程序中,首先为SIGUSR1建立一个信号处理程序,即hello()函数。在fork之后,父进程调用wait(),并且子进程将通过SIGUSR1信号“杀死”它来与父进行通信。结果是在父级和“Hello World!”中调用了处理程序。从父级打印到stdout。

虽然这只是一个小例子,但信号对于进程间通信是不可或缺的。在前面的课程中,我们讨论了如何使用pipe()在进程之间传递数据,信号是进程传递状态更改和其他异步事件的方式。也许最相关的是儿童过程中的状态变化。 SIGCHLD信号是孩子终止时传递给父母的信号。到目前为止,我们一直在通过wait()隐式处理这个信号,但您可以选择处理SIGCHLD并在子进程终止时采取不同的操作。

4.忽略信号

到目前为止,我们的处理程序一直在打印“Hello World!” - 但我们可能只是希望我们的处理程序什么也不做,基本上是忽略了信号。这很容易写入代码,例如,这是一个程序,它将通过处理信号忽略SIGINT并且什么都不做:

/*ingore_sigint.c*/
#include <signal.h>
#include <sys/signal.h>

void nothing(int signum){ /*DO NOTHING*/ }

int main(){
  signal(SIGINT, nothing);
  while(1);
}

如果我们运行这个程序,我们会看到,是的,Ctrl-c无效,我们必须使用Ctrl- \来退出程序:

>./ignore_sigint
^C^C^C^C^C^C^C^C^C^C^\Quit: 3

signal.h标头定义了一组可用于代替处理程序的操作:

  • SIG_IGN:忽略信号
  • SIG_DFL:用默认处理程序替换当前信号处理程序
    使用这些关键字,我们可以简单地将程序重写为:
int main(){

  // using SIG_IGN
  signal(SIGINT, SIG_IGN);
  while(1);
}

5.更改并恢复默认处理程序

设置信号处理程序不是一个单一事件。您始终可以更改处理程序,还可以将处理程序恢复为默认状态。例如,请考虑以下程序:

/*you_shot_me.c*/
void handler_3(int signum){
  printf("Don't you dare shoot me one more time!\n");

  //Revert to default handler, will exit on next SIGINT
  signal(SIGINT, SIG_DFL);
}

void handler_2(int signum){
  printf("Hey, you shot me again!\n");

  //switch handler to handler_3
  signal(SIGINT, handler_3);
}

void handler_1(int signum){
  printf("You shot me!\n");

  //switch handler to handler_2
  signal(SIGINT, handler_2);
}

int main(){
  //Handle SIGINT with handler_1
  signal(SIGINT, handler_1);
  //loop forever!
  while(1);
}
#> ./you_shout_me
^CYou shot me!
^CHey, you shot me again!
^CDon't you dare shoot me one more time!
^C

程序首先启动handler_1()作为SIGINT的信号处理程序。在第一个Ctrl-c之后,在信号处理程序中,处理程序更改为handler_2(),在第二个Ctrl-c之后,它再次从handler_2()更改为handler_3()。最后,在handler_3()中重新建立默认信号处理程序,即在SIGINT上终止,这就是我们在输出中看到的:

#> ./you_shout_me
^CYou shot me!
^CHey, you shot me again!
^CDon't you dare shoot me one more time!
^C

6.有些信号比其他信号更平等

关于信号处理的最后一点是,并非所有信号都是相同的。这意味着,您无法处理所有信号,因为它可能会使系统处于不可恢复的状态。

永远不会被忽略或处理的两个信号是:SIGKILL和SIGSTOP。我们来看一个例子:

/* ignore_stop.c */
int main(){
  //ignore SIGSTOP ?
  signal(SIGSTOP, SIG_IGN);
  //infinite loop
  while(1);
}

上面的程序试图为SIGSTOP设置忽略信号处理程序,然后进入无限循环。如果我们执行该计划,我们发现这些努力没有结果:

#>./ignore_stop
^Z
[1]+  Stopped                 ./ignore_stop

对于忽略SIGKILL的程序,我们可以看到相同的内容。

int main(){
  //ignore SIGSTOP ?
  signal(SIGKILL, SIG_IGN);
  //infinite loop
  while(1);
}
#>./ignore_kill &
[1] 13129
#>kill -SIGKILL 13129
[1]+  Killed: 9               ./ignore_kill

7.检查信号错误()

signal()函数返回一个指向前一个信号处理程序的指针,这意味着这里再次是一个系统调用,我们不能通过检查返回值是否小于0来以典型的方式进行错误检查。这是因为指针类型是无符号的,没有负指针这样的东西。

相反,使用特殊值SIG_ERR,我们可以比较signal()的返回值。这里再次是我们尝试忽略SIGKILL的程序,但这次正确的错误检查:

/*signal_errorcheck.c*/
int main(){
  //ignore SIGSTOP ?
  if( signal(SIGKILL, SIG_IGN) == SIG_ERR){
    perror("signal");;
    exit(1);
  }
  //infinite loop
  while(1);
}

输出

#>./signal_errorcheck
signal: Invalid argument
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/sysleo/article/details/95984946

智能推荐

前端导出当前页面为PDF或者图片_前端怎样导出整个网页为pdf-程序员宅基地

文章浏览阅读412次。前端导出当前页面为PDF或者图片_前端怎样导出整个网页为pdf

linux grep 正则表达式-程序员宅基地

文章浏览阅读1.1w次,点赞5次,收藏50次。目录1、grep命令2、grep 与正则表达式3、关于匹配的实例4、grep实例1、grep命令功能:输入文件的每一行中查找字符串。基本用法:grep [-acinv] [--color=auto] [-A n] [-B n] '搜寻字符串' 文件名参数说明:-a:将二进制文档以文本方式处理-c:显示匹配次数-i:忽略大小写差异-n:在行首显示行号-A:After的意思,显示匹配字符串后n行的数据-B:before的意思,显示匹配字符串前n行的数据-v:显示_linux grep 正则表达式

电商数据分析17——电商平台评价系统的数据分析与管理_优化业绩评价平台-程序员宅基地

文章浏览阅读1.1k次,点赞22次,收藏18次。在数字经济时代,电商平台已成为消费者购物的首选。随之而来,评价系统作为连接消费者与产品的重要桥梁,对于购买决策和产品改进起着至关重要的作用。通过数据分析来优化评价管理,不仅可以提升产品信誉,还能显著提高销售业绩。本文将深入探讨评价系统的作用、数据分析在评价系统管理中的应用,以及通过实际案例来展示评价系统管理优化的策略。_优化业绩评价平台

在linux中挂载磁盘ext3和ext4之间的区别_linux 命令mount ext3 和 ext4 u盘有什么区别-程序员宅基地

文章浏览阅读1.7k次。转载:https://blog.csdn.net/zhaoyoulin2016/article/details/80221101Linux kernel 自 2.6.28 开始正式支持新的文件系统 Ext4。 Ext4 是 Ext3 的改进版,修改了 Ext3中部分重要的数据结构,而不仅仅像 Ext3 对 Ext2 那样,只是增加了一个日志功能而已。Ext4可以提供更佳的性能和可靠性,还有更..._linux 命令mount ext3 和 ext4 u盘有什么区别

专业课知识问答-背诵版-程序员宅基地

文章浏览阅读671次,点赞8次,收藏8次。背背背,不理解怎么背?

计算机毕业设计springboot校园流浪猫管理平台777i79[附源码]_监控校园流浪猫-程序员宅基地

文章浏览阅读191次。选题背景:随着社会的发展和进步,越来越多的人开始关注动物福利问题。在校园中,流浪猫的数量逐渐增加,给校园环境和学生生活带来了一定的影响。因此,设计一个校园流浪猫管理平台具有重要的现实意义和紧迫性。选题意义:首先,校园流浪猫管理平台可以提供一个集中管理的平台,方便学校和相关部门对流浪猫进行统一管理和监控。通过该平台,可以及时记录流浪猫的数量、位置和健康状况等信息,为后续的管理工作提供数据支持。其次,该平台可以促进校园内流浪猫的救助和领养工作。通过平台的信息发布功能,可以将流浪猫的情况及时通知到感兴_监控校园流浪猫

随便推点

如何提高或者修改WiFi发射功率-程序员宅基地

文章浏览阅读2.6k次。比如MTK系列网卡驱动方案,驱动里边提供了一个.dat的配置文件,配置文件里边有TxPower的选项,(值为0—100,Set Transmit Power by percentage),网卡模块在出厂的时候已经将校准功率值写入EEPROM了,修改TxPower的值可适当微调功率,但默认值为100。如果要提高发射功率那只能修改EEPROM了。每个厂家都有自己的一套修改网卡模块功率值的办法,彻底修改..._mt6762修改wifi功率

Delphi实现对注册表的监视和扫描[转]-程序员宅基地

文章浏览阅读61次。声明:CSDN以外的任合团体和个人转载本文必须注明出处和作者。 Delphi自带的TRegistry类只能实现注册表的基本操作,如果我们要实时监视注册表的变化或者扫描注册表特定项下的所有子项,TRegistry类就无能为力了。我啃了半天SDK,终于实现了Delphi对注册表的监视与扫描,不敢独享,拿来献给广大的Delphi爱好者。 监视注册表相关项的改变要用到一个AP..._api监视注册表变动

C++动态规划经典案例解析之合并石子_c++石子合并优化-程序员宅基地

文章浏览阅读2.3k次,点赞4次,收藏14次。区间类型问题,指求一个数列中某一段区间的值,包括求和、最值等简单或复杂问题。此类问题也适用于动态规划思想。如前缀和就是极简单的区间问题。现给定区间信息[3,6],求区间内所有数字相加结果。即求如下图位置数字之和。区间至少包括2个属性,起始端和结束端,求和范围包含左端和右端数字。累加数组中0~6区间的值s1。累加数组中0~2区间的值s2。将s1中的值减去s2中的值。得到最终结果。如果对任意区间的求解要求较频繁,会存在大量的重复计算。如分别求区间[2,5]和[1,5]之和时,分析可知区间。_c++石子合并优化

前端权限控制(一):前端权限管理及动态路由配置方案_前端权限管理如何实现-程序员宅基地

文章浏览阅读1.1w次,点赞10次,收藏135次。在项目中,尤其是在后台管理系统中,不同人员登陆,看到的页面菜单是不一样的,比如,一个公司的办公系统,超级管理员登陆可以看到所有的页面,而普通员工账号登录可能无法看到人员管理等页面,比如公司的员工个人资料页面只有人力资源部门有权利看,其他部门的员工是不允许查看公司员工信息数据的。当然了除了页面的权限,还会有一些按钮级别的权限,比如一个下载按钮,有的帐号可以用,有的人不能用,比如人员账号管理中,一个页面中有一个确认添加、删除该账号人员按钮,这个按钮只有管理员有权利点击,其他人员登陆是无法点击的。........_前端权限管理如何实现

java毕设分享 基于Django与协同过滤的电影推荐系统-程序员宅基地

文章浏览阅读238次,点赞5次,收藏5次。基于Django与协同过滤的电影推荐系统电影推荐系统——实现用户登录、评分、推荐,采用协同过滤算法用户注册、登录系统,对看过的电影进行评分,点击提交评分按钮,再点击查看推荐按钮即可看见推荐的电影列表。项目主页以及推荐结果如下:1.首先将项目克隆到本地,用Pycharm打开movierecommend文件夹,并install项目依赖2.将用到的csv文件导入mysql数据表中,详见数据库建表 ,配置好数据库;注意数据库相关代码(settings.py、views.py)可能都要进行修改以符合实际情况;(

c语言中point的用法_C/C++编程笔记:C语言编程知识要点总结!大一C语言知识点(全)...-程序员宅基地

文章浏览阅读3.5k次,点赞5次,收藏8次。一、C语言程序的构成与C++、Java相比,C语言其实很简单,但却非常重要。因为它是C++、Java的基础。不把C语言基础打扎实,很难成为程序员高手。1、C语言的结构先通过一个简单的例子,把C语言的基础打牢。C语言的结构要掌握以下几点:(1)C语言的注释是/* ··· */,而不是//···,//是C++的单行注释,有的C语言版本也认可。(2)C语言区分大小写,每句以分号结尾。(3)C语..._c语言point函数

推荐文章

热门文章

相关标签