信号(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. redis 集群环境搭建

    原理: 1,每个Redis群集的节点都需要打开两个TCP连接,由于这两个连接就需要两个端口,分别是用于为客户端提供服务的常规Redis TCP命令端口(例如6379)以及通过将10000和命令端口相加 ...

  2. 使用vsftpd 搭建ftp服务

    ftp 基础服务器基础知识 ftp有三种登录方式.匿名登录(所有用户).本地用户.虚拟用户(guest). FTP工作模式 主动模式:服务端从20端口主动向客户端发起链接. 控制端口21:数据传输端口 ...

  3. docker使用redis过程出现的问题记录

    第一次使用docker搭建成功了单机版redis,但在使用过程中,还是遇到了不少问题,故而先把这些问题记录下来,以防后面再出现会忘记. 目前,只是在docker中搭建了三个单机版的容器,打算先捣鼓一周 ...

  4. fiddler 手机+浏览器 抓包

    用fiddler对手机上的程序进行抓包   前提: 1.必须确保安装fiddler的电脑和手机在同一个wifi环境下 备注:如果电脑用的是台式机,可以安装一个随身wifi,来确保台式机和手机在同一wi ...

  5. NAT & 防火墙

    NAT 网络地址转换 NAT产生背景 今天,无数快乐的互联网用户在尽情享受Internet带来的乐趣.他们浏览新闻,搜索资料,下载软件,广交新朋,分享信息,甚至于足不出户获取一切日用所需.企业利用互联 ...

  6. 设计系统(Design System),设计和开发之间的“DevOps”

    最近,我们网站的上新增了几个新功能,比如通过导航栏的QR Code可以下载App:通过Carousel的方式,显示多条信息. 以往这样的功能可能需要2-3个Sprints完成,但是现在这些功能都是在一 ...

  7. Part 11 to 20 Basic in C# continue

    Part 11-12 switch statement in C# switch statement break statement   if break statement is used insi ...

  8. Java设计模式之(九)——门面模式

    1.什么是门面模式? Provide a unified interface to a set of interfaces in a subsystem.Facade defines a higher ...

  9. JDK中Lambda表达式的序列化与SerializedLambda的巧妙使用

    前提 笔者在下班空余时间想以Javassist为核心基于JDBC写一套摒弃反射调用的轻量级的ORM框架,过程中有研读mybatis.tk-mapper.mybatis-plus和spring-boot ...

  10. 6.K8s集群升级、etcd备份和恢复、资源对象及其yaml文件使用总结、常用维护命令

    1.K8s集群升级 集群升级有一定的风险,需充分测试验证后实施 集群升级需要停止服务,可以采用逐个节点滚动升级的方式 1.1 准备新版本二进制文件 查看现在的版本 root@k8-master1:~# ...