进程信号(Signal) 是一种异步的进程间通信机制,用于通知进程发生了某种事件。
异步事件:不会阻塞当前进程,而是在某个条件满足后触发,并由系统或者回调函数处理的事件。
简单来说:我们用现实中一个很常见的事情来比喻异步事件,异步事件就是当我们在手机上点外卖时,我们不需要一直盯着手机上的点外卖的APP,而可以去做其他事情,当外卖送到的时候,手机上会通知,通知之后我们再决定去不去拿,我们可以选择去处理这个外卖,也可以选择当我们将手上的事完成之后再去取外卖。
处理信号的三种方式:默认行为,自定义行为,忽略。
每个信号都有自己的默认行为,我们拿一两个举例:
我们拿9号信号为例,9号信号的默认行为就是杀死进程。 我们可以用man命令查看一张表,可以查看每个信号的默认行为:
man 7 signal
这里我截取的了部分图片。
可以查看每个进程的默认行为。
在了解自定义行为之前,我们需要先了解一个系统调用:
signal可以用来捕捉信号。
第二个参数是函数指针,需要我们自己写一个函数传递进来,signal可以用来捕捉信号,然后改变信号的默认行为。 我们用代码来详细说明一下: 前提:首先我们提出一个结论,我们常用的ctrl+c,其实最后会转化为2号信号,系统识别到2号信号后,会将进程杀死,注意:这里杀死进程,只能杀死前台进程,不能杀后台进程,我们先验证,然后写代码
如何将进程变为后台进程:
./signal &
可以看到ctrl+c
是结束不了后台进程的,智能用kill -9来杀死这个进程。
#include <iostream>
#include <signal.h>
using namespace std;
void headler(int signo)
{
cout<<"get a signal"<<signo<<endl;
}
int main()
{
signal(2,headler);
while(true)
{
cout<<"hello world"<<endl;
sleep(2);
}
return 0;
}
用上面代码来验证ctrl+c是否是2号信号,并未验证signal函数捕捉信号。
可以看见我们ctrl+c的时候,进程不会退出,而是转而输出我们自定义行为的内容。
除了上面讲到的ctrl+c表示终止进程,还有组合键也可以终止进程,并且这个组合键转化的信号不是2号信号:
ctrl+\
这个组合键最后会被转化为3号信号,我们来验证一下:
可以看到输出的是3号信号。
假定我们将所有信号捕捉了之后会怎么样呢?
可以看见,无论如何都杀不死进程,那这样这个进程是不是就无法无天了?
9号信号还是可以杀死进程,由于操作系统早已料到这个结果,所以操作系统设置了9号信号是无法捕捉的。 上面讲的都是键盘产生的信号,其实还有系统调用产生信号。
kill可以向指定进程发送指定的信号。
#include <iostream>
#include <signal.h>
#include <sys/types.h>
using namespace std;
void headler(int signo)
{
cout<<"get a signal "<<signo<<endl;
}
int main()
{
signal(2,headler);
kill(getpid(),2);
while(true){}
return 0;
}
我们捕捉2号信号,可以看见确实可以向指定进程发送信号。 还有两个两个函数可以发送信号,一个是:raise,一个是abort raise表示无论谁调用raise都会给自己发送信号 abort表示无论谁调用进程都会给自己发送信号终止自己进程。
没错,kill既是指令也是系统调用,kill也可以产生信号
我们将一个例子,我们之前学的管道,当读端关闭之后,写端继续往管道中写入是非法的,所以系统就会给进进程发送信号将写端关闭。 这里我们讲一个系统调用:
这个系统调用是定时器函数,我们设置一个闹钟,当闹钟到的时候,进程会直接终止,这里我们利用signal,改变14号信号的行为:
#include <iostream>
#include <signal.h>
#include <sys/types.h>
using namespace std;
void headler(int signo)
{
cout<<"get a signal "<<signo<<endl;
}
int main()
{
signal(14,headler);
alarm(1);
while(true){}
return 0;
}
可以看见我们通过一个闹钟函数捕捉到了信号,也就是内部软件可以直接产生信号。
#include <iostream>
#include <signal.h>
#include <sys/types.h>
using namespace std;
int main()
{
int a = 1/0;
return 0;
}
可以看见异常也可以产生信号,我们来查一下信号是多少号
可以看见/0产生的8号信号。
进程信号(Signal)是 Linux 中一种重要的 进程间通信 和 异常处理 机制,能够异步通知进程发生特定事件。信号可以来自 用户输入(如 Ctrl+C
)、进程间通信(kill()
)、内核事件(如访问非法地址触发 SIGSEGV
)或 定时器(SIGALRM
)。
在实际编程中,我们可以通过 捕获(sigaction()
)、忽略(SIG_IGN
)、阻塞(sigprocmask()
) 等方式处理信号,以控制进程的行为。例如,使用 SIGCHLD
管理子进程,或使用 SIGUSR1
进行进程间通信。
然而,信号的 异步性 可能导致竞态条件,尤其是在信号处理函数中执行非异步安全操作时。因此,在设计信号处理逻辑时,应该避免使用不可重入函数,并合理利用信号屏蔽集来确保同步执行。
掌握进程信号的机制,不仅有助于编写更健壮的 Linux 应用程序,还能在 系统编程、进程控制、故障诊断 等方面提供强有力的工具。希望本文能帮助你更好地理解 Linux 进程信号的原理及应用!