Linux信号学习三步走及知识脉络
在Linux系统中,信号(Signal)是一个非常重要的概念,它作为一种异步通知机制,用于通知进程发生了某些事件。信号的本质是软件层次上对中断机制的一种模拟,是进程间通信机制中唯一的异步通信方式。学习Linux信号,可以通过以下三步走,并打通相关的知识脉络。
一、信号的基本概念与分类
1. 信号的定义
信号,又称为软中断信号,是Linux系统响应某些条件而产生的一个事件。它是操作系统向一个进程或者线程发送的一种异步通知,用于通知该进程或线程某种事件已经发生,需要做出相应的处理。信号本质是一种异步通知机制,用户或操作系统通过发送信号来通知进程某些事情已经发生,进程可以进行后续处理。
2. 信号的产生
信号可以由多种方式产生,包括:
- 通过终端按键产生信号:例如,在终端上按Ctrl+C键通常产生中断信号(SIGINT),终止进程。
- 调用系统函数向进程发信号:如
kill()
函数、raise()
函数等。 - 由软件条件产生信号:例如,使用
alarm()
函数设置的闹钟超时后会产生SIGALRM信号。 - 硬件异常产生信号:如除数为0、无效的内存引用等,这些条件通常由硬件检测到,并通知内核,然后内核为发生此错误的进程发送相应的信号。
3. 信号的分类
Linux系统支持多种信号,大致可以分为两大类:非实时信号(也称为不可靠信号)和实时信号(也称为可靠信号)。非实时信号的值范围是1到31,是从UNIX系统中继承下来的;实时信号的值范围是34到64。非实时信号不支持排队,可能会丢失,而实时信号支持排队,可以确保信号按顺序处理。
可以使用kill -l
命令查看系统中支持的所有信号及其编号和名称。每种信号都有其特定的含义和默认处理动作,例如,SIGKILL(信号值为9)的默认处理动作是立即终止进程,且不能被捕捉或忽略。
二、信号的处理与捕捉
1. 信号的处理方式
当进程接收到信号时,可以采取以下三种方式之一来处理信号:
- 忽略信号:大部分信号都可以被忽略,但SIGSTOP和SIGKILL两个信号除外,因为它们是给超级用户提供杀掉或停止任何进程的手段。
- 执行默认处理动作:系统为每种信号都定义了一个默认的处理动作,如终止进程、生成core dump文件等。
- 捕捉信号:进程可以注册一个信号处理函数来捕捉特定的信号,并在接收到该信号时执行相应的处理逻辑。
2. 信号的捕捉
在Linux中,可以通过signal()
函数或sigaction()
函数来设置信号处理函数。signal()
函数是UNIX系统的遗留接口,它相对简单但功能有限;而sigaction()
函数提供了更丰富的功能,是更推荐使用的接口。
sigaction()
函数允许进程为特定的信号指定一个处理函数,并可以修改信号的行为,如阻塞信号、查询信号等。其结构体sigaction
包含了信号的处理函数、信号屏蔽字等信息。
示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
void sig_handler(int signum) {
printf("Caught signal %d\n", signum);
// 清理资源、关闭文件等操作
exit(signum);
}
int main() {
struct sigaction act;
act.sa_handler = sig_handler;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGINT, &act, NULL);
while(1) {
printf("Looping...\n");
sleep(1);
}
return 0;
}
在这个例子中,我们为SIGINT信号设置了处理函数sig_handler
,当进程接收到SIGINT信号时,会执行sig_handler
函数,并打印出接收到的信号编号,然后退出程序。
3. 信号的阻塞与解除
进程可以选择阻塞某些信号,被阻塞的信号在产生时将保持在未决状态,直到进程解除对该信号的阻塞,才执行递达的动作。信号的阻塞和解除可以通过sigprocmask()
函数来实现。
sigprocmask()
函数允许进程指定一个新的信号屏蔽字(即阻塞哪些信号),并可以获取旧的信号屏蔽字。通过设置信号屏蔽字,进程可以控制哪些信号被阻塞,哪些信号被允许递达。
三、信号的保存与递达
1. 信号的保存
Linux内核为每个进程维护了与信号相关的数据结构,主要包括三张表:阻塞表(Block表)、未决表(Pending表)和处理函数表(Handler表)。
- 阻塞表:一个位图,用于表示进程当前阻塞了哪些信号。
- 未决表:同样是一个位图,用于表示进程当前有哪些信号处于未决状态。
- 处理函数表:一个函数指针数组,用于表示当信号递达时应该执行的处理动作。
当信号产生时,内核会在进程控制块(PCB)的Pending表中设置对应信号位的值为1,表示该信号已经产生且处于未决状态。然后,内核会检查Block表,查看该信号是否被阻塞。如果被阻塞,则信号会保持在未决状态;如果没有被阻塞,则根据Handler表中的处理函数指针执行相应的处理动作。
2. 信号的递达
信号的递达是指信号从产生到被处理的过程。在Linux中,信号的递达通常是在内核态和用户态之间切换时进行的。当进程从内核态返回到用户态时,内核会检查待处理的信号,并根据信号的处理方式执行相应的动作。
如果进程为某个信号设置了自定义处理函数,则在信号递达时,内核会切换到用户态并调用该处理函数。如果进程选择忽略信号或执行默认处理动作,则内核会根据相应的规则来处理信号。
四、总结与知识脉络
学习Linux信号需要打通以下知识脉络:
- 信号的基本概念:理解信号的定义、产生方式、分类以及默认处理动作。
- 信号的处理与捕捉:掌握如何通过
signal()
函数或sigaction()
函数来设置信号处理函数,并了解信号捕捉的机制和细节。 - 信号的阻塞与解除:了解如何通过
sigprocmask()
函数来设置和修改进程的信号屏蔽字,从而控制哪些信号被阻塞或允许递达。 - 信号的保存与递达:理解Linux内核如何为进程维护信号相关的数据结构,以及信号从产生到递达的过程。
通过这三步走,可以系统地学习Linux信号的相关知识,并在实际编程中灵活运用信号机制来实现进程间的异步通信和错误处理。
标签:脉络,三步走,阻塞,内核,信号,Linux,进程,函数 From: https://blog.csdn.net/m0_70066267/article/details/142515660