目录
一.信号的概念
二.信号的种类
三.信号的产生方式
3.1硬件产生的三种方式
四.信号注册
4.1非可靠信号的注册
4.2可靠信号注册
五.信号注销
5.1非可靠信号的注销
5.2可靠信号的注销
六.信号的处理方式?
七.信号的捕捉
八.信号的阻塞
8.1设置接口阻塞
8.2验证可靠信号和非可靠信号
一.信号的概念
在Linux系统中,信号的存在是必然的,信号可以理解为是一个软件中断,在某个条件下,系统会发出某个信号给正在运行的进程,通知进程需要执行某一特定的事件。
二.信号的种类
在终端中输入命令"kill -l"可列出Linux系统中的所有信号,如下:
- 非可靠信号(非实时信号).非可靠信号为1~31信号,信号可能会丢失。
- 可靠信号(实时信号)。可靠信号为34~64信号,信号绝对不会丢失。
三.信号的产生方式
3.1硬件产生的三种方式
- ctrl + c:可以产生SIGINT信号
- ctrl + |:可以产生SIGQUIT信号
- ctrl + z:可以产生SIGTSTP信号
3.2软件产生
- kill命令
- kill -9命令
- kill() ;主要用于向指定的进程或进程组发送信号,它的定义如下:
#include<stdio.h>
#include<signal.h>
int kill(pid_t pid , int sig);
pid | 参数pid为进程号或进程组号 pid = 0,将信号发送到当前进程所在的进程组中的每一个进程 pid = -1,将信号发送给除了init进程外的当前进程中有权发送的所有进程 pid < -1,将信号发送给进程组(-pid)中的每一个进程 若pid为一个有效的进程或进程组,信号将发送给pid代表的进程或进程组 | sig | 要发送信号的类型编号 若蚕食sig为0,就无信号可发送,但会进行错误检查 | - raise() ;主要用于将信号发送给当前进程,该函数原型为:
#include<stdio.h>
#nclude<signak.h>
int rasie(sig); 参数:sig为发送的信号类型的编号。如果函数调用成功,返回值为0;失败则为非0。 - abort();6号信号,使当前进程接收到信号而异常终止?该函数原型:
#include <stdlib.h>
void abort(void);
//就像exit函数一样,abort函数总是会成功的,所以没有返回值 - alarm();14号信号,该函数主要用于为发送的信号色丁一个时间警告,是系统在设定的时间之后发送信号,该函数原型:
#include<unistd.h>
unsigned int alarm(unsigned int seconds); 参数seconds设定为时间值。如果seconds设置为0,那么alarm()设置的警告始终将无效。alarm()安盼在seconds时间之后,发送一个信号SIGALRM给进程。在默认情况下,进程接收到这个信号会终止运行。
四.信号注册
信号注册前提:
- 在内核当中task_struct结构体中,保存一个struct sigpending的对象pending,struct sigpending这个结构体当中保存了两个元素。第一个元素是:struct list_head list;第二个元素:sigset_t signal;sigset_t类型而是一个结构体类型,struct sigset_t保存了一个unsigned long sig[];在这里sig这个数字是按照比特位来使用的,我们称之为位图。
- 内核当中还维护了一个sigqueue队列,队列当中的每个元素对应信号的一个处理节点。
与信号注册相关的两个点:sig位图,sigqueue队列
catgs . -R
ctrl + ] ==>跳转到光标所在类型的定义地方
ctrl + o==>回到光标上一次所在位置
typedef struct{
usingned long sig[_NSIG_WORDS];
//并不是按照正常的数组下标方式进行访问,他是按照位图方式进行访问的;每一个比特位代表一个信号,但是还是还有预留
//:64 / 32;_NSIG_WORDS是由两个宏相除得来的
//_NSIG / _NSIG_BPW
}sigset_t;
sigquene队列
前提-->同一个信号注册两次
4.1非可靠信号的注册
- 一次:将信号对应的比特位改称1 + 在sigqueue队列当中添加sigqueue结点
- 第二次:自会将型号对应的比特位从1改为1,不会添加sigqueue结点
4.2可靠信号注册
- 第一次:将信号对应的比特位改称1 + 在sigqueue队列当中添加sigqueue结点
- 第二次:还会再sigqueue队列当中添加可靠信号的sigqueue结点
五.信号注销
5.1非可靠信号的注销
在sigqueue队列当中将对应信号的节点进行出队操作
将对应的比特位值为0
5.2可靠信号的注销
将可靠信号对应的sigqueue结点从sigqueue队列当中进行出队操作
判断sigqueue队列当中是否还有当前可靠信号的sigqueue结点。如果没有,则会将sig位图当中可靠信号队形的比特位置为0;如果有,则不会将sig位图当中的可骄傲信号对应的比特为置为0。
六.信号的处理方式?
- 默认处理方式:SIGDFL
- 忽略处理方式:SIGING,SIGCHLD(子进程退出时会给父进程发送一个SIGCHLD信号)
- 自定义处理方式:想要完成更改信号的处理方式。在Linux中可以使用signal()与sigaction()对默认的信号处理方法进行修改。
自定义信号处理前提:
- 在task_struct结构体当中,有一个指向sighand_struct的结构体指针,在该结构体当中有一个action数组,数组当中的每一个元素都是一个struct k_digaction结构体,数组当中每一个元素对应一个信号处理逻辑。
- 在struct k_sigaction结构体当中有一个元素是dtruct sigaction sa,在struct sigaction结构体当中有一个sighandler_t类型的元素,这个sighandler_t是一个函数指针类型,typedef void(*sighandler)(int),保存吸纳后默认执行的函数。
操作系统对信号的处理:
当sig位图当中收到某一个信号的时候,意味着sig位图当中的某一个比特位被置为1了,操作系统处理该信号的时候,就会从PCB当中去寻找sighang_struct结构体的指针,从而找到da_handler,进而操作系统内核条用sa_handler保存函数地址,完成信号功能。
?6.1signal()
typedef void(*sighandler_t)(int);
sigandler_t signal(int signum,sighandler_t handler);
?参数:
?注意:在系统提供的信号类型种,SIGKILL和SIGSTOP信号不能被捕获或者忽略。
6.2sigaction()
int sigaction(int signum,const struct sigaction* act,struct sigaction* oldact);
struct sigaction{
void (*sa_handler)(int);//默认保存信号处理函数地址的函数指针
void (*sa_sigaction)(int, siginfo_t* ,void*);
int sa_flags;//如果说sa_flags的值为SA_SIGINFO,则在处理信号的时候会用sa_sigaction函数指针保存的函数地址来进行处理信号
sigset_t sa_mask;//当正在处理信号的时候,会将注册的信号先放到sa_mask当中进行过度,当处理完成该信号之后,会将刚才注册信号放到sig位图当中
void (*sa_restorer)(void);//预留信息
};
?参数:
- signum:待自定义处理函数的信号
- act:想要将喜好更改成为什么处理动作
- oldact:信号处理之前的动作
int sigemptyset(sigset_t* set);
//会将位图当中的比特位全部清空为0
int sigfillset(sigset* set);
//会将位图当中的比特位全部清空为1
注意:如果两个ligaction结构体类型指针act和oldact都指向空,则连个指针参数不会实现上述功能。?
?总结:
- signal函数是调用sigcation函数
- signal函数是更改函数指针中保存的函数地址
- sigaction函数是更改struct sigaction结构体的
七.信号的捕捉
从前面信号中有3种对型号的处理方法,一种是系统对信号的默认处理方法,一种是忽略信号,还有一种是捕捉信号。
- 执行流从内核态切换到用户态之前一定会调用do_signal函数处理信号
- 从用户态切换到内核态的时候,是是调用了系统调用函数,或者进程异常。
- 在进行系统调用or程序员调用库函数且库函数底层调用了系统调用or程序出现异常(访问空指针,内存访问越界,double free),此时会进入内核态。
八.信号的阻塞
信号的阻塞指的是准备处理信号的时候,会判断当前信号是否为阻塞,如果该信号为阻塞,则暂时不去处理信号。
?信号的阻塞并不是说信号不能被注册,不会影响更改限号pending位图和增加sigqueue节点
操作系统处理信号的逻辑:
- 当程序从用户态切换到内核态之后,处理do_signal函数的时候,发现收到某个信号,想要处理这个信号之前,先判断block位图当中对应信号的bit位是否为1
- block当中对应bit位为1:则不处理该信号,sigqueue当中对应的信号节点还在;block当中对应bit位为0,则处理该信号
8.1设置接口阻塞
int sigprocmask(int how,const sigset_t* set,sigset *oldset);
参数:
- how:告诉sigprocmask函数以什么方式进行工作,以下是三个方式;
SIG_BLOCK | 设置某个信号为阻塞状态 block(new) = blick(old) | set | SIG_UNBLOCK | 解除信号为阻塞状态 block(new) = blick(old) &?(~set) | SIG_SETMASK | 替换原来的block位图 block(new) = set |
8.2验证可靠信号和非可靠信号
?结论:
- 信号的阻塞不会干扰信号的注册的
- 可靠信号在收到多次,会处理多次,而非可靠信号收到多次只处理一次。非可靠信号可能会导致信号丢失。(处理原则:实时信号先处理,非实时信号后处理,处理时按照队列的出队操作进行处理,先进先出策略)
- 9号信号和19号信号是不能被阻塞的
其他知识点:
volatile关键字:保持内存可见性
|