Linux 进程信号的产生

目录

0.前言

1. 通过终端按键产生信号

1.1 Ctrl+C:发送 SIGINT 信号

1.2 Ctrl+\:发送 SIGQUIT 信号

1.3 Ctrl+Z:发送 SIGTSTP 信号

2.调用系统命令向进程发信号

3.使用函数产生信号

3.1 kill 函数

3.2 raise 函数

3.3 abort 函数

4.由软件条件产生信号

4.1 SIGPIPE 信号

4.2 SIGALRM 信号

5.硬件异常产生信号

5.1 常见硬件异常信号

5.1.1 除 0 操作:SIGFPE 信号

5.1.2 访问野指针:SIGSEGV 信号

5.2 核心转储(Core Dump)

6.小结


(图像由AI生成) 

0.前言

在 Linux 系统中,信号是用于进程间通信的重要机制,通过信号可以实现对进程的控制、状态传递以及异常处理。在上一篇博客中,我们简单介绍了进程信号的概念、常见类型以及信号的基本作用。本篇博客将继续深入,从信号的产生开始,探讨信号如何被触发并送达进程。这是理解信号处理的第一步,也是后续学习信号保存和信号处理机制的基础。通过本篇内容,你将了解信号的几种常见触发方式,包括用户手动产生、程序主动触发,以及系统自动生成等场景。

1. 通过终端按键产生信号

Linux 系统中,终端不仅是与操作系统交互的重要工具,也是信号产生的主要入口之一。在终端中按下特定的键盘组合,可以向当前前台进程发送信号,这些信号可以中断、暂停或终止程序运行。

1.1 Ctrl+C:发送 SIGINT 信号

  • 概念
    按下 Ctrl+C 会向当前前台进程发送 SIGINT 信号(Signal Interrupt)。这是用户最常用的中断方式,用于优雅地终止正在运行的程序。

  • 默认行为
    程序收到 SIGINT 信号后,默认会立即终止执行,但不会生成核心转储文件。

  • 实际场景
    例如,当你运行一个死循环脚本时,可以通过 Ctrl+C 终止它:

    while true; do echo "Running..."; sleep 1; done

    按下 Ctrl+C 后,循环将中止。

  • 自定义处理
    程序也可以捕获 SIGINT 信号并定义自定义逻辑。例如,以下代码展示了如何拦截并处理 SIGINT 信号:

    #include <stdio.h>
    #include <signal.h>
    
    void handle_sigint(int sig) {
        printf("Caught SIGINT signal: %d\n", sig);
    }
    
    int main() {
        signal(SIGINT, handle_sigint);
        while (1) {
            printf("Running... Press Ctrl+C to interrupt\n");
            sleep(1);
        }
        return 0;
    }
    

1.2 Ctrl+\:发送 SIGQUIT 信号

  • 概念
    按下 Ctrl+\ 会触发 SIGQUIT 信号(Signal Quit)。与 SIGINT 类似,SIGQUIT 的默认行为也是终止进程,但它会额外生成一个核心转储文件(core dump)。

  • 默认行为
    程序收到 SIGQUIT 信号后,系统会记录程序运行时的内存状态,生成 core dump 文件,供开发者调试使用。

  • 实际场景
    在程序运行时按下 Ctrl+\,程序不仅会终止,还会留下 core dump 文件。如果 core dump 未启用,可以通过以下命令启用:

    ulimit -c unlimited # 设置核心文件大小为无限

1.3 Ctrl+Z:发送 SIGTSTP 信号

  • 概念
    按下 Ctrl+Z 会向前台进程发送 SIGTSTP 信号(Signal Terminal Stop)。该信号用于将进程挂起(暂停运行),并将控制权还给终端。

  • 默认行为
    收到 SIGTSTP 信号后,进程会进入“暂停”状态,直到用户手动恢复它。恢复命令包括:

    • fg:恢复进程到前台继续执行。
    • bg:将进程移至后台继续执行。
  • 实际场景
    当运行一个耗时较长的任务时,可以按 Ctrl+Z 暂时挂起它,进行其他操作。例如:

    sleep 100

    按下 Ctrl+Z 后,任务将暂停。输入 fg 恢复它,或者使用 bg 将其移至后台。

2.调用系统命令向进程发信号

