信号的概念
信号的产生
信号的注册
信号的注销
自定义信号处理方式
信号的捕捉流程
信号阻塞
信号的概念:
1.信号是一个软件中断
2.信号的种类:
kill -l命令的时候,我们会看到62种信号
1~31 : 非可靠信号, 信号有可能会丢失
34~64 : 可靠信号, 信号是不可能丢失的
信号的产生
1.1 硬件产生ctrl + c : SIGINT(2) 发送给前台进程如何把一个进程放到后台去运行:在启动命令之后加 & 符号fg:就将刚刚放到后台的进程,放到前台来运行ctrl + z :SIGTSTP(20), 不要用,除非有特定的场景ctrl + | : SIGQUIT(3)产生核心转储文件1.产生核心转储文件的条件a.当前操作系统当中一定不要限制coredump文件的大小ulimit -a ==> core file sizeb.磁盘空间2.2.1 解引用了空指针, 进程就会收到11号信号,导致进程coredump2.2 内存访问越界,一旦进程崩溃,进程就会收到11号信号,也就会理论上产生coredump文件2.3 double free进程会受到6号信号,导致当前的进程退出,并产生coredump注:free(NULL) : linux 和 windows 下钧不会导致崩溃1.2 软件产生(1)kill 函数
int kill(pid_t pid, int sig);
pid : 要给哪一个进程发送信号
sig: 要发送的具体信号值
(2)kill -[信号] /[pid/] ->eg: kill -9 12121
(3)abort函数:
abort(pid_t pid); -->6号信号(SIGABRT) --> (double free)
谁调用该函数,谁收到6号信号
封装了kill
kill(getpid(), 6);
信号的注册
- 1个位图 + 1个sigquen队列
(1).sig数组不是按照long类型来使用的,而是按照bit位来使用的
(2).每一个信号在该位图当中都存在一个与之对应的比特位
(3).当与之对应的比特位为1的时候,表示当前进程收到了该信号
(4).对于位图当中的bit位,每一个比特位有与之对应的信号,直到我们当前操作信号表示完毕
非可靠信号的注册
1.当进程收到一个非可靠信号
第一件事情:将非可靠信号对应的比特位更改为1
第二件事情:添加sigqueue节点到sigqueue队列当中
但是,如果在添加sigqueue节点的时候,队列种已然有了该信号的sigqueue节点,则不
添加
可靠信号的注册
1.如果进程收到一个可靠信号
第一件事情: 在sig位图当中更改该信号对应的比特位为1
第二件事情: 不论之前sigqueue队列当中是否存在该信号的sigqueue节点,都再次添
加sigqueue节点到sigqueue队列当中去
信号的注销
非可靠信号的注销
1.将该信号的sigqueue节点从sigqueue队列当中进行出队操作
2.信号在sig位图当中对应的比特位从1置为0
可靠信号的注销
1.将该信号的sigqueue节点从sigqueue队列当中进行出队操作
2.需要在判断sigqueue队列当中是否还有sigqueue节点
2.1 没有了
信号在sig位图当中对应的比特位从1置为0
2.2 还存在
不会更改sig位图当中对应的比特位从1置为0
自定义信号处理方式
1.SIG_DFL : 默认处理方式
2.SIG_IGN : 忽略处理
SIGCHLD 信号
3.自定义信号的处理方式
signal函数
typedef void(*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
//该函数可以更改掉信号的处理动作
内核的源码:sched.h
find/usr -name sched.h
ctags
1.ctags R
ctrl + ] --> 跳转到定义
ctrl + o --> 跳转到上一次的位置
sigaction函数
int sigaction(int signum, const struct sigaction* act, struct sigaction* oldact);
signum:待更改的信号的值
struct sigaction{void(*sa handler)(int);//函数指针,报讯了内核对信号的处理方式void(*sa_sigaction)(int, siginfo_t*, void*);sigset_t sa_mask;//保存的是当进程在处理信号的时候,收到的信号int sa_flags;//SA_SIGINFO,操作系统在处理信号的时候,调用的就是sa_sigaction函数指针 //当中保存的值0,在处理信号的时候,调用sa_handler保存的函数void(*sa_restorer)(void);//预留信息
};
act: 将信号处理函数改变为act
oldact:信号之前的处理方式
int sigcmptyset(sigset_t* set);//将位图的所有比特设置为0
信号的捕捉流程
什么时候进入到内核空间:调用系统调用的时候,或者调用库函数的时候(库函数底层大多数
都是封装系统调用函数的)
信号阻塞
1.信号的阻塞,并不会干扰信号的注册!
信号该注册还是注册的,只不过当前的进程不能立即处理了
2.
2.1 当我们将block位图当中对应的信号的bite位置为1,表示当前进程阻塞该信号
2.2 当进程收到一个该信号的时候,进程还是一如既往的对该信号进行注册
2.3 当进程进入到内核空间,准备返回用户空间的时候,调用do_signal函数,这会不会立即
去处理该函数了
2.4 这里面的不会立即处理,一定不是之后不处理
int sigprocmask(int how, const sigset_t* set, sigset_t* oldset);
how:告诉sigpromask函数,应该做什么操作
SIG_BLOCK: 设置某个信号为阻塞
SIG_UNBLOCK: 解除某个信号的阻塞
SIG_SETMASK: 替换阻塞位图
set : 用来设置阻塞位图
SIG_BLOCK:设置某个信号为阻塞
block(new) = block(old) | set
SIG_UNBLOCK:接触对某个信号的阻塞
block(new) = block(old) & (~set)
SIG_SETMASK:替换阻塞位图
block(new) = set
oldset: 原来的阻塞位图
SIGCHLD信号
子进程先于父进程退出,子进程会给父进程发送一个SIGCHLD信号,父进程对SIGCHLD信号默认是忽略处理的,导致子进程的资源没有被回收,成为僵尸进程。