快乐Linux —— 11. 异常与信号
参考:
https://www.cnblogs.com/hanerfan/p/3592826.html 中断及信号
http://blog.sina.com.cn/s/blog_864017a701018vtb.html 各种信号及其释义
https://blog.csdn.net/woliuyunyicai/article/details/45165869 并发,并行,同步,异步,多线程之间的关系。
简介
异常与信号
异常
异常概念
讲信号,离不开异常。所谓的异常就是进程执行指令序列的突变,下图很好的阐述了这个观点。
异常共有以下四种。
异常的异步和同步其实就是产生异常的指令是否是确定的,同步就是产生异常的指令是特定的,异步就是产生异常的指令不一定。
-
中断(interrupt)
中断是异步发生的,是来自处理器外部的I/O设备的信号结果。
-
陷阱(trap)
提供了用户程序和操作系统间的接口,系统调用。
-
故障(fault)
故障有可能被修复,如果修复成功,就重新执行引起故障的指令,否则执行终止程序。
-
终止(abort)
异常处理
在内核中,有一个保存异常处理程序的数组称为异常表。异常号实际上就是这个数组的下标。
一部分异常号是由处理器提供的(除0,缺页,断点,内存访问违规等)另一部分是由操作系统提供的(系统调用,外部 I/O 设备的信号)。
当程序运行时检测到哪种异常号,就去找到相应的异常处理程序。然后执行。
与普通的函数调用有以下区别:
- 异常处理后执行的指令有可能还是当前指令(例如页错误),有可能是下一条指令。而函数调用肯定是下一条指令。
- 异常处理程序运行在内核模式。
信号的产生 及 处理方式
信号应该就是进程对内核中断的一种模拟了,主要用于进程之间的信息的传递,触发的相应的处理函数在进程的用户空间里执行。
Linux 下的信号库 #include <signal.h>
在终端输入此命令可以查看系统提供的信号。kill -l
系统定义的信号路径 /usr/include/bits/signum.h
信号的产生渠道有以下几种:
- 用户按下某些终端键,引发终端产生信号,例如 Ctrl + C
- 硬件异常产生信号,由硬件检测到并通知内核,然后内核产生适当信号,如 除0错误。
- 进程调用 kill 系统调用函数发送信号给当一个进程或进程组,条件是同一个进程所有者或超级用户。
- 用户终端调用 kill 命令讲终止信号发给其他进程。这个终端 kill 命令只是 kill 系统调用函数的接口。
- 当某种软件条件发生,并应将其通知有关进程时。例如 进程所设置的闹钟到达时间。
对信号的处理方式有以下三种:
- 忽略此信号。 除了 SIGKILL(终止信号) 和 SIGSTOP(停止信号)不可忽略。
- 执行系统默认动作。 大部分默认动作是终止,少部分是忽略。
- 捕捉信号。自定义一个信号处理函数,通知内核当接收到信号时调用用户自定义的信号处理函数。其中SIGKILL 和 SIGSTOP 不可捕捉。
修改信号处理方式
除了 SIGKILL 和 SIGSTOP,其它信号的处理方式都可以由以下的函数修改:
//signal 函数有三种写法
void (*signal (int sign, void(*fun)(int) ))(int);
//下面是另一种写法
typedef void (*FUN)(int);
FUN signal(int sign, FUN fun);
//或者
typedef void FUN(int);
FUN* signal(int sign, FUN* fun);
-
参数
整型值传入是要处理的信号。
函数指针是决定了要怎么处理这个信号,总共有三种选择
- SIG_DFL 选择执行系统默认动作 。
- SIG_IGN 忽略这个信号。
- 自定义的函数。
-
返回值
返回上一次该信号指定的处理方式。可以返回 SIG_DFL 或 SIG_IGN
信号操作
发送信号
int kill(pid_t pid,int sign);
int raise(int sign);
关于kill ,根据其参数值不同有以下四种情况:
失败返回-1,成功返回0;
而raise向该进程发送信号 也就相当于kill第一参数传入 getpid()。
接受信号
内核接受到后会自动执行相应的操作,有可能调用信号处理程序,有可能忽略,或者执行默认操作。