在 Linux 系统中,除了通过终端按键,用户还可以使用系统命令向进程发送信号。其中最常用的命令是 kill,它允许用户直接向目标进程发送指定信号,以控制或终止进程。kill 命令的基本语法如下:

kill [选项] <进程ID>

常用选项

  • -SIGNAL:指定发送的信号名称或编号。例如,-SIGKILL-9 表示发送 SIGKILL 信号。
  • -l:列出所有信号名称及其编号。
  • -s SIGNAL:显式指定信号名称。
  • -pid:目标进程 ID,可以是一个或多个。

举例说明

  • 发送默认信号(SIGTERM)

    kill 1234(某个进程的PID)

    默认发送 SIGTERM 信号,通常用于优雅地终止进程。

  • 发送特定信号
    强制终止进程可以使用 SIGKILL(信号编号 9):

    kill -9 1234
  • 列出所有信号
    查看系统支持的信号列表:

    kill -l

3.使用函数产生信号

3.1 kill 函数

kill 是用于向任意进程发送信号的系统调用,其语法如下:

#include <sys/types.h>
#include <signal.h>

int kill(pid_t pid, int sig);
  • 参数说明

    • pid:目标进程的 PID。
      • > 0:向指定的单个进程发送信号。
      • = 0:向调用进程所在的进程组发送信号。
      • < -1:向特定进程组发送信号。
    • sig:要发送的信号类型(如 SIGINTSIGKILL)。
  • 示例: 向当前进程发送 SIGTERM 信号:

    #include <signal.h>
    #include <unistd.h>
    
    int main() {
        kill(getpid(), SIGTERM);
        return 0;
    }
    

3.2 raise 函数

raise 是一个标准库函数,用于向自身发送信号,其本质是对 kill 函数的封装。语法如下:

#include <signal.h>

int raise(int sig);
  • 参数说明

    • sig:要发送的信号。
  • 特点

    • 只能向当前进程发送信号。
    • 使用方便,不需要获取 PID。
  • 示例: 触发 SIGINT 信号:

    #include <signal.h>
    #include <stdio.h>
    
    void signal_handler(int sig) {
        printf("Caught signal: %d\n", sig);
    }
    
    int main() {
        signal(SIGINT, signal_handler); // 注册信号处理函数
        raise(SIGINT); // 发送信号
        return 0;
    }
    

3.3 abort 函数

abort 是一个标准库函数,用于向自身发送 SIGABRT 信号,强制使程序异常终止,并生成核心转储文件(core dump)。语法如下:

#include <stdlib.h>

void abort(void);
  • 特点

    • 触发 SIGABRT 信号。
    • 通常用于在程序遇到严重错误时调用。
    • 如果未捕获 SIGABRT,程序将直接退出。
  • 示例: 强制终止程序并生成 core dump:

    #include <stdlib.h>
    
    int main() {
        abort();
        return 0;
    }
    

4.由软件条件产生信号

除了用户手动触发信号和程序显式调用信号函数外,信号也可以在程序运行时,由特定的软件条件自动触发。例如,管道写入异常会触发 SIGPIPE 信号,定时器到期会触发 SIGALRM 信号。

4.1 SIGPIPE 信号

  • 触发条件
    当一个进程尝试向已经关闭的管道(pipe)或套接字(socket)写入数据时,系统会自动发送 SIGPIPE 信号。默认行为是终止进程。

  • 常见场景
    例如,父进程向关闭的管道写入数据:

    #include <unistd.h>
    #include <signal.h>
    #include <stdio.h>
    
    int main() {
        int fds[2];
        pipe(fds);           // 创建管道
        close(fds[0]);       // 关闭读端
    
        if (write(fds[1], "data", 4) == -1) {  // 写入数据
            perror("write error");
        }
        return 0;
    }
    

    输出类似:

    write error: Broken pipe
  • 解决方式
    捕获 SIGPIPE 信号并进行自定义处理,或者忽略该信号:

    signal(SIGPIPE, SIG_IGN); // 忽略SIGPIPE

