✅<1>主页::我的代码爱吃辣
<2>知识讲解:Linux——进程等待
☂️<3>开发环境:Centos7
<4>前言:生活中处处有信号,linux中也有很多信号,OS使用来通知进程,控制进程,更好的管理进程。
目录
一.生活中的信号
二.技术应用角度的信号
三.信号的捕捉
四.信号的产生
1. 通过终端按键产生信号
2. 调用系统函数向进程发信号
3.由软件条件产生信号
4. 硬件异常产生信号
五.核心转储
测试代码:
#include
#include
int main()
{
while (1)
{
printf("I am a process, I am waiting signal!\n");
sleep(1);
}
}
测试结果:
注意:
查看信号列表:
kill -l
当我们按下Ctrl + C 前台进程会收到 2 号信号。那我们如何验证进程收到了2号信号呢?
介绍一个系统调用:
头文件: #include
typedef void (*sighandler_t)(int);
接口定义:sighandler_t signal(int signum, sighandler_t handler)。
作用:捕捉信号signnum,将signum的默认动作修改为 hander。
参数:1.signum信号编号 2.handler回调的方法。
测试代码:
#include
#include
#include
#include
using namespace std;
void handle(int signum)
{
cout << "I get a signal:" << signum << endl;
}
int main()
{
signal(2, handle);
while (1)
{
printf("I am a process, I am waiting signal!\n");
sleep(1);
}
}
测试结果:
在上述的代码中2号信号的默认动作是终止进程,当我们对2好信号进行捕捉,并且将2号信号的默认动作修改为我们自定义的一个函数。因此我们再次Ctrl + C,进程收到2号信号就不会终止,而是执行我们设计的函数。我们继续可以使用Ctrl + \ 终止进程。
中断按键,就是我们的键盘,当我们按下Ctrl + C 时OS会向前台进程发送2号信号。还有Ctrl + \ 可以向前台进程发送。
#include
#include
int kill(pid_t pid, int signo);向指定进程发指定信号。
int raise(int signo);向调用进程发送指定信号。
这两个函数都是成功返回0,错误返回-1。
#include
void abort(void);
就像exit函数一样,abort函数总是会成功的,所以没有返回值,给进程发送6号信号,6号直接终止进程。
kill-测试代码:
#include
#include
#include
#include
#include
using namespace std;
void handle(int signum)
{
cout << "I am child process,my PID :" << getpid() << ",I get a signal:" << signum << endl;
// 进程退出
exit(0);
}
int main()
{
// 创建子进程
pid_t pid = fork();
if (pid == 0)
{
// 让子进程捕捉2号信号
signal(2, handle);
while (1)
{
printf("I am child process, my PID :%d,I am waiting signal!\n", getpid());
sleep(1);
}
}
int count = 5;
while (count)
{
printf("%d秒之后我将给%d号进程发2号信号\n", count--, pid);
sleep(1);
}
// 给子进程发送2号信号
kill(pid, 2);
sleep(1);
return 0;
}
测试结果:
raise-测试代码
#include
#include
#include
#include
#include
using namespace std;
void handle(int signum)
{
cout << "I am process,I get a signal:" << signum << endl;
// 进程退出
exit(0);
}
int main()
{
// 让进程捕捉2号信号
signal(2, handle);
int count = 5;
while (count)
{
printf("%d秒之后我将给自己发2号信号\n", count--);
sleep(1);
}
// 给子进程发送2号信号
raise(2);
sleep(1);
return 0;
}
测试结果:
abort-测试代码:
代码:
#include
#include
#include
#include
#include
#include
using namespace std;
void handle(int signum)
{
cout << "I am process,I get a signal:" << signum << endl;
// 进程退出
exit(0);
}
int main()
{
// 让进程捕捉6号信号
signal(6, handle);
int count = 5;
while (count)
{
printf("%d秒之后我将给自己发6号信号\n", count--);
sleep(1);
}
// 给子进程发送6号信号
abort();
sleep(1);
return 0;
}
测试结果:
SIGPIPE是一种由软件条件产生的信号,在“管道”中已经介绍过了。
#include
unsigned int alarm(unsigned int seconds);
调用alarm函数可以设定一个闹钟,也就是告诉内核在seconds秒之后给当前进程发SIGALRM信号, 该信号的默认处理动作是终止当前进程。
测试代码:
#include
#include
#include
#include
#include
#include
using namespace std;
int main()
{
// 设置闹钟
alarm(5);
int count = 5;
while (count)
{
printf("%d秒之后闹钟响,进程终止\n", count--);
sleep(1);
}
sleep(15);
return 0;
}
测试结果:
硬件异常被硬件以某种方式被硬件检测到并通知内核,然后内核向当前进程发送适当的信号。例如当前进程执行了除以0的指令,CPU的运算单元会产生异常,内核将这个异常解释 为SIGFPE信号发送给进程。再比如当前进程访问了非法内存地址,,MMU会产生异常,内核将这个异常解释为SIGSEGV信号发送给进程。
除0异常模拟:
测试代码:
int main()
{
cout << "begin" << endl;
cout << "begin" << endl;
int num = 10;
int tmp = num / 0;
cout << "end" << endl;
cout << "end" << endl;
return 0;
}
说明:在运行到除0的地方,进程直接终止了,原因是进程收到了8号信号。
野指针异常模拟:
测试代码:
int main()
{
cout << "begin" << endl;
cout << "begin" << endl;
int *point = nullptr;
*point = 100;
cout << "end" << endl;
cout << "end" << endl;
return 0;
}
测试结果:
由此可以确认,我们在C/C++当中除零,内存越界等异常,在系统层面上,是被当成信号处理的。
总结:
我们在进程控制时,讲到进程等待,我们与遇到过这个概念:
首先解释什么是核心转储(Core Dump)。当一个进程要异常终止时,可以选择把进程的用户空间内存数据全部 保存到磁盘上,文件名通常是core,这叫做Core Dump。进程异常终止通常是因为有Bug,比如非法内存访问导致段错误,事后可以用调试器检查core文件以查清错误原因,这叫做Post-mortem Debug(事后调试)。一个进程允许产生多大的core文件取决于进程的Resource Limit(这个信息保存 在PCB中)。默认是不允许产生core文件的,因为core文件中可能包含用户密码等敏感信息,不安全。在开发调试阶段可以用ulimit命令改变这个限制,允许产生core文件。 首先用ulimit命令改变Shell进程的Resource Limit,允许core文件最大为1024K: $ ulimit -c1024
使用命令$:ulimit -a 可以查看进程的资源分配信息,其中也包括core 文件大小。
使用命令:ulimit -c 10240,给core dump添加大小。
测试代码:
int main()
{
pid_t pid = fork();
if (pid == 0)
{
cout << "begin" << endl;
cout << "begin" << endl;
int *point = nullptr;
*point = 100;
cout << "end" << endl;
cout << "end" << endl;
}
int status = 0;
waitpid(pid, &status, 0);
cout << "core dump:" << ((status >> 7) & 1) << endl;
cout << "signal:" << (status & 0x3f) << endl;
return 0;
}
测试结果:
说明:
查看核心转储文件: