Linux---进程信号

一、预备

1.信号

①物理:闹钟,红绿灯,上课铃声,狼烟,电话铃声,敲门声......

②什么叫信号:终端人正在做的事情,是一种事件的异步通知机制

③技术层面:信号是一种给进程发送的,用来进行事件异步通知的机制

④异步:两件事情同步进行互不干扰,信号的产生,先对于进程的运行,是异步的!

2.基本结论

①信号处理,进程在信号没有产生的时候,早就直到信号该如何处理了

②信号的处理,不是立即处理,二十可以等一会再处理,合适的时候,进行信号的处理

③人能识别信号,是被提前"教育"过的,进程也是如此,OS程序员设计的进程,进程早已内置了对于信号的识别和处理方式!

④信号源非常多->给进程产生信号的信号源也非常多。

二、信号的产生

1.键盘产生信号

①信号有哪些

其中1~31是普通信号,34~64是实时信号,无需我们考虑

②ctrl+c:向目标进程发送二号信号(SIGINT)

Ⅰ代码演示

Ⅱ ctrl+c是给目标进程发送信号,相当一部分信号的处理动作,就是让自己终止

③信号的三种操作:进程收到信号之后,合适的时候,处理信号的动作有三种

Ⅰ默认处理动作(大部分信号默认处理动作是终止)

Ⅱ自定义处理动作

Ⅲ忽略处理

⑤前台进程和后台进程

Ⅰ前台进程(./test):键盘产生的进程,只能发给前台进程

当运行程序时,输入ls,pwd等指令没有结果,因为前台进程只能有一个,系统运行./test就不能运行bash进程了

Ⅱ后台进程(./test &):ctrl+c无法相应

运行程序时,bash为前台进程,所以输入命令时可以执行

Ⅲ区别与共性

前台进程能从键盘上获取标准输入,后台进程无法从标准输入中获取内容;但是都可以向标准输出上打印

Ⅳ原因

键盘只有一个,输入数据一定是给确定的进程的,前台进程必须只有一个,后台进程可以有多个,

前台的本质就是要从键盘获取数据的

Ⅴ相互转变

jobs:查询后台任务号

fg+任务号:后台变前台

ctrl+z:暂停前台任务,自动变后台

bg+任务号:暂停后运行后台任务

⑥什么叫做给进程发送信号

信号产生后,并不是立即处理,所以要求进程必须把信号记录下来,在合适的时候处理!

再task_struct中使用位图结构:收到几号信号将那位置1

发送信号本质就是向目标进程写信号(修改位图,pid和信号编号)

2.系统调用

①相关接口

Ⅰsignal:修改信号操作

Ⅱ kill: 杀死目标进程中的信号

Ⅲ raise:指明发送给信号

Ⅳ abort:使当前进程接收到信号而异常终止,使用6号信号,要求进程必须处理,忽视信号捕捉

②演示

sigal

mykill

二者同时运行

③9号和19号信号无法被捕捉

3.异常产生信号

①示例

Ⅰ除0

8号SIGFPE:浮点数错误

Ⅱ野指针

11号SIGSEGV:段错误

②本质使硬件无法产生页表无法对应从而返回错误

4.软件条件(SIGPIPE)

①alarm:闹钟,时间到向OS发送信号,默认终止

alarm(0):取消闹钟

14号信号SIGALRM

②代码演示

操作系统的执行方法,while循环从开机开始就不停

cpp 复制代码
#include <functional>
#include <iostream>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <vector>

void func1() {
    std::cout << "方法一" << std::endl;
}
void func2() {
    std::cout << "方法二" << std::endl;
}
void func3() {
    std::cout << "方法三" << std::endl;
}

using func_t = std::function<void()>;
std::vector<func_t> funcs;

void handlerSig(int sig) {
    std::cout << "###############" << std::endl;
    for (auto f : funcs)
        f();
    std::cout << "###############" << std::endl;
    int n = alarm(1);
}
int main() {
    funcs.push_back(func1);
    funcs.push_back(func2);
    funcs.push_back(func3);

    signal(SIGALRM,handlerSig);
    alarm(2);
    while (true) {
        pause();//暂停
    }
}

三、信号的保存

1.预备

①实际执⾏信号的处理动作称为信号递达(Delivery)

