欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 健康 > 养生 > Linux 信号

Linux 信号

2025/5/6 13:28:25 来源:https://blog.csdn.net/2401_83238095/article/details/147670292  浏览:    关键词:Linux 信号

一、生活中的信号

1.1、生活中的信号从产生到结束过程

例:

①、外卖电话响了(信号产生)-> 我接了电话并告诉外卖员说先放到楼下的架子上(识别到这个信号,并记住,保存到我的脑海里面) -> 我在打游戏我继续推高地 (时间窗口,我要做更重要的事) -> 游戏结束了,我下楼取外卖(信号的处理);

②、200米冲刺比赛发令员举起信号枪说:“预备!”(信号产生)->我做起的预备跑的动作动作(我识别到预备的信号并立即对其进行处理)-> 发令员喊:“跑!”并开枪(信号产生)->我一瞬间飞奔了出去(识别到枪声信号,并立即对其进行处理);

二、进程中的信号

2.1、进程必须识别并能够处理信号--- 即便信号没有产生,也要具备处理信号的能力 --- 信号的处理能力属于内置进程的一部分;
2.2、当进程真的收到一个具体的信号时,进程可能立即对其进行处理(听到枪声立马飞奔),进程也可能不会立即处理这个信号,在合适的时候再处理(在打游戏,等我打完再去取外卖);
2.3、一个进程当信号产生到信号开始被处理,一定会有一个时间窗口,进程具有临时保存哪些信号已发生了的能力;

三、前台进程与后台进程

3.1、什么是前台进程?什么是后台进程?

在linux中,一次登陆,一个终端一般会配上一个bash,每一个登陆只允许一个进程是前台进程,可以运行多个进程是后台进程,谁是前台谁是后台取决于谁来获取键盘输入,能获取的就是前台进程;

3.2 、 linux中正在运行的程序从键盘输入 ctrl+c为什么能够杀掉进程?
#include <iostream>
#include <unistd.h>
using namespace std;
int main()
{while(true)
{cout<<"i am a process!!!"<<endl;sleep(1);
}return 0;
}

运行一会后键盘输入ctrl+c:

在bash命令行运行程序的后面加上&后再按ctrl + c:

我们发现ctrl+c杀不掉这个进程!!

原因:ctrl+c本质是被进程解释为收到了信号,这是一个2号信号,这个信号的默认动作是终止自己;从键盘输入ctrl+c是发送给前台进程的,当进程运行前在后面+&后这个进程会变成后台进程,所以ctrl+c,这个后台进程收不到信号,只能用kill -9来杀掉:

四、键盘的数据是如何输入给内核的?ctrl+c又是如何变成信号的?OS怎么知道键盘上有数据了?

①首先键盘录入数据,准备就绪后向cpu发送硬件中断信号;
②cup通过cup针角进行充放电,这个过程寄存器记录下数据;
③cup让OS去中断向量表里查找到对应的方法;
④OS找到读取方法后从键盘中把数据拷贝到内核缓冲区;
⑤再从内核缓冲区把数据拷贝到用户缓冲区,用户获取到键盘数据;
⑥OS在把数据拷贝到内核缓冲区前对键盘里的数据进行解析,如果录入的是像ctrl + c这样的组合键,不会往缓冲区里拷贝转而是向对应的进程发送信号,ctrl+c对应的是2号信号,把2号信号发送给对应的进程,终止掉程序;

五、信号的产生及处理

5.1、信号的处理方法

信号处理的三种方法:①默认动作(例如人看到红灯信号的默认动作是站着不动等待);②忽略(例如人看到红灯后忽略,后果自己承担);③自定义动作(例如人看到红灯信号后在原地跳舞);

5.2、信号的捕获及自定义处理

介绍一个接口:

#include <signal.h>
typedef void(*sighandler_t)(int);
sighandler_t signal( int signum, sighandler_t handler);
//1. 这个接口的作用是获取信号,并且修改信号的处理方式
//2. sighandler_t 这是一个函数指针类型,函数的返回值为void,函数参数类型为int
//3. sigbum为接收到的信号数字,handeler : 你需要传入一个已经定义好的方法

注意:①:只要设置一次,往后都有效;②如果进程没有接收到信号,自定义的处理方法不会被调用,只有产生信号才会被调用;

试一试:

