Linux高级编程--11.信号
基本概念
信号在Linux中是一个比较常见的概念,例如我们按Ctrl+C中断前台进程,通过Kill命令结束进程都是通过信号实现的。下面就以Ctrl+C为例简单的说明信号的处理流程:
- 用户按下Ctrl-C,这个键盘输入产生一个硬件中断。
- 该进程的用户空间代码暂停执行,CPU从用户态切换到内核态处理硬件中断。
- 终端驱动程序将Ctrl-C解释成一个SIGINT信号,记在该进程的PCB中(也可以说发送了一个SIGINT信号给该进程)。
- 当内核返回到该进程的用户空间代码继续执行之前,首先处理PCB中记录的信号,发现有一个SIGINT信号待处理,而这个信号的默认处理动作是终止进程,所以直接终止进程而不再返回它的用户空间代码执行。
用kill -l命令可以察看系统定义的信号列表:
$ kill –l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL
5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE
9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2
13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGSTKFLT
17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU
25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH
29) SIGIO 30) SIGPWR 31) SIGSYS 34) SIGRTMIN
35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3 38) SIGRTMIN+4
39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12
47) SIGRTMIN+13 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14
51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10
55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7 58) SIGRTMAX-6
59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
每个信号都有一个编号和一个宏定义名称,这些宏定义可以在signal.h中找到,可以通过man signal(7)查看详细说明:
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
...
产生信号的条件主要有:
用户在终端按下某些键时,终端驱动程序会发送信号给前台进程。
例如常见的Ctrl-C产生SIGINT信号,Ctrl-\产生SIGQUIT信号,Ctrl-Z产生SIGTSTP信号。
硬件异常产生信号,这些条件由硬件检测到并通知内核,然后内核向当前进程发送适当的信号。例如当前进程执行了除以0的指令,CPU的运算单元会产生异常,内核将这个异常解释为SIGFPE信号发送给进程。
一个进程调用kill函数可以发送信号给另一个进程。
当内核检测到某种软件条件发生时也可以通过信号通知进程,例如闹钟超时产生SIGALRM信号,向读端已关闭的管道写数据时产生SIGPIPE信号。
捕捉信号
如果不想按默认动作处理信号,用户程序可以调用sigaction函数接管该信号的处理流程。由于信号处理函数的代码是在用户空间的,处理过程比较复杂,举例如下:
- 用户程序注册了SIGQUIT信号的处理函数sighandler。
- 当前正在执行main函数,这时发生中断或异常切换到内核态。
- 在中断处理完毕后要返回用户态的main函数之前检查到有信号SIGQUIT递达。
- 内核决定返回用户态后不是恢复main函数的上下文继续执行,而是执行sighandler函数,sighandler和main函数使用不同的堆栈空间,它们之间不存在调用和被调用的关系,是两个独立的控制流程。
- ighandler函数返回后自动执行特殊的系统调用sigreturn再次进入内核态。
- 如果没有新的信号要递达,这次再返回用户态就是恢复main函数的上下文继续执行了。
sigaction函数可以读取和修改与指定信号相关联的处理动作,它的声明如下:
#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
其中关键就是第二个参数act,他是一个sigaction类型,结构如下:
struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
};
其中关键的是sa_handler这个参数,它通常可以有如下几种赋值:
- 常数SIG_IGN表示忽略信号
- 常数SIG_DFL表示执行系统默认动作,一般用于恢复信号处理
- 赋值为一个函数指针表示用自定义函数捕捉信号
另外,如果在调用信号处理函数时,除了当前信号被自动屏蔽之外,还希望自动屏蔽另外一些信号,则用sa_mask字段说明这些需要额外屏蔽的信号,当信号处理函数返回时自动恢复原来的信号屏蔽字。
下面就以一个简单的例子演示下如何实现捕捉信号的过程,该函数的功能比较简单,就是在Ctrl+C的时候并不直接退出,而是先输出一条华丽的分割线后才退出。
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
void show_and_exit(int sig)
{
printf("\n----------------------------\n");
exit(0);
}
int main(void)
{
struct sigaction act = {0}, oldact = {0};
act.sa_handler = show_and_exit;
//act.sa_flags = SA_RESETHAND | SA_NODEFER;
//sigaddset(&act.sa_mask, SIGQUIT);
sigaction(SIGINT, &act, &oldact);
int count = 0;
while(1)
{
sleep(1);
printf("sleeping %d\n", count++);
}
}
执行该函数结果如下:
$./sign
sleeping 0
sleeping 1
sleeping 2
sleeping 3
sleeping 4
----------------------------
$
Linux高级编程--11.信号的更多相关文章
- linux高级编程基础系列:线程间通信
linux高级编程基础系列:线程间通信 转载:原文地址http://blog.163.com/jimking_2010/blog/static/1716015352013102510748824/ 线 ...
- linux系统编程之信号(七)
今天继续学习信号,主要是学习关于时间和定时器相关的函数的使用,关于这个实际上有很多内容,这里先简要进行说明,等之后再慢慢进行相关深入,也主要是为接下来要做的一个综合linux系统编程的例子做准备,好了 ...
- UNIX环境高级编程——可靠信号与不可靠信号
在早期的UNIX中信号是不可靠的,不可靠在这里指的是:信号可能丢失,一个信号发生了,但进程却可能一直不知道这一点. 现在Linux 在SIGRTMIN实时信号之前的都叫不可靠信号,这里的不可靠主要是不 ...
- linux系统编程之信号(一):中断与信号
一,什么是中断? 1.中断的基本概念 中断是指计算机在执行期间,系统内发生任何非寻常的或非预期的急需处理事件,使得CPU暂时中断当前正在执行的程序而转去执行相应的事件处理程序,待处理完毕后又返回原来被 ...
- Linux高级编程--04.GDB调试程序(查看数据)
查看栈信息 当程序被停住了,你需要做的第一件事就是查看程序是在哪里停住的.当你的程序调用了一个函数,函数的地址,函数参数,函数内的局部变量都会被压入"栈"(Stack)中.你可以用 ...
- linux高级编程
系统调用 01.什么是系统调用? 02.Linux系统调用之I/O操作(文件操作) 03.文件描述符的复制:dup(), dup2() 多进程实现多任务 04.进程的介绍 05.Linux可执行文件结 ...
- 〖Linux〗Linux高级编程 - 进程间通信(Interprocess Communication)
[转自: http://blog.csdn.net/Paradise_for_why/article/details/5550619] 这一章就是著名的IPC,这个东西实际的作用和它的名字一样普及.例 ...
- linux系统编程之信号(二):信号处理流程(产生、注册、注销、执行)
对于一个完整的信号生命周期(从信号发送到相应的处理函数执行完毕)来说,可以分为三个阶段: 信号诞生 信号在进程中注册 信号在进程中的注销 信号处理函数执行 1 信号诞生 信号事件 ...
- linux c编程:信号(一)
信号是软件中断,很多比较重要的应用程序都需要处理信号.并且信号提供了一种处理异步事件的方法.如终端用户键入中断键,会通过信号机制停止一个程序,或及早终止管道中的下一个程序 很多条件都可以产生信号,比如 ...
随机推荐
- 从混战到三足鼎立,外卖O2O下一个谁先出局?
来自第三方数据挖掘和分析机构权威iiMedia Research(艾媒咨询)发布的<2016Q3中国在线餐饮外卖市场专题研究报告>显示,2016Q3中国在线餐饮外卖市场活跃用户分布方面,美 ...
- LR11.0 下载及破解
1. 把loadrunner相关程序全部退出:2. 用LR8.0中的mlr5lprg.dll.lm70.dll覆盖LR安装目录下“bin”文件夹中的对应文件:3. 清理注册表(不清理的话,在添加lic ...
- PHP中常用的正则表达式由哪些元素构成?
在程序开发中,我们常常要用到正则表达式,对于新手来说,很多时候知道正则表达式是怎么回事,但当真正需要使用的时候,却不知该用什么函数,具体的修饰符也比较混乱.下面小编就为大家整理了一些php正则表达式中 ...
- Android 发布可穿戴设备 SDK 的开发者预览版
今早上安卓官网查资料,发现网站上赫然显示着"Android Wear"几个大字.难道……?点进去看,果然,Android发布了可穿戴设备的SDK的开发者预览版. 其中这第五张图…… ...
- VMware安装RedHat Linux虚拟机图文详解
创建Red Hat Linux虚拟机 1.打开VMware,开始创建虚拟机 点击菜单[文件]->[新建虚拟机]. 2.默认典型,单击[下一步] 3.选择安装来源 在这里,我们选择安装来源为[安装 ...
- <后会无期>经典影评
先说明是转载,任何不同意见请对原作者表达,楼主不作任何回应,楼主影商极低,楼主觉得这二十几年来看的最好的电影是<一代宗师>,楼主只是觉得这篇影评精彩才发布上来让更多的人看到.原作者意见和楼 ...
- [ay原创作品]用wpf写了个模仿36Kr网站登录背景的效果
这里我借鉴了,上周比较火的一个前端文章,人家用js去写的,地址 自己用wpf也写了一个,但是它的 粒子比较,然后连线算法真的很差,他创建了一个加入鼠标点的集合,2个集合进行比较,并且粒子会向鼠标靠近 ...
- Android 使用java.net.socket 的接收问题
// 初始化socketsocket = new Socket(InetAddress.getByName(sip), sport);InputStream sin = socket.getInput ...
- Web 开发常备工具
工欲善其事,必先利其器.如今 Web 开发标准越来越高,Web 开发者也在不断寻找途径提升自己的技能.为使大家的开发工作更顺利进行,本文整理了 10+ 款比较优秀的 Web 开发工具,希望对你有帮助. ...
- 关于css布局的几篇文章
这可能是史上最全的CSS自适应布局总结 (http://www.cnblogs.com/qieguo/p/5421252.html) 使用 CSS 弹性框 (https://developer.moz ...