进程信号
前不久刚介绍过信号量,本文将来介绍进程信号,虽然都有信号,但是二者毫无关系。
📘生活中的信号
信号在我们的生活中并不陌生;闹钟,红绿灯,电话等等,都是生活中常见的信号。
而我们在看见红绿灯时,知道红灯停绿灯行,听见闹钟知道定闹钟是为了提醒我们处理事务…也就是说我们人类能够识别信号。
而相对的,进程也能够识别信号,如果把人看作进程的话,那么操作系统就是人所在的社会,操作系统发出信号,进程识别后会进行处理。
📒Linux中的信号
我们可以通过kill -l
的命令来查看Linux中的信号。
其中1 ~ 31号信号是普通信号,34 ~ 64号信号为实时信号,实时信号执行的优先级更高。
🧱信号的产生
1.通过键盘按键产生
我们之前会通过键盘输入ctrl + c来终止程序,而实际上这就是一个信号,并且产生的是2号信号SIGINT。具体来说是键盘输入产生了一个硬件中断,被操作系统OS获取解释成信号,并发送给目标前台进程,前台进程收到信号,执行该信号的默认处理操作,即推出进程。
[lyl@VM-4-15-centos Signal]$ cat signal.cc
#include <iostream>
#include <sys/types.h>
#include <unistd.h>
int main()
{
while(true)
{
std:: cout << "I am proc:" << getpid() << std::endl;
sleep(1);
}
}
[lyl@VM-4-15-centos Signal]$ ./mysignal
I am proc:9005
I am proc:9005
I am proc:9005
I am proc:9005
^C
2.通过系统命令(系统函数调用)产生
其实,除了键盘输入产生信号外,还可以通过kill命令发送信号给进程。
【注意】这里需要注意的是ctrl+c只能够终止前台进程,而如果要终止后台进程,需要通过kill命令。在这里,进程可以在执行的任何时候收到信号而终止,也就是说信号相对于进程的控制流程是异步的。
实际上,系统命令就是通过调用系统函数来产生的信号,比如kill命令就是调用的kill函数产生信号,常见的系统函数有:
#include <signal.h>
int kill(pid_t pid, int signo);
int raise(int signo);
这两个函数都是成功返回0,错误返回-1
另外,abort函数使当前进程接收到信号而异常终止:
#include <stdlib.h>
void abort(void);
就像exit函数一样,abort函数总是会成功的,所以没有返回值
3.由硬件条件(进程触发错误)产生
我们知道如果我们在程序中进行除0操作,或者有野指针的时候,程序在运行后是会崩溃的。其实,这里也是进程收到了信号后终止的结果,我们试着通过捕获信号来看看对应的信号都是几号信号,这里用到的是信号的自定义处理操作,后续介绍信号处理时会提到。
#include <iostream>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
void handler(int sig)//自定义捕捉信号
{
std::cout << "catch a sig: " << sig << std::endl;
exit(1);
}
int main()
{
while(true)
{
for(int i = 1; i < 32; i++)
{
signal(i, handler);
}
std:: cout << "I am proc:" << getpid() << std::endl;
int a = 0;
int b = 2;
std::cout << b / a << std::endl;
sleep(5);
}
}
我们实现一个除0的操作,运行结果如下: