1.Linux信号的基本概念


Linux 进程信号(Signal)是一种软件中断机制,用于通知进程发生了某个事件。

举例说明:

#include 
#include 
int main(){
while(true){
std::cout << "I am a process, I am waiting signal!" << std::endl;sleep(1);
}
}

上面代码大家都知道是一个死循环,当我们运行时,我们按下ctrl+c,程序就会停止。这是因为前台进程因为收到信号,进⽽引起进程退出

每个信号都有⼀个编号和⼀个宏定义名称,这些宏定义可以在signal.h中找到,例如:

编号34以上的是实时信号,本文只讨论编号34以下的信号,不讨论实时信号

这些信号各⾃在什么条件下产⽣,默认的处理动作是什么,在signal(7)中都有详细说明: man 7 signal

信号处理系统调用:

一共有三种

忽略信号:

void handler(int signumber)
{
std::cout << "我是: " << getpid() << ", 我获得了⼀个信号: " << signumber
<< std::endl;
} 
int main()
{
std::cout << "我是进程: " << getpid() << std::endl;
signal(SIGINT/*2*/, SIG_IGN); // 设置忽略信号的宏
while(true){
std::cout << "I am a process, I am waiting signal!" << std::endl;
sleep(1);
}

执⾏该信号的默认处理动作:

signal(SIGINT/*2*/, SIG_DFL);

提供⼀个信号处理函数,要求内核在处理该信号时切换到⽤⼾态执⾏这个处理函数,这种⽅式称为⾃定义捕捉(Catch)⼀个信号。

signal(SIGINT/*2*/, handler);

信号接口signal():

二. 产⽣信号

1.通过终端按键产⽣信号

ctrl+c已经验证

Ctrl+Z(SIGTSTP)可以发送停⽌信号,将当前前台进程挂起到后台等。

Ctrl+\(SIGQUIT)可以发送终⽌信号并⽣成core dump⽂件,⽤于事后调试(后⾯详谈)

2.调⽤系统命令向进程发信号
kill -SIGSEGV pid 向系统标明这个程序产生段错误了

kill 函数可以给⼀个指定的进程发送指定的信号

实现自己的kill命令:
#include 
#include 
#include 
#include 
// mykill -signumber pid
int main(int argc, char *argv[])
{
if(argc != 3)
{
std::cerr << "Usage: " << argv[0] << " -signumber pid" << std::endl;
return 1;
} i
nt number = std::stoi(argv[1]+1); // 去掉-
pid_t pid = std::stoi(argv[2]);
int n = kill(pid, number);
return n;
}

raise:raise 函数可以给当前进程发送指定的信号(⾃⼰给⾃⼰发信号)。

void handler(int signumber){
// 整个代码就只有这⼀处打印
std::cout << "获取了⼀个信号: " << signumber << std::endl;} // mykill -signumber pid
int main(){
signal(2, handler); // 先对2号信号进⾏捕捉
// 每隔1S,⾃⼰给⾃⼰发送2号信号
while(true)
{
sleep(1);
raise(2);
}}

abort 函数使当前进程接收到信号⽽异常终⽌。

void handler(int signumber)
{
// 整个代码就只有这⼀处打印
std::cout << "获取了⼀个信号: " << signumber << std::endl;
} /
/ mykill -signumber pid
int main()
{
signal(SIGABRT, handler);
while(true)
{
sleep(1);
abort();
}
}

由软条件产生:

在操作系统中,信号的软件条件指的是由软件内部状态或特定软件操作触发的信号产⽣机制。这些条件包括但不限于定时器超时(如alarm函数设定的时间到达)、软件异常(如向已关闭的管道写数据产⽣的SIGPIPE信号)等。当这些软件条件满⾜时,操作系统会向相关进程发送相应的信号,以通知进程进⾏相应的处理。简⽽⾔之,软件条件是因操作系统内部或外部软件操作⽽触发的信号产⽣。

alarm 函数 和 SIGALRM 信号。

alarm函数:

调⽤ alarm 函数可以设定⼀个闹钟,也就是告诉内核在 seconds 秒之后给当前进程发SIGALRM 信号,该信号的默认处理动作是终⽌当前进程。这个函数的返回值是0或者是以前设定的闹钟时间还余下的秒数。打个⽐⽅,某⼈要⼩睡⼀觉,设定闹钟为30分钟之后响,20分钟后被⼈吵醒了,还想多睡⼀会⼉,于是重新设定闹钟为15分钟之后响,“以前设定的闹钟时间还余下的时间”就是10分钟。如果seconds值为0,表⽰取消以前设定的闹钟,函数的返回值仍然是以前设定的闹钟时间还余下的秒数

他对io的效率有明显的提升:

看下面两次代码

#include 
#include 
#include 
int main()
{
int count = 0;
alarm(1);
while(true)
{
std::cout << "count : "
<< count << std::endl;
count++;
} r
eturn 0;
}
使用alarm:
#include 
#include 
#include 
int count = 0;
void handler(int signumber)
{
std::cout << "count : " <<
count << std::endl;
exit(0);
} i
nt main()
{
signal(SIGALRM, handler);
alarm(1);
while (true)
{
count++;
} 
return 0;
}

下面是对比数据:

1.count : 107148

2.count : 492333713

硬件异常产⽣信号

硬件异常被硬件以某种⽅式被硬件检测到并通知内核,然后内核向当前进程发送适当的信号。例如当前进程执⾏了除以0的指令, CPU的运算单元会产⽣异常, 内核将这个异常解释为SIGFPE信号发送给进程。再⽐如当前进程访问了⾮法内存地址, MMU会产⽣异常,内核将这个异常解释为SIGSEGV信号发送给进程。

1.除0
2.野指针
我们在C/C++当中除零,内存越界等异常,在系统层⾯上,是被当成信号处理的。

今天的更新就到了,如有错误,欢迎评论区指出!!