Linux 进程信号初识

server/2024/11/17 21:33:31/

目录

0.前言

1.什么是信号

1.1生活中的信号

1.2 OS中的信号

2.认识信号

2.1信号概念

2.2查看信号

2.3 signal函数

2.4代码示例

3. 信号处理方式

3.1 忽略信号

3.2 默认处理

3.3 自定义处理

4.小结


(图像由AI生成) 

0.前言

在之前的学习中,我们介绍了 Linux 进程间通信 的多种方式,如管道、消息队列、共享内存等。接下来,我们将关注另一种进程间的重要机制:信号。信号是 Linux 系统中一种重要的异步通信手段,用于通知进程发生了某些事件,比如用户按下 Ctrl+C 或操作系统的某些异常情况。在本文中,我们将从基础知识出发,一步步认识信号的作用和处理方式。

1.什么是信号

1.1生活中的信号

生活中,我们常常通过“信号”获取信息并采取行动。例如:

外卖通知
你通过手机订了一份外卖。以下是外卖到来过程中的几个场景:

  1. 下单后等待外卖送达:你知道外卖会来,但不知道具体什么时候会送到。此时,你继续忙自己的事情,比如工作、学习,甚至小憩。
  2. 接到送餐员的通知:手机收到送餐员的短信或电话通知时,你立刻知道外卖到了。但如果你正在开会,可能会让送餐员稍等几分钟。
  3. 处理外卖
    • 你亲自下楼取外卖,这相当于采取了默认动作。
    • 你让同事帮忙取外卖,类似于自定义动作。
    • 外卖到后,你暂时不处理,继续工作,这等于忽略了外卖。

整个过程中,“外卖通知”就是一种“信号”。

  • 它是异步的:你不知道通知会在什么时候发来。
  • 它需要处理:当收到通知时,你决定如何响应。
  • 它可能被忽略:你可以选择不处理这个信号(比如延后取外卖)。

这与操作系统中的信号非常相似,信号是用于通知进程发生了某种事件,而处理与否、如何处理由程序决定。

1.2 OS中的信号

信号在操作系统中的概念
在操作系统中,信号是用来通知进程发生特定事件的一种机制。例如:

  • 用户按下 Ctrl+C 中断程序。
  • 某些硬件或软件事件(如定时器超时、文件访问异常)触发信号。

举例:死循环程序与 Ctrl+C 中断

让我们用一个简单的死循环程序来演示信号的基本作用:

#include <stdio.h>
#include <unistd.h>int main() {while (1) {printf("Running... Press Ctrl+C to stop.\n");sleep(1);}return 0;
}
  1. 运行程序:执行该程序后,它会不断输出 Running... 并每隔一秒更新一次。
  2. 按下 Ctrl+C:此时,操作系统会向程序发送一个 SIGINT 信号(中断信号)。
  3. 响应信号:程序收到信号后,会执行默认的信号处理动作:终止程序。

以下是一次完整的信号交互过程:

  • 信号发送:按下 Ctrl+C,操作系统检测到键盘事件并发送 SIGINT 信号给程序。
  • 信号处理:程序执行默认动作,终止运行。
  • 异步性:信号的到来是非确定的,程序无法预测用户何时按下 Ctrl+C

总结:

  • 信号在操作系统中是一种异步事件通知机制。
  • 它类似于生活中的通知机制,可以在某个事件发生时通知进程并交由其处理。

2.认识信号

2.1信号概念

信号是操作系统中的一种进程间通信机制,用于通知进程发生了某些事件。
它的核心特性包括:

  1. 异步性:信号的发送和接收是异步的,进程无法预知信号何时到达。
  2. 触发性:信号通常与特定事件相关联,如用户输入、进程状态变化、硬件异常等。
  3. 有限性:信号的传递只携带类型信息(如 SIGINTSIGKILL),而不包括详细的数据。

信号的生命周期:

  1. 发送信号:由内核或进程触发。
  2. 等待处理:信号到达目标进程时,可能暂时被挂起。
  3. 信号处理:进程可以选择处理信号、忽略信号或采取默认操作。

信号类似于生活中的通知,例如系统提示电量不足(异步)、提醒需要充电(触发),但没有告知具体电量值(有限性)。

2.2查看信号

Linux 系统提供了多个标准信号,可以通过 kill -l 命令查看完整列表。

信号分类:

  1. 通用信号(1-31):常用于进程的基本控制,例如中断、终止、挂起等。

    • SIGHUP:挂起信号。
    • SIGINT:中断信号(如按下 Ctrl+C)。
    • SIGKILL:强制终止信号。
    • SIGTERM:终止信号,可被捕获和处理。
    • SIGSEGV:非法内存访问。
  2. 实时信号(34-64):支持更高的灵活性和用户定义,用于实时任务通信。

    • SIGRTMIN:最小的实时信号。
    • SIGRTMAX:最大的实时信号。

