欢迎光临
我们一直在努力

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

文章目录

  • 🚩预备工作
    • 前台进程后台进程
    • 自定义信号
    • 硬件中断
  • 🚩信号的产生
    • 1,键盘组合键
    • 2,kill 命令
    • 3,系统调用
      • kill
      • raise
      • abort
      • 4,异常
    • 5,软件产生异常
      • alarm
      • core核心转储

🚩预备工作

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文件填满,到时候操作系统直接挂掉了

赞(0)
未经允许不得转载:171主机测评 » 【Linux】进程信号的产生(一)
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址