目录
引言
基础知识(背景)
信号的产生
方法一:通过终端按键产生信号(键盘按键输入)
方式二:通过kill指令
方法3:系统调用
方法四:硬件异常
方法五:软件产生异常
进程结束--信号相关
引言
Linux系统信号的引入,是操作系统对进程进行有效控制和通信的关键组成部分。在多任务操作系统中,进程需要一种方式来响应外部事件或内部条件的变化,而信号提供了一种灵活、高效的解决方案。信号允许一个进程在不中断其正常执行流程的情况下,通知另一个进程或线程某个事件的发生,从而实现进程间的协同和同步。
基础知识(背景)
1. 生活角度的信号
一共62个信号
1-31:普通信号
34-64:实时信号(收到信号必须立即执行)
总结:只允许一个前台,键盘输入是给前台的指令
信号的产生
方法一:通过终端按键产生信号(键盘按键输入)
方式二:通过kill指令
对进程发送kill指令之后,发现进程收到这个指令并且终止。
方法3:系统调用
kill接口
bash中输入几个字符串,argc就是几
#include <cstdlib>
#include <sys/types.h>
#include <signal.h>
#include <string>
#include <iostream>
using namespace std;
void Usage()
{
cout << "Usage:./sig pid signal" << endl;
}
//./sig pid signal
int main(int argc, char* argv[])
{
if (argc != 3)
{
Usage();
exit(1);
}
int pid = stoi(argv[1]);
int signal = stoi(argv[2]);
kill(pid, signal);
return 0;
}
发现可以通过kill调用向进程发送特定的信号。
raise接口
给自己发信号
abort:让自己中止(6号信号)
但是跟直接发6号信号不太一致(kill发送 6 号信号)
三个系统调用/库函数:kill raise abort
方法四:硬件异常
进程退出时的两种方式:1.正常退出:1️⃣正确执行完成2️⃣出现逻辑错误---存在返回状态
2.被信号所杀,异常终止----存在终止信号
当出现野指针与除零错误时,CPU的寄存器会出现异常信息。这个寄存器的信息属于进程的上下文。
对于大多数架构,除以零错误会引发SIGFPE
(浮点异常)信号,其信号编号通常是8
当我们执行程序之后,返现另一个执行流(信号)在疯狂打印收到了8号信号,并且没有休眠。
原因:
1.进程是分执行流的,信号引发的执行流是一个独立的执行流。
2.信号一直被触发?
上下文信息随进程的切换而切换
这个OS会管理CPU,知道状态寄存器出异常(CPU在执行进程时转化成硬件问题)
代码出错---状态寄存器出异常--OS检测---向进程发送信号---处理信号
当进程恢复时,会把上下文恢复,从而一直出现浮点异常SIGFPE。
对于野指针,也会引起这样的异常,只不过区分段错误与浮点异常时,存在不同的寄存器去感知这些异常。
所以出现错误时,发送的信号大部分都会导致进程终止,那为什么还打印信息呢?----为了让用户更好的去处理解决错误。
C++的try---catch异常处理一定是信号处理的封装。只不过这个机制太过于简单与落后,大部分公司都有自己独立的异常处理机制。
方法五:软件产生异常
14号信号:闹钟⏰
alarm函数
参数是闹钟响时的秒数
返回值:提前几秒醒来:参数 - 醒来时间
(
可以设置定时任务
返回值:在设置新的闹钟的时候,可能上次的闹钟可能还有剩余的时间,这个时候返回值就有意义。
#include <iostream>
#include <unistd.h>
#include <signal.h>
using namespace std;
void handler(int sig)
{
cout << "Received signal " << sig << endl;
}
int main()
{
alarm(5);
signal(SIGALRM, handler);
while (1)
{
sleep(1);
}
return 0;
}
为什么进程没有一直打印闹钟呢(信号只发了一次))
这是因为闹钟只设置了一次,这并不是硬件层面的异常导致的信号(信号!=异常)
进程结束--信号相关
进程退出时,waitpid
的第二个参数status
可以获取退出信息,其中低7位表示被信号所杀时的信号信息,次低8位表示的是正常退出时的状态码
对于出异常的进程,大部分都会导致进程终止,但是终止的方式千奇百怪。
这些信号的行为。
Term和core(核心转储相关)是终止
Ign是忽略
Cont是继续
而进程退出当status的倒数第八位就是就是代表着是否存在核心转储。
#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
using namespace std;
int main()
{
pid_t id = fork();
if (id == 0)
{
// child
int cnt = 500;
while (cnt)
{
cout << "i am a child process, pid: " << getpid() << "cnt: " << cnt << endl;
sleep(1);
cnt--;
}
exit(0);
}
// father
int status = 0;
pid_t rid = waitpid(id, &status, 0);
if (rid == id)
{
cout << "child quit info, rid: " << rid
<< " exit code: " << ((status >> 8) & 0xFF)
<< " exit signal: " << (status & 0x7F) << " core dump: "
<< ((status >> 7) & 1) << endl; // ? & (0000 0000 ... 0001)
}
}
为了生产核心转储需要先ulimit -c 10240设置一个非零值。
核心转储文件包含着特定的出错信息。
为什么需要核心转储
运行时错误:什么原因出错,哪行代码出错
-g 用debug发布之后,就可以去gdb调试器调试
核心信息:8号信号 14行出现除零错误
core dump最好关闭:防止打满磁盘