🚩预备工作

1,进程必须要能识别信号
2,进程即使没收到信号,也要知道怎么处理这些信号处理信号能力是内核系统内置功能
3,进程收到信号,可能不会立即处理信号,等到合适时机再处理
4,进程收到信号,到处理信号,必然有时间窗口,也就是进程有保存信号已经发生的能力

🚩处理信号的三种模式:
1,默认
2,忽略
3,自定义

#include <iostream>
#include <unistd.h>
using namespace std;
int main()
{
    while(1)
    {
        cout<<"i am a process"<<endl;
        sleep(1);
    }
    return  0;
}

./process
在这里插入图片描述

为什么 cirl+c能杀掉进程?因为发送了终止信号

前台进程后台进程

杀掉的是前台进程,每次登录linux,会配一个bash只允许一个进程是前台进程,其他的都是后台进程键盘输入的是前台进程

如果改为后台进程?

./process &

在这里插入图片描述
可以看到,我们甚至可以输入ls ,即使乱码了,因为键盘输入发给前台进程ctrl+c也杀不掉后台进程

为什么乱码了也能执行ls?我们键盘有自己的缓冲区,只是我们输入的时候给显示器发送一份而已,而显示器又在接收其他进程,互相干扰乱码了,但是我们键盘缓冲区存储了ls

并且后台进程可以启动多份的
我可以多运行几次该程序,打印速度就会变快

ps ajx | grep mypocess
查到进程pid kill -9 杀掉

kill -l
查看所有进程

在这里插入图片描述
ctrl + c向前台发送2号信号了,而2号信号默认又代表终止,怎么验证?

自定义信号

在这里插入图片描述

signal捕捉信号,再自己实现该信号功能,这就是自定义信号

#include <iostream>
#include <unistd.h>
#include <signal.h>
using namespace std;
void myhandler(int signo)
{
    cout<<"get a signal: "<<signo<<endl;
}
int main()
{
    signal(SIGINT,myhandler);//只需设置一次,往后都有效
    while(1)
    {
        cout<<"i am a process"<<endl;
        sleep(1);
    }
    return  0;
}

在这里插入图片描述

9号信号是捕捉不到的

硬件中断

讲解键盘输入:
键盘数据是如何传给内核的,ctrl+c又是如何变成信号的

键盘与cpu针脚相连,硬件也有代号,比如说10,一旦键盘有数据,cpu就会感受到高低电位,同时cpu会让操作系统知道把高低点位转换成10,同时操作系统有中断向量表,里面存储了硬件的操作方法,找到10代号的数组下标,把键盘数据拷贝到操作系统中,完成输入

同时操作系统会检测输入类型,如果是正常输入abc1234这种就拷贝,如果是组合键ctrl+c ctrl+v这种操作系统会当作信号传给进程
在这里插入图片描述

在这里插入图片描述
操作系统有键盘缓冲区,我们输入的东西如ls在键盘缓冲区显示器也有缓冲区,我们的输入的东西都在显示器缓冲区,所以我们输入的ls和其他进程冲突了,显示器乱码了,但是键盘缓冲区还是正常的

如果键盘缓冲区不向显示器拷贝就不会乱码了,比如lLnux输入密码

🚩信号的产生

信号的产生代码的运行异步
同步:同学出去上厕所,老师就停下讲课
异步:同学出去上厕所,老师继续讲课

1,键盘组合键

ctrl+c:2
ctrl+ \ : 3

2,kill 命令

kill -signo pid

不是所有信号都能被捕捉的

#include <iostream>
#include <unistd.h>
#include <signal.h>
using namespace std;
void myhandler(int signo)
{
    cout<<"get a signal: "<<signo<<endl;
}
int main()
{
    signal(SIGINT,myhandler);
    signal(3,myhandler);
    signal(19,myhandler);
    while(1)
    {
        cout<<"i am a process"<<endl;
        sleep(1);
    }
    return  0;
}

kill -19 pid之后
在这里插入图片描述

for(int i=1;i<=31;i++)
{
    signal(i,myhandler);
}

将每个信号捕捉,然后kill -123456都杀一遍,就知道哪个信号不能被捕捉了

3,系统调用

kill

kill就是系统调用函数
在这里插入图片描述

我们可以自己实现kill了

void usage(string proc)
{
    cout<<"usage:\n\t"<<proc<<" -signo pid"<<endl;
}
int main(int agrc,char* argv[])
{
    if(agrc!=3)
    {
        usage(argv[0]);
        exit(1);
    }
    int signo=stoi(argv[1]);
    int pid=stoi(argv[2]);
    kill(pid,signo);
    return 0;
}

在这里插入图片描述

raise

在这里插入图片描述
raise向该进程发送一个信号,

 int n=5;
    while(n--)
    {
        cout<<"i am a process"<<endl;
    }
    raise(2);

