在Linux系统中,信号(Signal)是操作系统用来通知进程发生某些事件的一种机制。信号是一种软件中断机制,可以被进程用来响应特定的事件,如终止进程、暂停进程、重新加载配置等。信号机制是Unix及其衍生系统的核心功能之一
生活中的信号也可以理解为一种通过特定方式传递信息、指令或警告的方式。在日常生活中,信号无处不在,帮助我们理解周围环境并做出反应。以下是一些常见的生活中的信号:
交通灯:交通信号灯(红灯、绿灯、黄灯)是最常见的生活信号之一。通过颜色的变化来指示司机和行人是“停车”、“前进”还是“等待”。
语言表达:人们通过语言交流时,语气、词汇、语法等本身就是信号。例如,愤怒的语气、温柔的话语或请求,都传递了不同的情感或意图。
手机通知:手机的振动或铃声是用来提示有来电、短信、邮件等通知的信号。
天气变化:天气的变化可以是自然界给我们发出的信号。例如,乌云密布通常预示着即将下雨,气温变化可能暗示季节的变化。
Linux
中常见的信号外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
信号是操作系统用来与进程交互的一种方式,通过信号,操作系统能够控制进程的执行流,处理错误或执行特定任务。
kill -l
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
进程可以根据不同需求对信号进行以下三种处理:
忽略信号:进程可以选择忽略某个信号。例如,使用 signal(SIGINT, SIG_IGN)
忽略 SIGINT
信号,进程将不会因按下 Ctrl+C 而被终止。
自定义处理信号:进程可以为信号指定一个自定义的信号处理函数。进程接收到信号时,操作系统会调用该函数来处理信号。例如,可以通过 signal(SIGTERM, my_handler)
设置当进程接收到 SIGTERM
信号时,调用 my_handler()
函数进行处理。
默认处理:如果进程没有特别指定对某个信号的处理方式,操作系统会采用默认行为。例如,默认情况下 SIGINT
信号会终止进程,SIGSEGV
会让进程崩溃。
sighandler_t signal(int signum, sighandler_t handler);
typedef void (*sighandler_t)(int);//函数指针类型 参数是int 返回值是void
参数说明:
signum:要捕获的信号编号。它是一个整数值,表示特定的信号。例如,SIGINT
用来捕获中断信号(Ctrl+C),SIGTERM
用来请求终止进程等。
handler:信号处理函数。当进程接收到指定的信号时,操作系统会调用这个函数。这个函数应该符合 sighandler_t
类型(通常是 void handler(int sig)
类型的函数)。
Ctrl + C
: 本质是2
号信号。Ctrl + Z
: 本质是3
号信号。#include
#include
#include
#include
#include
// 信号处理函数
void myheander(int signo)
{
std::cout << "signo: " << signo << std::endl; // 输出捕获到的信号编号
sleep(3); // 模拟处理信号的延时操作
exit(1); // 退出程序,返回1表示异常终止
}
int main()
{
while(true) // 无限循环,模拟程序持续运行
{
signal(SIGINT, myheander); // 设置 SIGINT 信号的处理函数为 myheander
std::cout << "The process is running ... Pid: " << getpid() << std::endl; // 输出进程的PID
sleep(1); // 每秒输出一次当前的进程信息
}
return 0; // 退出程序,理论上不会执行到这行代码
}
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
分析:
"The process is running ..."
和对应的进程ID(例如,Pid: 26956
)。当用户按下 Ctrl+C
(产生 SIGINT
信号)时,程序捕获到信号并打印 "signo: 2"
。kill -2 27041
命令向进程ID为 27041
的进程发送 SIGINT
信号。
kill
命令用于向进程发送信号。-2
是 SIGINT
信号的数字表示(等同于按 Ctrl+C
),27041
是目标进程的进程ID。同理:Ctrl + Z
一样子可以得到证明。
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
kill
在终端杀死进程kill
杀死进程kill
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
int kill(pid_t pid , int sig);
参数:
pid
:目标进程的PID(进程ID)。可以指定一个特定进程,或者发送信号给进程组或所有进程:
pid
是一个正数,表示发送信号给 PID 对应的进程。pid
是 0,表示向与当前进程同属一组的所有进程发送信号。pid
是 -1,表示向所有进程发送信号。sig
:要发送的信号。可以使用预定义的信号常量,如 SIGINT
、SIGKILL
等
#include
#include
#include
#include
#include
int main()
{
int cnt = 5; // 定义一个计数器,控制循环次数
while(cnt--) // 循环5次
{
signal(SIGQUIT, myheander); // 设置 SIGQUIT 信号的处理函数为 myheander
std::cout << "The process is running ... Pid: " << getpid() << std::endl; // 输出当前进程的PID
sleep(1); // 每秒输出一次当前进程的信息
}
kill(getpid(), SIGKILL); // 发送 SIGKILL 信号终止当前进程
return 0; // 程序结束
}
信号处理
SIGQUIT
信号时,调用 myheander
函数,打印信号编号并退出程序。循环:
强制退出:
kill(getpid(), SIGKILL)
发送 SIGKILL
信号来强制结束当前进程。外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
通过9号信号直接杀死进程。
kill
命令用于向进程发送信号。-2
是 SIGINT
信号的数字表示(等同于按 Ctrl+C
),27041
是目标进程的进程ID。
raise
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
sig
:指定要发送的信号,可以是任何有效的信号常量#include
#include
#include
#include
#include
int main()
{
int cnt = 5; // 定义一个计数器,循环5次
while(cnt--) // 每次循环减少计数器
{
signal(SIGQUIT, myheander); // 设置 SIGQUIT 信号的处理函数为 myheander
std::cout << "The process is running ... Pid: " << getpid() << std::endl; // 输出当前进程的PID
sleep(1); // 每秒输出一次进程信息
}
raise(SIGKILL); // 发送 SIGKILL 信号,强制终止当前进程
return 0; // 程序结束(实际上不会执行到这里,因为进程会在发送 SIGKILL 后被终止)
}
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
kill
是一样的。raise
:用于在进程内部模拟或触发信号,通常用于程序自身的信号处理或测试。raise
向调用它的进程发送信号,并由该进程的信号处理函数处理该信号。kill
:用于进程间的通信,可以用于发送信号给任何进程或进程组。kill
常用于终止进程、暂停进程等操作。abort
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
#include
#include
#include
#include
#include
int main()
{
int cnt = 5; // 定义计数器,循环执行5次
while(cnt--) // 计数器递减,每次循环执行
{
signal(SIGQUIT, myheander); // 设置 SIGQUIT 信号的处理函数为 myheander
std::cout << "The process is running ... Pid: " << getpid() << std::endl; // 输出当前进程的PID
sleep(1); // 每隔1秒输出一次进程状态
}
abort(); // 强制终止当前进程,并生成核心转储(如果启用)
return 0; // 这行代码不会执行,因为进程已经在 abort() 后终止
}
abort()
:
abort()
被调用时,程序会立即中止,并且通常会生成一个核心转储(如果启用了核心转储)。abort()
会发送 SIGABRT
信号,导致进程异常终止,不会进行正常的清理操作。外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
验证6号信号:
#include
#include
#include
#include
#include
void myheander(int signo)
{
std::cout << "SIGABRT----"<<"signo: " << signo <<std::endl;
exit(1);
}
int main()
{
int cnt = 5;
signal(SIGABRT,myheander);
while(cnt--)
{
signal(SIGQUIT, myheander);
std::cout << "The process is running ... Pid: " << getpid() <<std::endl;
sleep(1);
}
abort();
return 0;
}
执行:
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
0
错误#inclde<iostream>
int main()
{
int x = 10;
int y = 0;
int c = x / y;
std::cout << "Calculation completed"<<std::endl;
return 0;
}
触发原理:
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
#include
#include
#include
#include
#include
int main()
{
int *p = NULL;
*p = 10;
std::cout << "completed"<<std::endl;
return 0;
}
操作系统通过内存管理单元(MMU)来监控进程对内存的访问。当进程试图访问未分配、保护或非法的内存地址时,MMU 会触发硬件中断,报告错误给操作系统。
操作系统检测到该非法访问后,通常会通过发出 SIGSEGV
信号终止程序,并可能生成核心转储文件(core dump),以便开发人员进行调试。
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
unsigned int alarm(unsigned int seconds);
参数
seconds
:指定定时器的时间,单位为秒。函数会在 seconds
秒后触发 SIGALRM
信号,通知进程时间到达。返回值
0
。#include
#include
#include
#include
#include
void myheander(int signo)
{
std::cout << "SIGABRT----"<<"signo: " << signo <<std::endl;
int ret =alarm(2);
std:: cout << ret<<std::endl;
}
int main()
{ signal(SIGALRM,myheander);
unsigned int n = 5;
alarm(20);
while(n--)
{
std::cout << "process id running.."<<"Pid: "<<getpid()<<std::endl;
sleep(1);
}
return 0;
}
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
可以看到在在第四秒的时候发送14号信号(alarm
),alarm
的返回值是16.