信号(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. Mybatis实现简单的CRUD(增删改查)原理及实例分析

    Mybatis实现简单的CRUD(增删改查) 用到的数据库: CREATE DATABASE `mybatis`; USE `mybatis`; DROP TABLE IF EXISTS `user` ...

  2. Typora简介

    Typora是什么 Typora是一款支持实时预览的Markdown文本编辑器,拥有macOS.Windows.Linux三个平台的版本,并且完全免费. 下载地址:https://www.typora ...

  3. kail入侵win2003 sp2 中文版系统

    攻击机:kail -- IP:192.168.74.132 目标靶机:win2003 sp2 中文版系统-- IP:192.168.74.128 一.扫描漏洞 1. 在kail中运行 namp 192 ...

  4. Linux下的 sniff-andthen-spoof程序编写

    Linux下的 sniff-andthen-spoof程序编写 一.任务描述 在本任务中,您将结合嗅探和欺骗技术来实现以下嗅探然后欺骗程序.你需要两台机器在同一个局域网.从机器A ping IP_X, ...

  5. Python命令行参数及文件读出写入

    看完了柯老板的个人编程作业,虽然是评测组不用做此次作业,但还是想对本次作业涉及到利用Python命令行参数以及进行文件读出写入操作做一个简单的总结.(个人编程作业还是想自己能敲一敲,毕竟我的码力还是小 ...

  6. request模块做post请求时,body为json格式,并且带有中文,如何请求

    后台接口只能解析json,并且一定要是中文才能解析出来,如果是unicode编码的中文则会报错 看requests的源码.以下为解决方法: #将requests库中的models.py文件中的第461 ...

  7. [hdu6582]Path

    首先,从1和n跑一次dij,判断每一条边能否出现在最短路上,不能出现就删掉,然后将所有边建在图上,流量为边权,跑最小割即可. 1 #include<bits/stdc++.h> 2 usi ...

  8. [loj4]Quine

    很有趣的一道题目,如何让一个程序输出自身如果用字符串s表示程序,那么意味着可以通过s来输出sprintf是一个可以利用的函数,相当于要求printf(s,s)输出的就是s那么只需要在s中加入%c和%d ...

  9. 『学了就忘』Linux用户管理 — 52、用户组管理相关命令

    目录 1.添加用户组 2.删除用户组 3.把用户添加进组或从组中删除 4.有效组(了解) 1.添加用户组 添加用户组的命令是groupadd. 命令格式如下: [root@localhost ~]# ...

  10. mybatis源码分析二

    这次分析mybatis的xml文件 1. <?xml version="1.0" encoding="UTF-8" ?> <configura ...