②信号从产⽣到递达之间的状态,称为信号未决(Pending)。
③进程可以选择阻塞 (Block )某个信号。
④被阻塞的信号产⽣时将保持在未决状态,直到进程解除对此信号的阻塞,才执⾏递达的动作.
⑤注意,阻塞和忽略是不同的,只要信号被阻塞就不会递达,⽽忽略是在递达之后可选的⼀种处理动
作。

2.理解

在一个进程pcb中有三张表

①pending表(未决表):保存收到的信号的位图,bit位的位置表示第几个信号,内容(0或1)表示是否收到

②block表:bit位的位置表示第几个信号,bit的内容表示是否阻塞

③handler表:是一个函数指针数组,下标表示信号,当使用signal函数时,第二个参数就是传入这个表,有默认,自定义,忽略三种方式,使用signal就是自定义

3.sigprocmask

调用函数可以读取或者更改进程的信号屏蔽字

①how

how核心控制参数 ,用于指定 sigprocmask 如何修改当前进程的信号掩码,必须传入以下 3 个预定义常量之一,决定了 set 中信号集的使用逻辑:

②set

set 是一个指向 sigset_t 类型的指针,sigset_t 是系统定义的信号集数据结构(可理解为 "存储多个信号的容器"),用于指定要 "阻塞 / 解除阻塞 / 替换" 的具体信号。

关键说明:

  • set 的值取决于 how 的作用:

    • howSIG_BLOCK/SIG_UNBLOCK 时,set 需包含要 "添加阻塞" 或 "解除阻塞" 的信号;
    • howSIG_SETMASK 时,set 就是新的完整信号掩码;
    • setNULL,表示 "不修改信号集",仅通过 oldset 获取当前掩码(此时 how 参数会被忽略)。

③oldset(输出型参数)

oldset 是一个指向 sigset_t 类型的指针,用于保存修改前的 "旧信号掩码" (即调用 sigprocmask 前进程的信号掩码状态)。

关键说明:

  • oldset 不为 NULL:系统会将修改前的信号掩码写入 oldset 指向的内存,便于后续恢复原状态(例如临时阻塞信号后,需还原为原来的阻塞规则);
  • oldsetNULL:表示 "不需要保存旧掩码",仅执行修改操作。

4.sigpending

①功能

  1. 核心概念:待处理信号(Pending Signal)

在理解 sigpending 前,需先明确 "待处理信号" 的定义:

  • 当一个信号被发送给进程时,系统会先检查该信号是否在进程的信号掩码中(即是否被阻塞);

  • 未被阻塞:进程会立即处理该信号(执行信号处理函数或默认行为);

  • 已被阻塞:信号不会被立即处理,而是被标记为 "待处理" 状态,暂存于进程的 "待处理信号队列" 中;

  • 当该信号从信号掩码中被解除阻塞(如通过 sigprocmask(SIG_UNBLOCK))后,进程会立即处理这个待处理信号。

注意:每个信号类型(如 SIGINT、SIGQUIT)在待处理队列中最多只有一个实例------ 若同一信号被多次发送且均被阻塞,只会记录一次,解除阻塞后也仅处理一次。

②参数:sigset_t *set(输出型参数)

set 是一个指向 sigset_t 类型(信号集)的指针,其作用是接收进程当前所有待处理信号的集合

5.sig集合

①sigemptyset

  • 功能:初始化一个信号集,将该信号集中所有信号的对应标志位都设置为 0,即清空信号集,使其不包含任何信号。
  • 返回值:成功时返回 0;若出现错误,返回 - 1。

②sigfillset

  • 功能:将信号集中的所有信号对应的标志位都设置为 1,也就是使该信号集包含系统支持的所有信号。
  • 返回值:成功时返回 0;若出现错误,返回 - 1。

③sigaddset

  • 功能 :向指定的信号集 set 中添加一个特定的信号。signum 参数表示要添加的信号编号,比如常见的 SIGINT(信号编号通常为 2,代表终端中断信号)、SIGTERM(终止信号)等。
  • 返回值 :成功时返回 0;若出现错误(例如 signum 不是一个有效的信号编号,或者 set 指针无效等情况),返回 - 1。

④sigdelset

  • 功能 :从指定的信号集 set 中删除一个特定的信号。signum 是要删除的信号编号。
  • 返回值 :成功时返回 0;若出现错误(比如 signum 不是一个有效的信号编号,或者 set 指针无效,又或者要删除的信号原本就不在信号集中 ),返回 - 1。