4.2 SIGALRM 信号

  • 触发条件
    SIGALRM 信号由 alarm 函数触发,用于在指定的时间间隔后通知进程执行某些任务。

  • alarm 函数
    alarm 函数设置一个秒级定时器,到期时发送 SIGALRM 信号:

    #include <unistd.h>
    
    unsigned int alarm(unsigned int seconds);
    
  • 示例
    设置一个 3 秒定时器,触发后执行信号处理函数:

    #include <signal.h>
    #include <unistd.h>
    #include <stdio.h>
    
    void handle_alarm(int sig) {
        printf("Alarm triggered!\n");
    }
    
    int main() {
        signal(SIGALRM, handle_alarm); // 注册处理函数
        alarm(3);                      // 设置定时器
        pause();                       // 等待信号
        return 0;
    }
    

    输出类似:

    Alarm triggered!
  • 取消定时器
    再次调用 alarm 函数,传入 0 即可取消定时器:

    alarm(0);

5.硬件异常产生信号

在程序运行过程中,某些硬件异常(例如非法操作或内存访问)会触发系统向进程发送特定信号。这些信号通常表示程序存在错误,默认行为是终止程序运行,并可能生成 核心转储文件(core dump)

5.1 常见硬件异常信号

5.1.1 除 0 操作:SIGFPE 信号

  • 触发条件
    当程序执行非法的算术运算(如整数除以 0)时,会触发 SIGFPE 信号(Floating Point Exception)。默认行为是终止进程。

  • 示例代码
    以下代码试图执行除以 0 的操作:

    #include <stdio.h>
    
    int main() {
        int a = 1;
        int b = 0;
        printf("%d\n", a / b); // 除以 0
        return 0;
    }
    

    结果:程序崩溃,触发 SIGFPE

  • 捕获信号
    使用信号处理函数捕获 SIGFPE

    #include <signal.h>
    #include <stdio.h>
    
    void handle_sigfpe(int sig) {
        printf("Caught SIGFPE: Illegal operation\n");
    }
    
    int main() {
        signal(SIGFPE, handle_sigfpe);
        int a = 1, b = 0;
        printf("%d\n", a / b);
        return 0;
    }
    

5.1.2 访问野指针:SIGSEGV 信号

  • 触发条件
    当程序尝试访问非法内存地址(如空指针或已释放的内存)时,会触发 SIGSEGV 信号(Segmentation Fault)。默认行为是终止进程。

  • 示例代码
    以下代码访问了一个空指针:

    #include <stdio.h>
    
    int main() {
        int *ptr = NULL;
        *ptr = 42; // 访问空指针
        return 0;
    }
    

    结果:程序崩溃,触发 SIGSEGV

  • 捕获信号
    可以捕获 SIGSEGV 信号来处理异常:

    #include <signal.h>
    #include <stdio.h>
    
    void handle_sigsegv(int sig) {
        printf("Caught SIGSEGV: Invalid memory access\n");
    }
    
    int main() {
        signal(SIGSEGV, handle_sigsegv);
        int *ptr = NULL;
        *ptr = 42;
        return 0;
    }
    

5.2 核心转储(Core Dump)

核心转储是一种调试工具,用于记录程序崩溃时的内存状态,帮助开发者排查问题。

生成核心转储

  1. 启用核心转储
    默认情况下,核心转储可能被限制或禁用。可以使用以下命令启用:

    ulimit -c unlimited
  2. 运行出错程序
    当程序崩溃时,系统会生成核心转储文件,通常命名为 corecore.<pid>

  3. 调试核心转储
    使用调试工具 gdb 分析核心转储文件:

    gdb ./program core

    在调试器中,可以查看崩溃时的调用堆栈和内存状态。

示例调试

假设程序崩溃生成了核心转储文件,可以通过以下命令进入调试器:

gdb ./a.out core

进入调试器后,运行 bt(backtrace)命令查看调用堆栈:

bt (进入gdb调试后)

6.小结

本篇博客全面介绍了 Linux 系统中信号的产生方式,从终端按键、系统命令,到函数调用和软件条件触发,再到硬件异常的信号处理。信号作为进程间通信和异常管理的关键机制,贯穿了用户操作、程序设计和系统运行的各个层面。理解信号的来源和触发方式,是深入学习 Linux 信号处理与调试技巧的重要基础,为编写健壮的程序提供了强有力的支持。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/917492.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【大数据学习 | HBASE高级】hive操作hbase

