什么是Linux信号?
本质是一种通知机制,用户 or 操作系统通过发送一定的信号,通知进程,某些事件已经发生,你可以在后续进行处理
singal
结合进程,信号结论
a.进程要处理信号,必须具备信号“识别”的能力(看到+处理动作)
b.凭什么进程能够“识别”信号呢?程!
c信号产生是随机的,进程可能正在忙自己的事情,所以,信号的后续处理,可能不是立即处理的!
d.信号会临时的记录下对应的信号,方便后续进行处理
e.在什么时候处理呢?合适的时候
g.一般而言,信号的产生相对于进程而言是异步的
产生信号
信号的捕捉
调用signal函数,输入对应的信号码,就可以捕捉到对应的内容。
signal制作
信号如何产生
ctrl+c本质就是通过健盘組合键向目标进程发送2号信号,然后在执行的进程就退出了。
信号处理的常见方式:
a.默认(进程自带的,程序员写好的逻辑)
b.忽略(也是信号处理的一种方式)
c.自定义动作(捕捉信号)
如何理解组合键变成信号呢?
键盘的工作方式是通过:中断方式进行的,当然也能够识别组合键,ctrl+c
OS解释组合键->查找进程列表->前台运行的进程->OS写入对应的信号到进程内部的位图结构中!
如何理解信号被进程保存呢?
a.什么信号?
b.是否产生?
进程PCB内部保存了信号位图字段必须具有保存信号的相关数据结构(位图,unisgned int)
如何理解信号发送的本质?
信号位图是在task_struct -> task_struct内核数据结构->OS
核心转储
在进程等待的waitpid中的status参数第七位有个叫core dump的标志位就叫核心转储,当进程发生错误或者收到信号,那么就会有一个core的文件用于调试。
ulimit
ulimit -a可以查看是否开启核心转储,一般在云服务器上是关闭的
ulimit -c unlimited不限制文件大小
打开核心转储
终止进程后看到core文件
错误地方会立即标注
abort
使进程终止运行
验证服务器算力alarm
说明了io的速度是远远低于cpu的
为什么生产环境一般都是要关闭core
有可能会产生大量的代码,占用存储空间
系统调用接口
如何理解?用户调用系统接口->执行OS对应的系统调用代码->OS提取参数,或者设置特定的数值->OS向目标进程写信号->修改对应进程的信号标记位->进程后会处理信号->执行对应的处理动作!
由软件条件产生信号
管道中写端关闭写,读端关闭读
硬件异常产生信号
如何理解除0呢?
1.进行计算的是CPU,这个硬件
2.CPU内部是有寄存器的,状态寄存器(位图),有对应的状态标记位,溢出标记位,OS会自动进行计算完毕之后的检测!如果溢出标记位是1,OS里面识别到有溢出问题,立即只要找到当前谁在运行提取PID,OS完成信号发送的过程,进程会在合适的时候,进行处理
3.一旦出现硬件异常,进程一定会退出吗?不一定!一般默认是退出,但是我们即便不退出,我们也做不了什么
4. 为什么会死循环?寄存器中的异常一直没有被解决!
如何理解野指针或者越界问题?
1.都必须通过地址,找到目标位置
2. 我们语言上面的地址,全部都是虚拟地址
3. 将虚拟地址转成物理地址
4.页泰+MMU(Memory Manager Unit, 硬件!!)
5.野指针,越界-》非法地址-》MIMU转化的时候,一定会报错!
信号产生
task_struct中一共有三张和信号产生的表有关,pending、block和handler,panding表示未达代表了有无收到了信号,block表示是否屏蔽该信号,handler表示处理方法,实际上就是个回调函数。block 位图,结构和pending一模一样位图中的内容,代表的含义是对应的信号是否被阻塞
sigset_t
sigset_t--- 不允许用户自己进行位操作---0S 给我们提供了对应的操作位图的方法
sigset_t---user是可以直接使用该类型—-和用内置类型 && 自定义类型 沒有任何差別
sigset t---定需要对应的系统接口,来完成对应的功能,其中系統接口需要的参数,可能就包含了sigset t 定义的变量或者对象
#include<signal.h>
sigpending(sigset_t *set);
获取当前调用进程的pending信号集
int sigemptyset (sigset_t *set);
清空表中内容
int sigfillset(sigset_t *set);
将表中内容置1
int sigaddset (sigset_t *set, int signo);
将signal添加到表中,常用于block中添加屏蔽关键字
int sigdelset (sigset_t *set, int signo);
将signal从表中删除,常用于block中删除屏蔽关键字
int sigismember (const sigset _t *set, int signo);
可以用于输出该表中的成员
int sigprocmask(int how,const sigset_t *set,sigset_t *oldset)
其中how填以下图标的内容,oldset是输出型参数,返回老的set,如果不需要可以填nullptr
例1
捕捉信号,从1-31,其中管理员信号9不能被捕捉。
#include<iostream>
#include <signal.h>
#include<cassert>
#include<unistd.h>
using namespace std;
void handler(int signum){
cout<<"catching a signal now:"<<signum<<endl;
}
int main(){
cout<<getpid()<<endl;
for(int i=1;i<=31;i++){
signal(i,handler);
}
while(true)sleep(1);
}
例2
输出信号集,从1-31,,block2号信号,等待2号被捕捉。
#include<iostream>
#include <signal.h>
#include<cassert>
#include<unistd.h>
using namespace std;
void showsigpeding(sigset_t pending){
for(int i=1;i<31;i++){
if(sigismember(&pending,i)){
cout<<"1";
}else{
cout<<"0";
}
}
cout<<endl;
}
int main(){
sigset_t bset,obset;
sigset_t pending;
sigemptyset(&bset);
sigemptyset(&obset);
sigemptyset(&pending);
sigaddset(&bset,2);
int n=sigprocmask(SIG_BLOCK,&bset,&obset);
assert(n==0);
cout<<"sigprocmask success "<<"n:"<<n<<" "<<getpid()<<endl;
while(true){
sigpending(&pending);
showsigpeding(pending);
sleep(1);
}
}
信号产生之后,信号可能无法被立即处理,在合适的时候被处理,那么什么时候合适呢?
在内核态中,内核态返回用户态的时候进行检测
为什么会陷入内核态?
因为用户态会进行系统调用,缺陷陷阱异常等情况
用户态->内核态执行流程
用户态是一个受管控的状态,内核态是一个操作系统执行自己代码的一个状态,具有非常高的优先级。
cpu寄存器有两套,一套可见,平常所见ax,bx都是可见。一套不可见,表示cpu寄存器状态的,比如CR3表示当前cpu执行权限,1为内核,3为用户态。
捕捉信号
首先在用户态中因为一些系统中断、异常进行内核,可能是open之类的系统调用函数,进入内核中处理异常准备回用户模式前处理递送的信号,如果发现pending表为1,block表为0,那么就会去回调函数中的handler函数,handler函数处理完再返回用户态执行上次被中断的地方的语句,执行完毕再返回用户态执行main函数,执行其他流程。
sigaction
捕捉信号后10秒再重新清空捕捉
#include<iostream>
#include<signal.h>
#include<unistd.h>
using namespace std;
void showpending(sigset_t *pending ){
for(int i=1;i<=31;i++){
if(sigismember(pending,i))cout<<1;
else{
cout<<0;
}
}
cout<<endl;
}
void handler(int signum)
{
cout<<"获取了一个信号:"<< signum << endl;
cout<<"获取了一个信号:"<< signum << endl;
cout<<"获取了一个信号:"<< signum << endl;
cout<<"获取了一个信号:"<< signum << endl;
sigset_t pending;
int c=10;
while(true){
sigpending(&pending);
showpending(&pending);
c--;
if(!c){
break;
}
sleep(1);
}
}
int main(){
signal(2,SIG_IGN);
// signal(3,SIG_IGN);
// 内核数据类型,用户栈定义的
struct sigaction act, oact;
act.sa_flags = 0;
sigemptyset (&act.sa_mask);
act.sa_handler = handler;
// 设置进当前调用进程的pcb中
sigaction(2, &act, &oact);
cout<<"default action: "<<(int)(oact.sa_handler)<<endl;
while(true) sleep(1);
return 0;
}