🎬慕斯主页:修仙—别有洞天
♈️今日夜电波:it's 6pm but I miss u already.—bbbluelee
0:01━━━━━━️💟──────── 3:18
🔄 ◀️ ⏸ ▶️ ☰
💗关注👍点赞🙌收藏您的每一次鼓励都是对我莫大的支持😍
目录
Linux进程信号的概念
引入进程信号
信号的产生
回顾进程的运行
认识进程信号
通过signal替换信号
通过raise给自己发信号
通过abort终止自己
具体信号的产生(下一篇内容)
Linux进程信号的概念
什么叫做信号?Linux进程信号是一个通知机制,用于在进程之间传递信息和控制进程行为。
Linux进程信号是操作系统中的一种通信机制,它允许内核或其他进程向目标进程发送消息,这些消息被称为信号。信号可以由多种事件触发,包括硬件异常、软件事件、用户交互等。例如,当用户按下Ctrl+C时,会产生一个中断信号(SIGINT),通知前台进程终止。
此外,Linux系统中的信号可以分为两类:非可靠信号和可靠信号。非可靠信号是Unix早期版本的信号,编号为1到31。它们不可靠,因为不能被捕获或忽略。可靠信号编号为34到64,它们可以被捕获、阻塞或忽略,提供了更多的控制能力。
综上所述,Linux进程信号是一种灵活的通信机制,它不仅能够响应系统事件,还能够实现进程间的通信和控制。了解信号的概念对于进行Linux编程和管理是非常重要的。
引入进程信号
通过一个例子了解信号:日常生活中,我们经常会碰到红绿灯,当我们看到红绿灯时就算是碰到了一种信号,我们在看到它后会识别它,“即便没看到红绿灯我们也知道对应的灯亮,意味着什么”,当你等红绿灯的时候可能在玩手机或者干什么,但是“红绿灯信号的变化跟你看手机这件事是不冲突的”,此时,在看手机的你注意到变成了红绿灯变成了绿灯,但是,“你没有立刻过红绿灯而是用手机回完信息后才走”。
上面提到的种种行为实际上也对应着进程信号的种种特点:(1)信号没有产生的时候,其实我们就已经知道怎么处理这信号了。(2)信号的到来,我们并不清楚具体是什么时候,信号的到来期间,我正在做工作,所以信号的产生是异步的。(3)信号产生了,我们不一定立刻去处理它,而是我们在合适的时候处理。
信号的产生
回顾进程的运行
进程在运行时,前台进程(命令行操作的时候)只能有一个,后台进程可以有多个。一般情况下,可以通过 Ctrl+c 终止前台进程,Ctrl+z 暂停进程。我们通过进程有没有能力接受用户输入来判断是否为前台进程。
需要注意的是:前台进程不能被暂停(也就是Ctrl+z),被Ctrl+z后会被放到后台再暂停。如果被shell也是进程,OS会自动把shell提到前台或者后台。
通过以下命令来设置对应的前、后台进程:
./exe程序 //运行前台进程
./exe程序 & //运行后台进程
通过以下命令来查看后台进程:
jobs //查看所有后台进程的属性
通过以下命令来改变后台进程:
bg number //让进程在后台跑起来
Ctrl+z 接着 bg number //将进程放到后台运行
fg number //把进程放到前台运行
通过以下命令查看运行的进程:
ps axj //后面通常还会跟管道过滤
认识进程信号
通过kill -l 命令查看所有进程信号:
通常我们将1-31号的信号称为普通信号,34-64称为实时信号,没有0号、32号、33号信号!
需要注意的是:每一个进程都有一张自己的函数指针数组,数组的下标和信号编号强相关。实际上,进程可以通过类似位图的数据结构表示自己是否收到了信号:比特位的位置,决定信号编号;内容,决定是否收到信号。可以简要理解:(后续介绍signal()函数会理解位图以及函数指针数组)
struct task_struct
{//信号位图0000 0010uint32_t sigmap;//...
}
无论信号有多少种产生方式,永远只能让OS向目标进程发送!为什么呢?因为OS是所有进程的管理者!
通过signal替换信号
signal()函数的原型如下:
typedef void (*sighandler_t)(int);sighandler_t signal(int signum, sighandler_t handler);
- 参数:
- signum:这是信号的编号,代表你想要处理的信号类型。可以通过终端输入kill -l来查看系统支持的信号列表。需要注意的是,并非所有信号都可以被捕获,例如SIGKILL和SIGSTOP信号是不能被捕获的。
- handler:这是一个函数指针,指向当信号发生时应该调用的函数。如果不希望改变信号的默认行为,可以将此参数设置为特定的常数。
- 处理方式:
- SIG_IGN:表示忽略该信号,即信号发生时不采取任何行动。
- SIG_DFL:表示采用系统默认的处理方式,通常是终止进程或忽略该信号。
- 自定义处理函数:如果你希望在信号发生时执行特定的操作,可以设置一个自定义的处理函数。这个函数通常需要接受一个整数参数(信号编号)并且返回void。
signal()函数的作用是为指定的信号设置处理程序。当进程接收到指定的信号时,会调用相应的处理程序。如果之前已经为该信号设置了处理程序,signal()函数会返回之前设置的处理程序指针;如果没有设置过,将返回SIG_DFL,表示使用默认的信号处理方式。需要注意的是:9号信号不能自定义处理函数。(因为定义了也不起作用)
如下为一个替换的例子:
#include <iostream>
#include <unistd.h>
#include <signal.h>void handler(int signo)
{std::cout<<"获得一个:NO."<<signo<<"信号"<<std::endl;
}int main()
{signal(2,handler);while(true){std::cout << "i am running~,pid:" <<getpid()<< std::endl;sleep(1);}return 0;
}
如下,我们通过改变2号信号要调用的函数,从而改变了原来2号信号终止进程的功能:
接着再看一个对比例子,左边为替换了2号信号调用的函数的进程,右边为正常的进程。可以注意到,当我们两个进程同时跑起来的时候,左边是不能用Ctrl+c终止的,右边确可以,这说明了修改信号只针对于某个进程,也就是说:我们的进程中每一个进程都有一张自己的函数指针数组,数组的下标和信号编号强相关,其中我们可以通过位图来映射这样的数组。
通过raise给自己发信号
raise
函数用于向当前进程发送指定的信号。其函数原型为:
#include<signal.h>
int raise(int sig);
该函数接受一个参数sig
,它是要发送的信号的编号。如果函数执行成功,返回值为0;如果失败,则返回非0值。以下是关于raise
函数的一些详细解释:
- 功能:
raise
函数的主要作用是允许一个进程给自己发送一个信号。这在某些情况下是有用的,比如当进程需要通知自己发生了某个事件或需要改变自身的行为时。- 等价关系:
raise(sig)
的功能等同于kill(getpid(), sig)
。换句话说,raise
函数相当于是kill
函数的一个特殊形式,其中信号发送者(发送进程)和信号接收者(目标进程)是同一个进程。- 错误处理:如果
raise
函数不能发送信号(例如,由于权限问题或无效的信号编号),它将返回一个非零错误代码。这与kill
函数的错误处理行为类似。- 信号处理:一旦
raise
函数成功发送了信号,接收进程(即发送进程本身)必须对该信号进行处理。信号的处理方式取决于进程预先设置的处理例程,或者如果没有设置,那么将采用默认的信号处理动作。- 使用场景:
raise
函数通常在需要自我通知或自我中断的场景中使用,比如在多线程程序中,一个线程可能需要通知另一个线程发生了某个事件而不需要其他进程介入。
如下示例:
#include <iostream>
#include <unistd.h>
#include<signal.h>void handler(int signo)
{std::cout<<"获得一个:NO."<<signo<<"信号"<<std::endl;
}int main()
{signal(2,handler);while(true){std::cout << "i am running~,pid:" <<getpid()<< std::endl;sleep(1);raise(2);}return 0;
}
通过abort终止自己
在Linux系统中,abort
函数用于异常终止一个进程。具体来说,abort
函数的作用如下:
函数原型:
#include <stdlib.h>
void abort(void);
当调用该函数时,它会向当前进程发送SIGABRT
信号,导致进程异常终止。
- 信号处理:
SIGABRT
信号是一个由系统内核发出的信号,它通常用于表示程序出错或需要立即终止的情况。abort
函数的实现通常是先取消对SIGABRT
信号的阻塞,然后使用raise
函数(或等效的机制)向当前进程发送该信号。- POSIX规范:根据POSIX标准,调用
abort
函数后,进程必须被杀死。这意味着除非在信号处理例程中捕获并处理了SIGABRT
信号,否则进程将不可避免地被终止。- 程序示例:在实际编程中,当检测到严重错误或无法恢复的情况时,可以使用
abort
函数来立即停止程序运行。例如,如果内存分配失败或者程序配置错误,调用abort
可以快速结束程序,避免进一步的错误操作。
综上所述,
abort
函数是Linux中用于异常终止进程的一个重要函数,它通过发送SIGABRT
信号来实现这一目的。在应用程序开发中,合理使用abort
函数可以帮助开发者处理不可预见的错误情况,确保程序的稳定性和安全性。
如下示例:
#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>void handler(int signo)
{std::cout<<"获得一个:NO."<<signo<<"信号"<<std::endl;
}int main()
{signal(SIGABRT,handler);abort();while(true){std::cout << "i am running~,pid:" <<getpid()<< std::endl;sleep(1);}return 0;
}
具体信号的产生(下一篇内容)
通过终端按键产生信号
调用系统函数向进程发信号
由软件条件产生信号
硬件异常产生信号
感谢你耐心的看到这里ღ( ´・ᴗ・` )比心,如有哪里有错误请踢一脚作者o(╥﹏╥)o!
给个三连再走嘛~