信号(signal)是一种软中断,他提供了一种处理异步事件的方法,也是进程间唯一的异步通信方式。在Linux系统中,根据POSIX标准扩展以后的信号机制,不仅可以用来通知某进程发生了什么事件,还可以给进程传递数据。

1.1信号的来源

(1)硬件方式

·用户在终端上按下某些键时,将产生信号。如死循环时经常按的Ctrl+c

·硬件异常产生信号:除数为0、无效的存储访问等。

(2)软件方式

·用户在终端下调用kill命令向进程发送任意信号。

·进程调用kill或sigqueue函数发送信号。

·当检测到某种软件条件已经具备时发出信号,如alarm或settimer设置的定时器超时时将生成SIGALRM信号。

1.2信号的种类

在Shell下输入 kill -l 可显示Linux系统支持的全部信号。

信号的值在signal.h中定义

前30个信号的意义

https://blog.51cto.com/peacefulmind/1948591

从31号信号到第64信号是Linux的实时信号,他们没有固定的含义(可由用户自由使用)。Linux线程机制使用了前3个实时信号。所有实时信号的默认动作都是终止进程。

①可靠信号与不可靠信号

第1到第31号信号都是继承自UNIX系统,是不可靠信号。第33到第64号信号都是可靠信号,也称为实时信号。

信号的可靠性是指信号是否会丢失,或者说该信号是否支持排队。当导致产生信号的事件发生时,内核就产生一个信号。信号产生以后,内核通常会在进程表中设置某种形式的标志。当内核设置了这个标志以后,我们就称内核向进程传递了一个信号。信号产生和信号传递之间的时间间隔,称之为信号未决。

进程可以调用sigpending将信号设置为阻塞,若为进程产生了一个阻塞的信号,而对该信号的动作是捕捉该信号,则内核将为该进程的此信号保持为为决状态,直到该进程对此信号解除阻塞或者将此信号的响应更改为忽略。如果在进程解除对某个信号的阻塞之前,这种信号发生了多次,那么如果信号被递送多次(即信号在为决信号队列里排队),则称之为可靠信号;只被递送一次的信号被称之为不可靠信号。

②信号的优先级

由于信号的实质是软中断,中断有优先级,那信号同样有优先级。如果一个进程有多个为决信号,则对于同一个未决的实时信号,内核将按照发送的顺序来递送信号。若存在多个未决的实时信号,则值越小的越先被递送。如果既存在不可靠信号,有存在可靠信号,则Linux系统优先递送不可靠信号。

1.3进程对信号的响应

当信号发生时,用户可以要求进程以下列3种方式之一对信号做出响应。

①捕捉信号。对于要捕捉的信号,可以为其指定信号处理函数,信号发生时该函数自动被调用,在该函数内部实现对该信号的处理。

②忽略信号。大多数信号都可以使用这种方式进行处理,但是SIGKILL和SIGSTOP这俩信号不可以被忽略,这两个信号也不能被捕获或者阻塞。此外,如果忽略某些由硬件异常产生的信号,则进程的行为是不可预测的。

③按照系统默认方式处理。大部分信号的默认操作是终止进程,且所有的实时信号的默认动作都是终止进程

2.信号处理

2.1信号的捕捉和处理

Linux系统中对信号的处理主要由signal和sigaction函数来完成。

①signal函数

signal函数用来设置进程在接收到信号时的动作

#include<signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);

signal会根据参数signum指定的信号编辑来设置该信号的处理函数。当指定的信号到达时就会跳转到参数handler指定的函数执行。如果参数handler不是函数指针,则必须是常数SIG_IGN(忽略该信号)或SIG_DFI(对该信号执行默认操作)。handler是一个函数指针,它所指向的函数的类型时sighandler_t,即它所指向的函数有一个int型参数,且返回值的类型为void。

signal函数执行成功时返回以前的信号处理函数指针,当有错误发生时则返回SIG_ERR。

例程看一下signal怎么用

#include<stdio.h>
#include<signal.h>
void handler_sigint(int signo)
{
printf("recv SIGINT\n");
} int main()
{
signal(SIGINT, handler_sigint);
while(1);
return 0;
}

运行程序以后按 Ctrl+c

程序先使用signal()安装信号SIGINT的处理函数handler_sigint,然后进入死循环。当程序接收到SIGINT信号以后(Ctrl+c就是在给程序发送SIGINT信号),程序自动跳转到信号处理函数处执行。

②sigaction函数

sigaction函数可用来检查或设置进程在接收到信号时的动作。

#include<signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

sigaction会根据参数signum指定的信号来设置该信号的处理函数。参数signum可以是SIGKILL和SIGSTOP以外的任何信号。如果参数act不是空指针,则为signum设置新的信号处理函数;如果oldact不是空指针,则旧的信号处理函数将被存储在oldact中。struct sigaction定义如下