示例命令:

kill -l

输出如下:

 

2.3 signal函数

函数简介

signal 是一个 ANSI C 标准函数,用于设置进程对特定信号的处理方式。通过该函数,可以定义信号处理程序,或选择默认行为、忽略信号。

函数原型

#include <signal.h>typedef void (*sighandler_t)(int);sighandler_t signal(int signum, sighandler_t handler);
  • 参数:

    • signum:信号编号,例如 SIGINT(中断信号)。
    • handler:指定的信号处理方式,可以是以下三种之一:
      • SIG_IGN:忽略信号。
      • SIG_DFL:采用信号的默认处理方式。
      • 自定义处理函数的地址。
  • 返回值:

    • 成功时返回之前的信号处理方式(SIG_IGNSIG_DFL 或处理函数地址)。
    • 失败时返回 SIG_ERR,同时设置 errno 以说明错误原因。

函数行为

  • 当进程接收到信号 signum 时,signal 函数会决定如何处理:
    • 忽略信号SIG_IGN):直接跳过信号处理。
    • 默认处理SIG_DFL):执行信号的系统默认行为(如终止进程)。
    • 自定义函数:调用程序员定义的函数处理信号。
  • 特殊信号:SIGKILLSIGSTOP 无法被捕获或忽略。

注意事项

  • 不同 UNIX 系统对 signal 的实现存在差异,Linux 的行为在历史版本中也有变化。
  • 推荐使用 sigaction 替代 signal,因为它提供了更强的功能和一致性。

2.4代码示例

以下代码演示了如何使用 signal 函数捕获 SIGINT 信号,并自定义处理方式:

#include <stdio.h>
#include <signal.h>
#include <unistd.h>// 自定义信号处理函数
void handle_sigint(int signum) {printf("Caught SIGINT (signal number: %d). Exiting gracefully...\n", signum);// 释放资源并退出_exit(0);
}int main() {// 使用 signal 函数捕获 SIGINT 信号if (signal(SIGINT, handle_sigint) == SIG_ERR) {perror("Error setting signal handler");return 1;}printf("Press Ctrl+C to trigger SIGINT...\n");// 无限循环,等待信号while (1) {printf("Running...\n");sleep(1);}return 0;
}

代码分析

  1. 自定义信号处理函数

    • handle_sigint 是一个函数,接收信号编号作为参数。
    • SIGINT 信号到达时,程序会调用该函数,输出提示信息并优雅退出。
  2. 设置信号处理

    • signal(SIGINT, handle_sigint) 指定 SIGINT 信号的处理方式为调用 handle_sigint 函数。
    • 如果 signal 调用失败,返回 SIG_ERR 并设置 errno,在此处打印错误信息并退出程序。
  3. 等待信号

    • 程序进入无限循环,每秒打印一次 Running...,等待用户按下 Ctrl+C 触发 SIGINT 信号。

运行结果

  • 正常运行时,程序会不断输出 Running...
  • 当按下 Ctrl+C 时,程序捕获到 SIGINT 信号,调用 handle_sigint 函数,输出提示并退出。

3. 信号处理方式

在 Linux 系统中,当进程接收到信号时,可以采用三种不同的方式处理信号:忽略默认处理自定义处理。进程对信号的处理方式决定了程序如何响应特定事件或异常。

3.1 忽略信号

进程可以选择忽略某些信号,这意味着即使信号被发送到进程,它也会被直接丢弃,进程的行为不会受到任何影响。

  • 常用场景

    • 忽略某些不重要的信号,如 SIGPIPE(管道断裂信号),避免进程因无关紧要的错误而终止。
    • 对用户输入的某些信号(如 SIGINT)做出特定策略,暂时忽略信号而不中断当前任务。
  • 代码示例

#include <signal.h>
#include <stdio.h>
#include <unistd.h>int main() {// 忽略 SIGINT 信号signal(SIGINT, SIG_IGN);printf("SIGINT signal is ignored. Try pressing Ctrl+C.\n");while (1) {printf("Running...\n");sleep(1);}return 0;
}

运行此程序后,按下 Ctrl+C 时进程不会终止,因为 SIGINT 信号被忽略了。

3.2 默认处理

系统为每种信号定义了默认的处理方式,进程可以直接使用这些默认动作:

  • 常见默认行为

    • 终止进程:SIGTERMSIGKILL
    • 生成核心转储文件并终止进程:SIGSEGVSIGABRT
    • 停止进程:SIGSTOP
  • 特点

    • 简单快捷。
    • 适用于无需自定义处理逻辑的场景。
  • 代码示例

#include <signal.h>
#include <stdio.h>
#include <unistd.h>int main() {printf("Default behavior for SIGINT. Press Ctrl+C to terminate.\n");while (1) {printf("Running...\n");sleep(1);}return 0;
}