⑤sigismember

  • 功能 :检查指定的信号 signum 是否存在于信号集 set 中。这是一个查询函数,用于判断某个信号是否被包含在特定的信号集中。
  • 返回值 :如果信号 signum 在信号集 set 中,返回 1;如果信号 signum 不在信号集 set 中,返回 0 ;若出现错误(比如 set 指针无效),返回 - 1。

⑥代码使用

cpp 复制代码
#include <iostream>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>

void PrintPending(sigset_t &pending) {
    printf("我是一个进程(%d), pending: ", getpid());
    for (int signo = 31; signo > 0; signo--) {
        if (sigismember(&pending, signo)) {
            std::cout << "1";
        } 
        else {
            std::cout << "0";
        }
    }
    std::cout << std::endl;
}

// 先将2号信号进行屏蔽,然后不断获取pending表,然后向目标进程发送信号

int main() {
    // 1.屏蔽二号信号
    sigset_t block, oblock;
    sigemptyset(&block);
    sigemptyset(&oblock); // 清空

    sigaddset(&block, SIGINT); // 添加信号
    sigprocmask(SIG_SETMASK, &block, &oblock);

    int cnt = 0;
    while (true) {
        // 获取pending集合
        sigset_t pending;
        sigpending(&pending);

        PrintPending(pending);
        sleep(1);
        cnt++;
    }

    return 0;
}

6.核心转储

Action中有些时Trem有些时Core,他们都是终止信号,但是有差异

  • Term:单纯的终止进程,没有多余的动作
  • Core:先进性核心转储,再终止进程

核心转储及其作用

核心转储(core dump)是指在程序发生严重错误导致异常终止时,操作系统将程序的内存内容以及相关的调试信息保存到一个特殊的文件中,以供后续分析和调试使用。这个文件通常被称为核心转储文件或核心文件。

核心转储文件包含了程序崩溃时内存的快照,以及与进程相关的其他信息,如寄存器状态、调用栈、变量值等。这对于开发人员来说是非常有价值的,因为它提供了关于程序崩溃原因的详细信息,有助于识别和调试问题。

核心转储文件通常以 "core" 或者在某些系统中以进程ID为文件名的形式保存在程序当前工作目录或系统指定的核心转储文件目录中。它们对于排查由于程序错误、内存损坏或其他异常情况引起的问题非常有用。

有一些关键的概念和注意事项与核心转储相关:

ulimit 设置: 操作系统可能会设置 ulimit(用户资源限制)来限制核心转储文件的大小,以避免占用过多磁盘空间。

调试符号: 为了更好地解析核心转储文件,通常需要保留程序的调试符号。调试符号是编译时信息,包含了程序源代码的映射关系,有助于将内存地址映射回源代码。

调试工具: 使用调试工具(如gdb)可以加载核心转储文件,并允许开发人员分析崩溃时的状态、查看堆栈跟踪,以及检查变量值等。

产生核心转储: 在Unix-like系统中,可以通过在程序中调用 ulimit 设置允许生成核心转储文件,或者在终端运行程序时使用 ulimit -c unlimited 临时修改。

再见

相关推荐
Abstracod3 小时前
centos7 安装病毒扫描软件ClamAV
服务器
Sopaco4 小时前
告别项目文档滞后:Litho(deepwiki-rs)在CI/CD中的自动化文档生成实践
运维·ci/cd·自动化
Maple_land4 小时前
Linux进程第五讲:PPID与bash的关联、fork系统调用的原理与实践操作(上)
linux·运维·服务器·centos·bash
风为你而吹5 小时前
【玩泰山派】4、制作ubuntu镜像-(6)使用鲁班猫的sdk去制作镜像
linux·运维·ubuntu
running thunderbolt5 小时前
项目---网络通信组件JsonRpc
linux·服务器·c语言·开发语言·网络·c++·性能优化
影子24015 小时前
Windows Server2016 服务器安装JDK,一直卡在“应用程序正在为首次使用作准备,请稍候” ,导致jdk安装失败解决方案
运维·服务器·windows·jdk
养生技术人6 小时前
Oracle OCP认证考试题目详解082系列第48题
运维·数据库·sql·oracle·database·开闭原则·ocp
_OP_CHEN6 小时前
Linux 系统编程:(一)从历史演进到 XShell 远程登录实操
linux·运维·服务器·centos·unix·xshell
为java加瓦7 小时前
Rust 的类型自动解引用:隐藏在人体工学设计中的魔法
java·服务器·rust