struct sigaction
{
void (*sa_handler)(int);
void (*sa_sigaction)(int, signfo_t *, void *);
sigset_t sa_mask;
void (*sa_restorer)(void);
}

sa_handler和sa_sigaction在某些体系结构上被定义为共用体,即这两个值在某一时刻只有一个有效。

数据成员sa_restorer已经作废,不再使用。

sa_handler可以是常数SIG_DFL或SIG_IGN,或者是一个信号处理函数的函数名。信号处理函数只有一个参数即信号编号。该参数和参数sa_sigaction实际上都是函数指针。

sa_sigaction也是用来指定函数signum的处理函数,它有三个参数,第一个参数是信号编号;第二个参数是指向siginfo_t结构的指针;第三个参数是一个指向任何类型的指针,一般不使用。

sa_mask成员声明了一个信号集,在调用信号捕捉函数前,该信号集会增加到进程的信号屏蔽码中,新的信号屏蔽码会自动包括正在处理的函数信号。当从信号捕捉函数返回时,进程的信号屏蔽码会恢复为原来的值。因此,当处理一个给定的信号时,如果这个信号再次发生,那么他会被阻塞到本次信号处理结束为止。若这个信号发生了多次,则对于不可靠信号,他只会被阻塞一次,即本次信号处理结束以后只会再处理一次(相当于丢失了信号);对于可靠信号(实时信号),则会被阻塞多次,即信号不会丢失,信号发生了多少次就会调用信号处理函数多少次。

sa_flags成员用来说明信号处理的一些其他相关操作。他可以取以下值或他们的组合

注:Linux下signal函数是由sigaction函数实现的

例程

#include<stdio.h>
#include<unistd.h>
#include<signal.h> int temp = 0; void handler_sigint(int signo)
{
printf("recv SIGINT\n");
sleep(5);
temp += 1;
printf("the value of temp is: %d\n", temp);
printf("in handler_sigint, after sleep\n");
} int main()
{
struct sigaction act;
act.sa_handler = handler_sigint;
act.sa_flags = SA_NOMASK;
//act.sa_flags = NULL;
sigaction(SIGINT, &act, NULL);
while(1);
return 0;
}

程序定义了一个act结构,并设置了act的sa_handler微信号处理函数handler_sigint,并且将sa_flags赋值为SA_NOMASK,即支持信号的嵌套处理。

快速按下 Ctrl+c 组合键两次,执行结果如下(实际上我按了4次)

程序启动后,接收SIGINT信号(我们按得Ctrl+c就是给程序发信号),程序打印出“recv SIGINT”表示信号处理函数处理了信号SIGINT,然后sleep(5)。在函数挂起期间,我们又按了三次 Ctrl+c ,由于我们设定了sa_flags的值为SA_NOMASK,因此程序又响应一次信号SIGINT,程序·从sleep()处嵌套调用信号处理函数handler_sigint,再一次打印出“recv SIGINT”。睡眠5秒后,将temp的值打印出来并返回到本次信号处理程序的跳入点sleep处,然后再打印出temp的值并返回到主函数。

从程序执行顺序可以看出来,temp的值随着被调用的次数而自增,而由于实际应用中信号总是随机发生的,这样temp的值也会随机变化。若main函数或其他地方还用到了这个全局变量,则程序将产生不可预料的结果。我们将这种函数称之为不可重入函数。编写信号处理函数不能使用不可重入函数。一般来说,满足一下条件的函数为不可重入函数:

(1)使用了静态的数据结构,如getgrgid(),全局变量等

(2)函数实现时,调用了malloc()函数或free()函数

(3)函数实现时,使用了标准I/O函数

将程序中的“act.sa_flags = SA_NOMASK”这行代码注释,将下一行注释取消,然后重新编译并执行,快速按3次及以上Ctrl+c,结果如下

可以看到,sigaction按照默认方式阻塞当前正在处理的信号,但为什么结果只有两个呢?这是因为SIGINT是不可靠信号,不可靠信号不支持排队,从而有可能丢失信号,从程序可以看出,第三个信号确实被丢失了。

③pause

pause函数使调用进程挂起直至捕捉到一个信号。

#include<unistd.h>
int pause(void);

pause函数会令目前的进程暂停(睡眠),直至被信号(signal)所中断。该函数只返回-1并将errno设置为EINTR.