运行此程序时,按下 Ctrl+C 会发送 SIGINT 信号,进程将采用默认行为:立即终止。

3.3 自定义处理

通过定义信号处理函数,程序可以指定如何响应某些信号。这种方式为程序提供了灵活性,可以在信号到达时执行特定逻辑。

  • 常见用途

    • 释放资源(文件句柄、内存)并优雅退出。
    • 记录日志以便诊断问题。
    • 延迟或改变信号的处理逻辑。
  • 代码示例

#include <signal.h>
#include <stdio.h>
#include <unistd.h>// 自定义信号处理函数
void custom_handler(int signum) {printf("Caught signal %d. Cleaning up before exiting...\n", signum);// 释放资源// 退出程序_exit(0);
}int main() {// 捕获 SIGINT 信号并设置自定义处理函数signal(SIGINT, custom_handler);printf("Custom handler for SIGINT. Press Ctrl+C.\n");while (1) {printf("Running...\n");sleep(1);}return 0;
}

运行此程序后,按下 Ctrl+C 时会触发自定义的信号处理逻辑,打印提示信息并优雅退出。

4.小结

信号是 Linux 系统中重要的进程间通信机制,具有异步性和灵活性。在本篇博客中,我们从生活中的类比入手,深入了解了信号的概念、种类及其处理方式,包括忽略信号、使用默认处理、以及自定义处理函数。通过实际代码示例,我们掌握了如何使用 signal 函数捕获并处理信号,为开发更高效、更健壮的 Linux 应用程序奠定了基础。


http://www.ppmy.cn/server/142449.html

相关文章

Redis 字符串(String)

Redis 字符串数据类型的相关命令用于管理 redis 字符串值&#xff0c;基本语法如下&#xff1a; 语法 redis 127.0.0.1:6379> COMMAND KEY_NAME实例 redis 127.0.0.1:6379> SET w3ckey redis OK redis 127.0.0.1:6379> GET w3ckey "redis"在以上实例中我…

【回溯法】——分割回文串

131. 分割回文串 一、题目难度 中等 二、相关标签与相关企业 [相关标签] [相关企业] 三、题目描述 给你一个字符串 s s s&#xff0c;请你将 s s s 分割成一些子串&#xff0c;使每个子串都是回文串。返回 s s s 所有可能的分割方案。 四、示例 示例1 输入&#xf…

Bugku CTF_Web——点login咋没反应

Bugku CTF_Web——点login咋没反应 进入靶场 随便输个试试 看来确实点login没反应 抓包看看 也没有什么信息 看了下源码 给了点提示 一个admin.css try ?12713传参试试 拿到一个php代码 <?php error_reporting(0); $KEYctf.bugku.com; include_once("flag.php&q…

Kafka-Controller选举

一、上下文 《Kafka-broker粗粒度启动流程》博客中我们分析了broker的大致启动流程&#xff0c;这个时候每个broker都不是controller角色&#xff0c;下面我们就来看下它是如何选举出来的吧 二、设置ZooKeeper ‌ZooKeeper是一个开源的分布式协调服务&#xff0c;主要用于分…

机器学习-4:机器学习的建模流程

机器学习的建模流程 流程为&#xff1a; 原始数据 --> 数据预处理 --> 特征工程 --> 建模 --> 验证。 原始数据收集 所有AI或机器学习的基础就是数据&#xff0c;没有数据就什么都做不了&#xff0c;在搭建一个系统之前首要考虑的就是有没有足够多的数据可以支撑这…

VMware 17虚拟Ubuntu 22.04设置共享目录

VMware 17虚拟Ubuntu 22.04设置共享目录 共享文件夹挂载命令&#xff01;&#xff01;&#xff01;<font colorred>配置启动自动挂载Chapter1 VMware 17虚拟Ubuntu 22.04设置共享目录一、卸载老版本二、安装open-vm-tools<font colorred>三、配置启动自动挂载四、添…

【计网不挂科】计算机网络第一章< 概述 >习题库(含答案)

前言 大家好吖&#xff0c;欢迎来到 YY 滴计算机网络 系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过C的老铁 本博客主要内容&#xff0c;收纳了一部门基本的计算机网络题目&#xff0c;供yy应对期中考试复习。大家可以参考 本章为分章节的习题内容题库&#x…

【青牛科技】视频监控器应用

1、简介&#xff1a; 我司安防产品广泛应用在视频监控器上&#xff0c;产品具有性能优良&#xff0c;可 靠性高等特点。 2、图示&#xff1a; 实物图如下&#xff1a; 3、具体应用&#xff1a; 标题&#xff1a;视频监控器应用 简介&#xff1a;视频监控器工作原理是光&#x…