信号是什么?
信号其实就是传递的一种信息,让我们能根据这个信息进一步的去处理响应的事件。比如说:红绿灯,绿灯亮了,这就是一个信号,提醒我们此时可以安全的过马路了。比如我们键盘按下:Ctrl-C 给前台进程发送信号,终止进程。
对于信号的理解,我们可以类比现实生活,举个例子:老师给我们布置了一个作业,然后我们将作业记录下来是什么,然后需要一段时间去完成作业,最后上交给老师。linux下信号同样如此,需要产生一个信号,然后在一个适合的时间,进程去进行处理。
- 信号实质是软中断,用于通知进程发生某些事件。
- 信号作用:通知事件的发生,信号产生之后第一时间也不是直接处理,而是先存储下来。再处理信号
- 信号生命周期:信号的产生—>信号的注册—>信号的堵塞—>信号的注销—>信号的处理
信号是系统预先定义好的某些特定的事件,信号可以被产生,可以被接收,产生和接收的主体都是进程。进程收到信号之后,会有三种响应方式:忽略、默认、自定义(捕获)
1.忽略此信号。
2.执行该信号的默认处理动作(终止该信号)
3.提供一个信号处理函数(自定义动作),要求内核在处理该信号时切换到用户态执行这个处理函数,这种方式被称为 捕捉(catch)一个信号。
产生信号的方式:
(1)通过终端按键(组合键)产生信号
用户在终端按下某些键时,终端驱动程序会发送信号给前台进程,例如Ctrl-C 产生SIGINT信号,Ctrl-\ 产生SIGOUIT信号等
概念:当一个进程要异常终止时,可以选择把进程的用户空间内存数据全部保存到磁盘上,文件名通常是core,这叫做core Dump,也叫 核心转储,帮助开发者进行调试,在程序崩溃时把内存数据dump到磁盘上,让gdb 识别。
(2)硬件异常产生的信号
硬件异常产生信号,这些条件由硬件检测到并通知内核,然后内核向当前进程发送适当的信号。 例如当前进程执行了除以0的指令,CPU的运算单元会产生异常,内核将这个异常解释为 SIGFPE信号发送给进程。 再比如当前进程访问了非法内存地址,MMU会产生异常,内核将这个异常解释为SIGSEGV 信号发送给进程。
(3)调用系统函数向进程发信号
1. 一个进程可以调用 kill(2)函数 发送信号给另一个进程。可以用kill(1)命令发送信号给某个进程,kill(1)命令也是调用kill(2)函数实现的,如果不明确指定信号则发送给 SIGTREM信号,该信号的默认处理动作是终止进程。
2.raise函数可以给当前进程发送信号,相当于自己给自己发信号。
#include<signal.h>
int kill(pid_t pid, int signal);
int raise(int signal);
//都是成功返回0,失败返回-1;
3. absort函数可以使当前进程接收到信号而异常终止
#include<stdlib.h>
void absort(void);
//没有返回值,总能调用成功;
(4)由软件条件产生信号
1.SIGPIPE 是一种软件条件产生的信号。在匿名管道中,如果读端的文件描述符关闭了,那么写端就会发送SIGPIPE来 导致write进程终止退出。
2.alarm函数可以设置一个闹钟,在seconds 之后给当前进程发送一个SIGALARM信号,该信号的默认处理时结束当前进程。
#include<unistd.h>
unsigned int alarm(unsigned int seconds);
//返回值是0或者返回值是当前second余下的秒数;
//函数的返回值仍是以前闹钟设置的余下秒数
Signal()函数可以修改信号的响应方式:
Typedef void (*sighandle_t)(int);
Sighandle_t (*signal,sighandle_t);
忽略: SIG_IGN
默认: SIG_DFL
自定义: 自己写的信号处理函数
Kill函数可以向指定的进程发送指定的信号:
int kill(pid_t pid , int signo);
Pid > 0 指定将信号发送给那个进程
Pid==0 信号被发送到和当前进程在同一个进程组的进程
Pid == -1 将信号发送给系统上有权限发送的所有的进程
Pid < -1 将信号发送给进程组id等于pid 绝对值,并且有权限发送的所有的进程。
Sig 指定发送信号的类型
Raise函数将信号发送给自己:
Int raise(int sig);
相当于: kill(getpid,sig);
信号集
在实际的应用中一个应用层程序需要对多个信号进行处理,为了方便,linux系统引进了信号集概念。信号集用多个信号组成的数据类型sigset_t。可用以下的系统调用设置信号集中国所包含的数据。
Int sigemptyset(sigset_t *set); 将set集合置空,成功则返回0,失败则返回-1.
Int sigaddset(sigset_t *set, int signo)将signal信号加入到 set集合,成功则返回0,失败则返回-1.
Int sigdelset(sigset_t*set,int signo); 从set集合中移除signo信号,成功则返回0,失败则返回-1.
Int sigismember(const sigset_t *set , int signo ); signo 判断信号是否存在于set集合中,若真返回1,假返回0,失败返回-1.
Int sigprocmask(int how, const sigset_t *set,sigset_t *oset); 用于检测或更改其信号屏蔽字,包含头文件<signal.h> ,成功则返回0,失败则返回1.
参数:how 指示如何修改屏蔽信号
Set 是一个非空指针时,根据how修改屏蔽信号。
Oset是一个 非空指针时,存放当前屏蔽信号集
若set为NULL ,不改变该进程的信号屏蔽字,how也意义。