一般在查询hbase的数据的时候我们可以直接使用hbase的命令行或者是api进行查询就行了&#xff0c;但是在日常的计算过程中我们一般都不是为了查询&#xff0c;都是在查询的基础上进行二次计算&#xff0c;所以使用hbase的命令是没有办法进行数据计算的&#xff0c;并且对于hbas…

微信小程序 https://thirdwx.qlogo.cn 不在以下 downloadFile 合法域名列表中

授权登录后&#xff0c;拿到用户头像进行加载&#xff0c;但报错提示&#xff1a; https://thirdwx.qlogo.cn 不在以下 downloadFile 合法域名列表中 解决方法一&#xff08;未完全解决&#xff0c;临时处理&#xff09;&#xff1a;在微信开发者工具将不校验...勾上就可以访问…

rk3399开发环境使用Android 10初体验蓝牙功能

版本 日期 作者 变更表述 1.0 2024/11/10 于忠军 文档创建 零. 前言 由于Bluedroid的介绍文档有限&#xff0c;以及对Android的一些基本的知识需要了(Android 四大组件/AIDL/Framework/Binder机制/JNI/HIDL等)&#xff0c;加上需要掌握的语言包括Java/C/C等&#xff0…

1. Django中的URL调度器 (项目创建与简单测试)

1. 创建 Django 项目 运行以下命令创建一个名为 blog_project 的 Django 项目&#xff1a; django-admin startproject blog_project2. 创建博客应用 Django 中&#xff0c;项目可以包含多个应用。创建一个名为 blog 的应用&#xff1a; cd blog_project python manage.py …

数据结构(初阶4)---循环队列详解

循环队列 1.循环队列的结构  1).逻辑模式 2.实现接口  1).初始化  2).判断空和满  3).增加  4).删除  5).找头  6).找尾 3.循环队列的特点 1.循环队列的结构 1).逻辑模式 与队列是大同小异的&#xff0c; 其中还是有一个指向队列头的head指针&#xff0c; 也有一个指向尾…

【蓝桥杯算法】Java的基础API