Linux信号1的更多相关文章

  1. Linux信号类型说明

    说明 在Linux系统开发中经常要使用到信号来实现异步通知机制.而在Linux系统中信号有很多种,也不用全部记住,学习几种常见的信号,学会使用即可:当然也要知道用哪种方式能够发送这样的信号. 查看li ...

  2. Linux信号基础

    Linux信号基础   作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! Linux进程基础一文中已经提到,Linux以进程为单位来 ...

  3. Linux信号(signal) 机制分析

    Linux信号(signal) 机制分析 [摘要]本文分析了Linux内核对于信号的实现机制和应用层的相关处理.首先介绍了软中断信号的本质及信号的两种不同分类方法尤其是不可靠信号的原理.接着分析了内核 ...

  4. 利用linux信号机制调试段错误(Segment fault)

    在实际开发过程中,大家可能会遇到段错误的问题,虽然是个老问题,但是其带来的隐患是极大的,只要出现一次,程序立即崩溃中止.如果程序运行在PC中,segment fault的调试相对比较方便,因为可以通过 ...

  5. Linux信号实践(2) --信号分类

    信号分类 不可靠信号 Linux信号机制基本上是从UNIX系统中继承过来的.早期UNIX系统中的信号机制比较简单和原始,后来在实践中暴露出一些问题,它的主要问题是: 1.进程每次处理信号后,就将对信号 ...

  6. 非常好的一篇对linux信号(signal)的解析 (转载)【转】

    转自:https://blog.csdn.net/return_cc/article/details/78845346 Linux信号(signal) 机制分析 转载至:https://www.cnb ...

  7. Linux 信号:signal 与 sigaction

    0.Linux下查看支持的信号列表: france@Ubuntux64:~$ kill -l ) SIGHUP ) SIGINT ) SIGQUIT ) SIGILL ) SIGTRAP ) SIGA ...

  8. Linux信号机制

    Linux信号(signal) 机制分析 [摘要]本文分析了Linux内核对于信号的实现机制和应用层的相关处理.首先介绍了软中断信号的本质及信号的两种不同分类方法尤其是不可靠信号的原理.接着分析了内核 ...

  9. python学习笔记——多进程间通信——Linux信号基础

    1 信号的基本描述 Signal信号(其全程为软中断信号)是Linux系统编程中非常重要的概念,信号是异步进程中通信的一种方式. 作用是通知进程发生了异步事件.进程之间可以调用系统来传递信号, 本身内 ...

  10. linux信号Linux下Signal信号太详细了,终于找到了

    linux信号Linux下Signal信号太详细了,终于找到了 http://www.cppblog.com/sleepwom/archive/2010/12/27/137564.html

随机推荐

  1. Typora 快捷方式

    1.标题编写 方法一:几个#号 代表几级标题  (共6级) 方法二:ctrl +1 .2.3.4.5.6 2.如何编写子标题 第一种:无序子标题(无序列表) *号  +  空格书写标题文本   (输入 ...

  2. Zabbix 4.4 离线安装 使用mariadb的踩坑,无法停止服务

    先分享一个网站,之前就没注意过有这个网站,不知道是啥时候开放的.里面分享了N多zabbix的模板. https://share.zabbix.com/ 报错如下 Unsupported charset ...

  3. 全面!总结BQ系列阻抗跟踪电量计化学Chemical ID配置和Golden学习方法

    BQ系列阻抗跟踪电量计SOC最高能达到1%,功能强大,应用起来也比较复杂.不仅要配置好参数,匹配好化学ID,并且进行好Golden学习和相关测试.本文就讲述ID匹配,Golden学习和测试的终极方法流 ...

  4. React 三大属性state,props,refs以及组件嵌套的应用

    React 三大属性state,props,refs以及组件嵌套的应用 该项目实现了一个简单的表单输入添加列表的内容 代码如下 <!DOCTYPE html> <html> & ...

  5. ssh密码登录

    https://stackoverflow.com/a/16928662/8025086 https://askubuntu.com/a/634789/861079 #!/usr/bin/expect ...

  6. Mac 下安装 MySQL 步骤

    安装 MySQL Mac 下安装MySQL推荐去官网下载dmg 版本的,我使用的版本是5.7.30. 如上图所示. 之后就是傻瓜式一键狂点不过需要注意的是,不要关闭下图所示的框框!不要关闭下图所示的框 ...

  7. flume的配置详解

    Flume:===================== Flume是一种分布式的.可靠的.可用的服务,可以有效地收集.聚合和移动大量的日志数据. 它有一个基于流数据的简单而灵活的体系结构. 它具有健壮 ...

  8. 【拥抱元宇宙】创建你的第一个Unity程序HelloWorld,并发布

    第一个Unity程序--Hello World. 1.需要先下载一个Unity Hub,以及安装Unity编辑器.Unity Hub需要登陆,激活码可以选择个人用户,免费的.免费的无法改变启动画面,其 ...

  9. JDBC操作多张表一

    一.操作一对多情况开发步骤1创建对象 //代码部门的对象public class Department { private String id; private String name; privat ...

  10. [bzoj1107]驾驶考试

    转化题意,如果一个点k符合条件,当且仅当k能到达1和n考虑如果l和r($l<r$)符合条件,容易证明那么[l,r]的所有点都将会符合条件,因此答案是一个区间枚举答案区间[l,r],考虑如何判定答 ...