初识信号:
信号是一个软件中断,通知进程发生了某个事件,打断进程当前的操作,去处理这个事件。
系统定义的信号列表中,1-31是非可靠信号,45-64是可靠信号。
kill杀死一个进程的原理:
给进程发送一个信号(默认发送15号信号)进程收到信号处理事件(默认为退出)
信号的生命周期:信号的产生,在进程中注册,在进程中注销,信号的处理
信号的几种产生方式
- 通过终端按键产生信号
ctrl+c 中断 ctrl+z停止 ctrl+\退出
- 通过系统函数向进程发送信号
kill命令调用kill函数
eg:kill -SIGSEGV 4567
- 由软件条件产生信号
#include<signal.h>
int kill(pid_t pid,int singo)----发送的信号可以自己确定
int raise(int singo)-----只能给自身发送信号
#include<stdlib.h>
void abort(void)----使当前进程接受到信号,而异常终止
信号在进程中注册
在进程pcb中有一个位图,包含的信息是—未决(没有被处理)信号集合,进程可以通过这个位图知道自己接收到了哪些信号,信号的进程中注册就是将位图中的对应位置改为1
在进程pcb中还有一个双向链表,sigqueue,链表中相同信号信息节点的数量就是这个信号在进程中出现的次数
非可靠信号注册:如果信号已经注册但没有被处理,第二个相同信号到来的时候,双向链表没有改变,只有一个节点
可靠信号的注册:只要信号到来都会再添加一个新的节点,可以有多个节点
信号的注销
消除信号在pcb中的痕迹,将位图中的对应位置置0
信号的处理
信号的处理就是完成一个功能,调用这个信号的信号处理函数,并执行函数。
有三种处理方法:
忽略处理方式:忽略此信号 SIG_IGN
默认处理方式:执行该信号的默认处理方法 SIG_DFL
自定义处理方式:提供一个信号处理函数,改变默认的处理方法,要求内核在执行到该信号时切换到用户态执行这个默认的信号处理函数,此操作称之为捕捉信号
typedef void(*sighandler_t)(int)------函数指针
sighandler_t signal(int signum—信号值,sighandler_t handler----函数指针)
signum:指定要修改信号处理方式的信号值
handler:用于指定signum信号新的信号处理函数
sighandler_t:函数指针类型名称
信号的捕捉流程
1.主控流程收到系统调用,中断或异常,切换到内核态。
2.在内核态完成系统调用内核功能,完成后即将返回用户态,
3.在返回用户态之前,会调用接口do_signal检测处理当前收到的信号
4.如果有待处理的信号,则返回用户态执行信号的自定义处理函数(默认和忽略处理不用返回用户态,只有自定义处理函数需要反悔到用户态)
5.执行完毕后再次返回内核态,执行3操作,返回用户态从主控流程中断的地方继续向下运行
用户态:自己写的代码,以及库函数
内核态:运行内核中的代码完成功能
用户态切换到内核态的情况:系统调用,中断,异常
信号处理的时间:程序从内核态返回用户态之前进行信号的处理
有三个相关的位图:pending位图,block位图,handler信号处理方式数组
pending位图显示了有哪些信号,信号的值就是对应处理方式的下标。handler记录对应处理方式,block是阻塞集合,如果信号阻塞则进行标记
在所有信号中,9号强杀和19号停止,这两个信号不会被阻塞,不会被自定义,不会忽略
实际应用:
SIGCHLD:17号信号—僵尸进程:子进程先于父进程退出,就会给父进程发送一个SIGCHLD进行事件通知,然而SIGCHLD的默认处理方式就是忽略,因此父进程如果没有一直等待,子进程就会成为僵尸进程。如果不想让父进程阻塞,可以自定义SIGCHLD的信号处理函数,在信号处理函数中调用wait或waitpid接口进行处理,SIGCHLD信号是非可靠信号,多个子进程同时到来可能会丢失事件
sigcb(int no){
while(waitpid(-1,NULL,WNOHANG)>0);
}
SIGPIPE:所有管道读端被关闭,继续write会触发异常,这个异常对应的信号就是SIGPIPE,默认处理方式是退出进程
volatile关键字:用于修饰一个变量,防止编译器对变量的过度优化,让cpu每次操作变量都是从内存中获取,而不是直接从寄存器中获取。
可重入和不可重入
判断标准:一个函数是否对全局变量进行了不受保护的非原子操作
//使用函数指针进行自定义信号处理函数
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
//no参数--是实际出发本次信号回调函数的信号值
void (*oldcb)(int);
void sigcb(int no)
{
printf("recv a signal no:%d\n", no);
signal(no, oldcb);
}
int main (int argc, char *argv[])
{
int a = 10;
printf("%d\n", a/0);
//signal(信号, 函数地址);
oldcb = signal(SIGINT, sigcb);
while(1) {
printf("下雨天要吃巧克力~~\n");
sleep(1);
}
return 0;
}