在这里插入图片描述
其实相当于kill(getpid(),2);

abort

在这里插入图片描述
abort:发送6号信号,停止程序

  signal(6,myhandler);
    int n=5;
    while(n--)
    {
        cout<<"i am a process"<<endl;
        abort();
    }

在这里插入图片描述
问题来了,我们捕捉了6号信号,可程序还是停止了
说明abort发送6号信号,捕捉到了,返回myhandler函数后,继续执行abort函数,里面封装了停止程序代码
abort不能用kill来替代了

🚩最后:信号无论怎么产生,都是操作系统发给进程,
因为操作系统是进程的管理者

4,异常

异常也能产生信号

#include <iostream>
using namespace std;
int main()
{
    int a=1;
    int b=0;
    a/=b;
    cout<<"a/=b:"<<a/b<<endl;
    return 0;
}

在这里插入图片描述
在这里插入图片描述
可以看到操作系统发送了8号信号
我们捕捉一下8号信号

void myhandler(int signo)
{
    cout<<"get signo: "<<signo<<endl;
}
int main()
{
    signal(8,myhandler);
    int a=1;
    int b=0;
    a/=b;
    cout<<"a/=b:"<<a/b<<endl;
    return 0;
}

在这里插入图片描述
死循环了 !
为什么呢?
除0错误是cpu硬件产生的异常
cpu有保存上下文的寄存器eip/pc,也有状态寄存器,当除0或溢出时会将0置1,同时,进程切换会带自己的数据,也就是说,如果一个进程崩溃,它不会影响其他进程,此进程状态寄存器是1,其他进程来的时候就是0,但是如果除0时候进程不崩溃,该代码就会一直被调度切换时一直出错误,通知操作系统不断向进程发信号,导致死循环了

在这里插入图片描述

野指针错误呢?
也是cpu产生异常
野指针在页表MMU硬件单元中转换失败,cpuCR2寄存器会记录

在这里插入图片描述
记住,异常只是让我们死的明白,知道怎么错了,不是让我们解决的,大部分情况是直接退出

5,软件产生异常

异常不是只有硬件产生,
当管道文件,关闭读端时,再向里面写数据就会报错
在这里插入图片描述

alarm

在这里插入图片描述
闹钟,设定几秒后终止进程,发送14号信号

int main()
{
    int n=alarm(5);
    while(1)
    {
        cout<<"i am a process:"<<getpid()<<endl;
        sleep(1);
    }
    cout<<n<<endl;
    return 0;
}

在这里插入图片描述
返回值返回上一个闹钟剩余时间,我们设多个闹钟时,重新设置的闹钟返回值是上一个闹钟的剩余时间

void myhandler(int signo)
{
    cout<<"get signo: "<<signo<<endl;
    int n=alarm(5);
    cout<<n<<endl;
}

int main()
{
    signal(14,myhandler);
    int n=alarm(50);
    while(1)
    {
        cout<<"i am a process:"<<getpid()<<endl;
        sleep(1);
    }
    cout<<n<<endl;
    return 0;
}

在这里插入图片描述
提前触发闹钟重新设置,发现剩余时间25s

操作系统中有很多闹钟,先描述再组织
闹钟结构体,肯定有时间戳,获取当前时间,比对设置时间,用最小堆组织起来,如果堆顶没超时则所有闹钟没超时

core核心转储

man 7 signal

在这里插入图片描述

term是终止

进程等待有这副图,我们只用低16位,其中第8位给core dump标志位低7位给信号,次低8位给退出码
在这里插入图片描述

int main()
{
    int n = fork();
    if (n == 0)
    {
        int cnt = 50;
        while (cnt--)
        {
            cout << "i am child getpid():" << getpid() << endl;
            sleep(1);
        }
        exit(2);
    }
    int status;
    pid_t rid = waitpid(-1, &status, 0);
    cout<<"exit code:"<<((status>>8)&0xFF)<<"signal code:"<<(status&0x7F)<<"core dump code:"<<(status>>7&1)<<endl;
    return 0;
}

在这里插入图片描述
当发送8号信号时,core dump没有置1,为什么??

因为云服务器把core关了
我们打开
ulimit -a 查看所有限制,ulimit -c 设置
在这里插入图片描述

在这里插入图片描述
现在置1了,有什么用呢?
首先它一定是运行时出错,core将内存中的数据转存到磁盘当前目录(core文件)中
我们还能看到core文件,
在这里插入图片描述
可以gdb进行调试,能看到出错在哪一行

int main()
{
    int a=1;
    int b=0;
    a/=b;
    cout<<"a/=b:"<<a/b<<endl;
    return 0;
}

在这里插入图片描述

那为什么要禁掉core呢?
因为公司服务器挂掉时,会尝试重启,计算机计算很快,而一点小错误不断重启,磁盘瞬间被core文件填满,到时候操作系统直接挂掉了

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