【Linux】进程信号的产生(一)

文章目录

🚩预备工作

1,进程必须要能识别信号

2,进程即使没收到信号,也要知道怎么处理这些信号,处理信号能力是内核系统内置功能

3,进程收到信号,可能不会立即处理信号,等到合适时机再处理

4,进程收到信号,到处理信号,必然有时间窗口,也就是进程有保存信号已经发生的能力

🚩处理信号的三种模式:

1,默认

2,忽略

3,自定义

c 复制代码
#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捕捉信号,再自己实现该信号功能,这就是自定义信号

c 复制代码
#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

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

c 复制代码
#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之后

c 复制代码
for(int i=1;i<=31;i++)
{
    signal(i,myhandler);
}

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

3,系统调用

kill

kill就是系统调用函数

我们可以自己实现kill了

c 复制代码
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向该进程发送一个信号,

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

其实相当于kill(getpid(),2);

abort

abort:发送6号信号,停止程序

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

问题来了,我们捕捉了6号信号,可程序还是停止了,

说明abort发送6号信号,捕捉到了,返回myhandler函数后,继续执行abort函数,里面封装了停止程序代码

abort不能用kill来替代了

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

4,异常

异常也能产生信号

c 复制代码
#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号信号

c 复制代码
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号信号

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


返回值:返回上一个闹钟剩余时间,我们设多个闹钟时,重新设置的闹钟返回值是上一个闹钟的剩余时间

c 复制代码
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位给退出码

c 复制代码
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进行调试,能看到出错在哪一行

c 复制代码
int main()
{
    int a=1;
    int b=0;
    a/=b;
    cout<<"a/=b:"<<a/b<<endl;
    return 0;
}

那为什么要禁掉core呢?

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

相关推荐
端平入洛1 天前
auto有时不auto
c++
Rockbean1 天前
用40行代码搭建自己的无服务器OCR
服务器·python·deepseek
蝎子莱莱爱打怪1 天前
Centos7中一键安装K8s集群以及Rancher安装记录
运维·后端·kubernetes
茶杯梦轩1 天前
CompletableFuture 在 项目实战 中 创建异步任务 的核心优势及使用场景
服务器·后端·面试
崔小汤呀2 天前
最全的docker安装笔记,包含CentOS和Ubuntu
linux·后端
何中应2 天前
vi编辑器使用
linux·后端·操作系统
何中应2 天前
Linux进程无法被kill
linux·后端·操作系统
何中应2 天前
rm-rf /命令操作介绍
linux·后端·操作系统
何中应2 天前
Linux常用命令
linux·操作系统
葛立国2 天前
从 / 和 /dev 说起:Linux 文件系统与挂载点一文理清
linux