前面我们说ctrl+c发送的是2号信号,我们编写一个程序并调用这个接口去获取这个信号,看它是不是2号,获取后把信号数打印出来:

#include <iostream>
#include <unistd.h>
#include <signal.h>
using namespace std;
typedef void(*sighandler)(int);//定义函数指针类型
void Handler(int signum)//自定义信号的处理方法
{cout<<signum<<endl;
}
int main()
{signal(2,Handler);//捕获2号信号,并调用自定义处理方法while(true)
{cout<<"i am a process!!!"<<endl;sleep(1);
}return 0;
}

编译运行程序后按ctrl +c :

我按了两次ctrl+c 可看到每次后面都跟着数字2,说明chrl+c的信号数字确是是2!而且我们发现按完ctrl+c后,程序没有终止而是继续执行,原因很简单因为我们把对信号的处理方法改变了,改成向屏幕打印数字2而不是终止程序,这样就实现了对信号的自定义处理!

5.3、不是所有信号都会被捕获

设想:如果我们把所有信号都捕获了并且把它处理方法定义为循环打印,那么岂不是这个程序一直在运行无法被杀死掉?

操作系统是不会允许这样的事情发生的,我们来测试一下究竟哪些信号不会被捕获:

代码改一下:

#include <iostream>
#include <unistd.h>
#include <signal.h>
using namespace std;
typedef void(*sighandler)(int);//定义函数指针类型
void Handler(int signum)//自定义信号的处理方法
{cout<<signum<<endl;
}
int main()
{for(int i=1;i<32;i++){signal(i,Handler);//捕获1~31号信号,并调用自定义处理方法}//signal(2,Handler);//捕获2号信号,并调用自定义处理方法while(true)
{cout<<"i am a process!!!"<<endl;sleep(1);
}return 0;
}

编译运行后我们用kill命令向这个进程发送1~31号信号,看看是不是全都被捕获:

    

                           ...............

最终测试发现:1~31号信号,只有9、19号信号是不会被进程捕获的!用kill -l 命令查看信号列表,我们看到9跟19号信号对应的是 :杀掉进程、停止进程!!1~31号信号(只演示普通信号)终除了这两个之外其他都被捕获!!

上面程序运行我们发现,信号的产生和我们自己的代码的运行是异步的,这种进程间事件异步通知的方式是软中断;

5.4、信号的产生

I 以上我们知道了信号产生的两种方法:

1、通过从键盘中录入“ctrl + c”等组合键,被操作系统解析为信号,并发送给对应的进程;

2、通过命令( kill -信号数字 进程pid),产生信号发送给进程;

II 第三种信号产生的方法:

3、系统调用:

介绍一个接口:

#include <sys/systypes.h>
#include <signal.h>
int kill( pid_t pid, int sig);
//1. 这是一个向进程发送信号的接口
//2. pid : 进程的id   sig : 信号数字
//3. int 返回值如果失败返回-1;

利用这个接口模拟实现一下kill 命令:

//mykill.cc
#include <iostream>
#include <sys/types.h>
#include <signal.h>
#include <stdio.h>
#include <string>
using namespace std;
int main(int argc,char*argv[])
{//mykill signum pidif(argc!=3){printf("please enter@ %s signum pid\n",argv[0]);}pid_t pid=stoi(argv[2]);int signum=stoi(argv[1]);int n=kill(pid,signum);return 0;
}

①:我们执行myprocess程序,循环向屏幕打印:

②编译好自己实现的mykill,运行用 9 号信号杀死掉26713这个程序:

成功杀掉进程!!

5.5、介绍两个接口
#include<signal.h>
int raise(int sig) ;
//1.这是一个给自己发送信号的接口
//2. sig -> 信号数字
#include <stdlib.h>
void abort(void);
//1. 这是一个终止自己的接口

我们调用这两个接口并调用捕获信号的接口,看看这两货到底发送了什么信号:

第一个接口形参是多少就发送几号的信号给自己;

然后我把它改成这样:

再编译运行后结果一样,所以raise()函数也可以写成kill(getpid(), signum);

测试第二个接口:

捕获不到信号,直接终止了程序!

查看信号列表发现是6号信号:

好了,今天分享到这里,如果对你有所帮助记得点赞收藏+关注哦!!谢谢!!!

咱下期见!!!

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com