1. BigInteger 的使用 1.1. 判素数 package 模板;import java.math.BigInteger; import java.util.Scanner;public class 判素数 {static Scanner in new Scanner(System.in);public static void main(String[] args) {int q in.nextInt();while (q-- > 0) {BigInteger …

STM32设计井下瓦斯检测联网WIFI加Zigbee多路节点协调器传输

目录 目录 前言 一、本设计主要实现哪些很“开门”功能&#xff1f; 二、电路设计原理图 1.电路图采用Altium Designer进行设计&#xff1a; 2.实物展示图片 三、程序源代码设计 四、获取资料内容 前言 本系统基于STM32微控制器和Zigbee无线通信技术&#xff0c;设计了…

320页PDF | 集团IT蓝图总体规划报告-德勤(限免下载)

一、前言 这份报告是集团IT蓝图总体规划报告-德勤。在报告中详细阐述了德勤为某集团制定的全面IT蓝图总体规划&#xff0c;包括了集团信息化目标蓝图、IT应用规划、数据规划、IT集成架构、IT基础设施规划以及IT治理体系规划等关键领域&#xff0c;旨在为集团未来的信息化发展提…

Python毕业设计选题:基于django+vue的二手物品交易系统

开发语言&#xff1a;Python框架&#xff1a;djangoPython版本&#xff1a;python3.7.7数据库&#xff1a;mysql 5.7数据库工具&#xff1a;Navicat11开发软件&#xff1a;PyCharm 系统展示 管理员登录 管理员功能界面 用户管理 店铺管理 二手物品管理 广告管理 留言反馈 订单…

Android CoordinatorLayout:打造高效交互界面的利器

目录 一、CoordinatorLayout 介绍及特点 二、使用方法 2.1 创建 CoordinatorLayout 布局 2.2 添加需要协调的子视图 2.3 自定义 Behavior 三、结语 相关推荐 在Android开发中&#xff0c;面对复杂多变的用户界面需求&#xff0c;CoordinatorLayout以其强大的交互管理能力…

docker-hub 无法访问,使用windows魔法拉取docker images再上传到linux docker环境中

云机的服务器是可以docker拉取镜像的&#xff0c;但是本地的虚拟机、物理服务器等网络环境不好的情况&#xff0c;是无法访问docker-hub的&#xff0c;即使更换了docker镜像源国内源也无法使用。 本文章使用 在魔法网络环境下的windows&#xff0c;下载docker images后&#xf…

在Ubuntu22.04上源码构建ROS noetic环境

Ubuntu22.04上源码构建ROS noetic 起因准备环境创建工作目录并下载源码安装编译依赖包安装ros_comm和rosconsole包的两个补丁并修改pluginlib包的CMakeLists的编译器版本编译安装ROS noetic和ros_test验证 起因 最近在研究VINS-Mono从ROS移植到ROS2&#xff0c;发现在编写feat…

【linux学习指南】VSCode部署Ubantu云服务器,与Xshell进行本地通信文件编写

文章目录 &#x1f4dd;前言&#x1f320; 步骤&#x1f309;测试同步 &#x1f6a9;总结 &#x1f4dd;前言 本文目的是讲使用Vscode连接Ubantu,与本地Xshell建立通信同步文件编写。 查看本机系统相关信息&#xff1a; cat /etc/lsb*DISTRIB_IDUbuntu: 表示这是 Ubuntu 发行…

【JavaSE线程知识总结】

多线程 一.创建线程1.多线程创建方式一(Thread)2.多线程创键方式二(Runnable)3.线程创建方式三 二.线程安全问题解决办法1.使用同步代码块synchornized 2 .使用Lock解决线程安全问题 三.总结 线程就是程序内部的一条执行流程 一.创建线程 常用的方法 Thread.currentThread()…

用OMS进行 OceanBase 租户间数据迁移的测评

基本概念 OceanBase迁移服务&#xff08;&#xff0c;简称OMS&#xff09;&#xff0c;可以让用户在同构或异构 RDBMS 与OceanBase 数据库之间进行数据交互&#xff0c;支持数据的在线迁移&#xff0c;以及实时增量同步的复制功能。 OMS 提供了可视化的集中管控平台&#xff…

第一个 Flutter 项目(1)共46节

前端开发工具vs code&#xff0c;安装Flutter sdk&#xff0c;如果你的下载速度比较慢&#xff0c;可以选择这个&#x1f604; flutter sdk 解压码&#xff1a;stwq 配置可以看这Flutter 新建工程一直等待 解决办法-CSDN博客 如果你是新的 Flutter 开发者&#xff0c;我们建…

POI实现根据PPTX模板渲染PPT

目录 1、前言 2、了解pptx文件结构 3、POI组件 3.1、引入依赖 3.2、常见的类 3.3、实现原理 3.4、关键代码片段 3.4.1、获取ppt实例 3.4.2、获取每页幻灯片 3.4.3、循环遍历幻灯片处理 3.4.3.1、文本 3.4.3.2、饼图 3.4.3.3、柱状图 3.4.3.4、表格 3.4.3.5、本地…

高级数据结构——hash表与布隆过滤器

文章目录 hash表与布隆过滤器1. hash函数2. 选择hash函数3. 散列冲突3.1 负载因子3.2 冲突解决3. STL中的散列表 4. 布隆过滤器4.1 背景1. 应用场景2. 常见的处理场景&#xff1a; 4.2 布隆过滤器构成4.3 原理4.4 应用分析4.5 要点 5. 分布式一致性hash5.1 缓存失效问题 6. 大数…

小程序19-微信小程序的样式和组件介绍

在小程序中不能使用 HTML 标签&#xff0c;也就没有 DOM 和 BOM&#xff0c;CSS 也仅支持部分选择器 小程序提供了 WXML 进行页面结构的编写&#xff0c;WXSS 进行页面的样式编写 WXML 提供了 view、text、image、navigator等标签构建页面结构&#xff0c;小程序中标签称为组件…

[Linux]多线程详解

多线程 1.线程的概念和理解1.1线程的优点1.2线程的缺点1.3线程的设计1.4线程 VS 进程 2.线程控制2.1线程等待2.2 线程终止2.3 线程分离 3.线程互斥3.1背景3.2抢票代码演示3.3保护公共资源&#xff08;加锁&#xff09;3.3.1创建锁/销毁锁3.3.2申请锁/尝试申请锁/解锁 3.4解决抢…