当前位置:首页 > 系统教程 > 正文

Linux进程信号详解(一):信号的产生与保存

Linux进程信号详解(一):信号的产生与保存

从原理到实践,掌握信号基础

Linux进程信号详解(一):信号的产生与保存 进程信号 信号产生 信号保存 信号处理 第1张

在Linux系统中,进程信号是进程间通信的一种异步通知机制,用于通知进程发生了某个事件。信号本质上是软件层次上对中断机制的模拟,它可以在任何时间发送给进程,进程需要做出相应的处理。本文作为信号系列的第一篇,将深入探讨信号产生的多种方式以及信号保存在内核中的实现原理。

一、信号的概念与分类

Linux系统定义了标准信号(如SIGINT、SIGKILL等),每个信号都有一个唯一的编号和默认动作。信号的生命周期包括:产生、保存(未决)、递送和处理。本文重点讲解前两个阶段,即信号产生信号保存

二、信号的产生方式

信号的产生可以来自多种场景,主要分为以下几类:

  • 硬件异常产生信号:例如非法访问内存(段错误)会触发SIGSEGV,浮点异常触发SIGFPE。
  • 终端按键产生信号:用户按下Ctrl+C会向前台进程组发送SIGINT,Ctrl+\发送SIGQUIT。
  • 软件条件产生信号:如alarm定时器到期产生SIGALRM,向管道写入但无读端产生SIGPIPE。
  • 系统调用产生信号:通过kill()、raise()、sigqueue()等函数显式发送信号。

这些产生方式最终都会转化为内核向目标进程的PCB中写入一个信号记录,即进入信号保存阶段。

三、信号在内核中的保存

每个进程的PCB(task_struct)中维护了三个与信号相关的位图:pending(未决信号集)block(阻塞信号集)handler(信号处理函数表)。信号产生后,内核会将对应信号的比特位置1,表示该信号已经产生但尚未被处理(即未决状态)。信号的保存就是通过pending位图实现的。

阻塞信号集(block)用于屏蔽某些信号,被屏蔽的信号即使产生也不会被递送,仍然停留在pending中,直到解除屏蔽。这种机制为信号处理提供了灵活的控制能力。

四、相关系统调用与操作

用户空间可以通过一系列函数操作信号集:

  • sigemptyset()sigfillset()sigaddset()等用于操作信号集。
  • sigprocmask()用于读取或更改进程的阻塞信号集。
  • sigpending()用于获取当前未决信号集。

下面是一个简单示例,演示如何屏蔽SIGINT信号并查看未决状态:

    #include #include #include int main() {    sigset_t newmask, oldmask, pendmask;    sigemptyset(&newmask);    sigaddset(&newmask, SIGINT);          // 将SIGINT加入屏蔽集    sigprocmask(SIG_BLOCK, &newmask, &oldmask); // 阻塞SIGINT        printf("SIGINT blocked, press Ctrl+C in 5 seconds...");    sleep(5);        sigpending(&pendmask);                // 获取未决信号集    if (sigismember(&pendmask, SIGINT))        printf("SIGINT is pending");    // 如果期间按了Ctrl+C,则pending中SIGINT位为1        sigprocmask(SIG_SETMASK, &oldmask, NULL); // 解除阻塞    printf("SIGINT unblocked, exiting");    return 0;}  

运行该程序,在5秒内按下Ctrl+C,可以看到pending信号被捕获,但进程并未终止,因为信号被阻塞保存了。当解除阻塞后,信号才会递送(默认动作终止进程)。

五、总结

本文详细介绍了进程信号的产生来源(硬件、软件、终端、系统调用)以及信号在内核中的保存机制(pending和block位图)。理解信号的产生与保存是深入学习信号处理(如自定义信号处理函数)的基础。下一篇文章我们将继续探讨信号的递送与处理过程。