《Linux系统编程》Linux 进程信号深度解析(上):信号的产生方式、本质和闹钟

🔥小叶-duck个人主页

❄️个人专栏《Data-Structure-Learning》《C++入门到进阶&自我学习过程记录》

《Linux操作系统从入门到实践》《Qt从入门到实践》

《算法题讲解指南》--优选算法

《算法题讲解指南》--递归、搜索与回溯算法

《算法题讲解指南》--动态规划算法

未择之路,不须回头
已择之路,纵是荆棘遍野,亦作花海遨游


目录

前言

[一. 信号的快速认知:从生活场景到技术本质](#一. 信号的快速认知:从生活场景到技术本质)

[1.1 生活角度理解信号](#1.1 生活角度理解信号)

[1.1.1 信号的本质](#1.1.1 信号的本质)

[1.1.2 生活中有哪些信号?](#1.1.2 生活中有哪些信号?)

[1.1.3 收到一个信号会立即处理它吗?](#1.1.3 收到一个信号会立即处理它吗?)

[1.1.4 信号怎么处理?](#1.1.4 信号怎么处理?)

[1.1.5 总结生活角度理解信号](#1.1.5 总结生活角度理解信号)

[1.2 技术视角的信号定义](#1.2 技术视角的信号定义)

[1.3 查看系统信号:kill -l 命令](#1.3 查看系统信号:kill -l 命令)

[二. 信号的产生:5 种核心方式](#二. 信号的产生:5 种核心方式)

[2.1 系统命令产生信号(kill 命令)](#2.1 系统命令产生信号(kill 命令))

[2.2 终端按键产生信号(键盘,最常用)](#2.2 终端按键产生信号(键盘,最常用))

[2.2.1 Ctrl+C:SIGINT(2 号信号)](#2.2.1 Ctrl+C:SIGINT(2 号信号))

补充知识:前台进程/后台进程及两者如何切换?

什么叫做给进程发送信号?发送信号的本质是什么?

[2.2.2 Ctrl+\:SIGQUIT(3 号信号)](#2.2.2 Ctrl+\:SIGQUIT(3 号信号))

[2.1.3 Ctrl+Z:SIGTSTP(20 号信号)](#2.1.3 Ctrl+Z:SIGTSTP(20 号信号))

[2.3 函数调用产生信号(编程触发)](#2.3 函数调用产生信号(编程触发))

[2.3.1 kill 函数:向指定进程发送信号](#2.3.1 kill 函数:向指定进程发送信号)

[2.3.2 raise 函数:向当前进程发送信号](#2.3.2 raise 函数:向当前进程发送信号)

[2.3.3 abort 函数:异常终止进程](#2.3.3 abort 函数:异常终止进程)

[2.3.4 三个系统调用层层递进](#2.3.4 三个系统调用层层递进)

OS先收到键盘输入,再由操作系统转换成信号发送给目标进程

[2.4 硬件异常产生信号(CPU / 内存错误)](#2.4 硬件异常产生信号(CPU / 内存错误))

[2.4.1 除零错误:SIGFPE(8 号信号)](#2.4.1 除零错误:SIGFPE(8 号信号))

[2.4.2 非法内存访问:SIGSEGV(11 号信号)](#2.4.2 非法内存访问:SIGSEGV(11 号信号))

[2.4.3 关键说明](#2.4.3 关键说明)

[2.5 软件条件产生信号(定时 / 管道异常)](#2.5 软件条件产生信号(定时 / 管道异常))

[2.5.1 管道破裂信号(SIGPIPE)](#2.5.1 管道破裂信号(SIGPIPE))

[2.5.2 alarm 函数:定时器信号(SIGALRM)](#2.5.2 alarm 函数:定时器信号(SIGALRM))

基本alarm验证-体会IO效率问题:

设置重复闹钟:

[2.5.3 关于 alarm 的应用场景和管理(先描述,再组织)](#2.5.3 关于 alarm 的应用场景和管理(先描述,再组织))

结束语


前言

在 Linux 系统中,信号是一种经典的进程间异步通信机制,也是操作系统响应异常、处理中断的核心手段。从用户在终端按下 Ctrl+C 终止前台进程,到程序因访问非法内存触发段错误崩溃,再到定时器超时向进程发送通知,这些常见场景的背后都是信号在发挥作用。简单来说,信号就是内核写给进程的一张"紧急便条",告知其某个事件已经发生。由于信号的到来是异步的,进程无法预知其具体时机,只能在收到通知后暂停当前工作并予以响应。本文将从生活类比、核心概念与实战用法三个维度,由浅入深地带你理解 Linux 信号的底层逻辑与具体操作。

一. 信号的快速认知:从生活场景到技术本质

1.1 生活角度理解信号

"快递收发" 的场景类比信号处理流程,瞬间理清核心逻辑:

  • 信号识别:你知道快递来了要取(进程能识别系统预设的信号,如SIGINT);
  • 信号产生:快递员打电话通知(信号由内核或其他进程发送);
  • 信号未决:你正在打游戏,5 分钟后才去取(信号产生后未立即处理,处于 "未决" 状态);
  • 信号递达:游戏结束后取快递(进程在合适时机执行信号处理动作);
  • 处理方式
    • 默认动作(打开快递进行使用);
    • 自定义动作(送给朋友);
    • 忽略(拿到快递但扔在一边)。

核心结论 :信号是异步通知,进程无法预知信号何时到来,但提前知道如何处理。

1.1.1 信号的本质

什么是通知?什么是异步?

  • 通知:这个通知需要我们理解一下------事件通知。
  • 异步:通知的到来,跟我不同步。
    举个例子,我点了个外卖,然后就打游戏,外卖小哥送货上门,敲门是一个通知,说明外卖到了,我和外卖小哥是不同步的。
    异步关系就是没关系(你做你的,我做我的);同步关系就是有关系(我得等你做完再做)。
  • 再比如,比如我是一个老师,我讲课,突然快递电话打过来了,我叫张三去帮我取快递,但是我等张三找完快递回来再继续讲,这就是个同步的关系,张三不回来,我就不往下讲。
  • 如果我继续讲课,张三也在找快递,我讲课、他帮我办事,这个就是个异步的关系------我讲我的课,张三找他要帮我找的快递。

1.1.2 生活中有哪些信号?

上面这些所有的信号产生的几乎都是异步的。

  • 人能够识别对应的、甚至还没有发生的这些信号

人为什么能够识别这些信号?

人是经过教育的,早就知道信号对应处理动作的对应关系。

操作系统给目标进程发送信号,目标进程能不能识别呢?

  • 答案是:进程天然能够识别------进程和信号都是程序员写的代码 (进程相当于我们人,已经被程序员教育 过了,程序员设计好了
    进程能够识别信号,并且已经知道怎么处理信号了。

1.1.3 收到一个信号会立即处理它吗?

当我们收到一个信号,准备处理这个信号,这里的我们即进程,会立即处理这个信号吗?有时候中断不了呢!处理不了信号。

  • 对于信号的处理------信号可能会立即处理 ,也可能在合适的时候会处理

既然有一定概率不会立即处理,那么得要有把信号临时保存 起来的能力------不会立即处理,就得有临时保存 的能力。

比如像生活中外卖小哥打电话,答应去取外卖之后如果没有把这件事记下来(保存信号) ,是不是就打游戏打着打着就忘了取外卖了。

1.1.4 信号怎么处理?

  • 默认:我后面打完游戏去拿外卖回来吃
  • 忽略:我直接就不想拿了(但是并不是忘了,只是不想)
  • 自定义:比如我拿完外卖直接丢了或者干别的事,自定义行为

以红绿信号灯为例:

  • 红灯------自觉遵守交通规则停下来------默认信号。
  • 收到信号,也处理了,但是处理的方式是忽略,也就是闯红灯------忽略信号。
  • 红灯亮了,别人要么忽略要么停下,而你在莫名其妙地跳舞------自定义信号。

1.1.5 总结生活角度理解信号

  • 设别信号是内置的,进程能自己识别信号,是内核程序员写的内置特性。
  • 信号的处理方法,在信号产生之前就已经准备好了。
  • 处理信号不是立即处理的,因为我可能正在做优先级更高 的事情,会选择合适的时候进行处理。比如我在打游戏,外卖员给我打电话叫拿外卖,那我肯定是先忙完手头的事再去拿。
  • 信号不会被立即处理 ,所以就注定了进程要有临时保存信号的能力!
  • 信号处理的动作有三种:默认、忽略、自定义 ,后续都叫信号捕捉

1.2 技术视角的信号定义

信号是 Linux 中进程间事件异步通知 的一种方式,属于**"软中断"** ------ 模拟硬件中断的行为(硬件中断发给 CPU,信号发给进程),用于处理突发事件(如用户中断程序异常定时触发等)。

关键特性:

  • 异步性:信号的产生与进程的控制流程无关,进程执行到任意位置都可能收到信号;
  • 预定义动作:每个信号都有默认处理动作(终止、忽略、Core Dump 等),进程可自定义处理逻辑;
  • 内核转发:信号的产生、发送、递达均由内核管理,进程仅需关注处理动作;
  • 前台进程专属Ctrl+C终端按键 产生的信号,仅发送给前台进程后台进程 需用kill命令发送)。

1.3 查看系统信号:kill -l 命令

Linux 系统支持 64 种信号(34 以下为常规信号34 以上为实时信号 ),通过kill -l可查看所有信号的编号和名称:

bash 复制代码
kill -l
# 输出示例(核心信号):
 1) SIGHUP       2) SIGINT        3) SIGQUIT       9) SIGKILL      11) SIGSEGV
13) SIGPIPE      14) SIGALRM      15) SIGTERM       17) SIGCHLD     20) SIGTSTP

核心信号说明(后续都会进行讲解)

信号编号 信号名称 产生场景 默认动作
2 SIGINT 按下 Ctrl+C 终止进程
3 SIGQUIT 按下 Ctrl+\ 终止进程 + Core Dump
6 SIGABRT abort 函数调用 终止进程 + Core Dump
8 SIGFPE 算术异常(如除零) 终止进程 + Core Dump
9 SIGKILL kill -9 PID 命令 强制终止进程(不可捕捉 / 忽略)
11 SIGSEGV 非法内存访问(野指针) 终止进程 + Core Dump
14 SIGALRM alarm 函数超时 终止进程
15 SIGTERM kill PID 命令默认信号 终止进程(可捕捉)
19 SIGSTOP kill -STOP PID 暂停进程(不可捕捉 / 忽略)
20 SIGTSTP 按下 Ctrl+Z 暂停进程

二. 信号的产生:5 种核心方式

前置储备

2.1 系统命令产生信号(kill 命令)

kill命令是发送信号的常用工具,本质是调用kill系统函数,语法:

bash 复制代码
kill -信号编号 进程PID
kill -信号名称 进程PID

实战案例:用 kill 命令发送 SIGSEGV 信号

cpp 复制代码
// 程序:死循环运行,等待外部信号
#include <iostream>
#include <unistd.h>

int main() 
{
    std::cout << "进程PID:" << getpid() << ",等待信号(用kill命令测试)..." << std::endl;
    while (true) 
    {
        sleep(1);
    }
    return 0;
}

操作步骤

  • 编译运行程序,记录 PID(如 213784);
  • 打开新终端,发送 SIGSEGV(11 号信号,段错误):
bash 复制代码
kill -11 213784
# 或 kill -SIGSEGV 213784

原终端输出:Segmentation fault (core dumped),进程终止。

2.2 终端按键产生信号(键盘,最常用)

终端通过组合键产生预设信号,用于控制前台进程,核心组合键及对应信号:

2.2.1 Ctrl+C:SIGINT(2 号信号)

默认动作 :终止前台进程,可通过signal 函数自定义捕捉

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

// 自定义信号处理函数
void sigint_handler(int signum) 
{
    std::cout << "\n进程[" << getpid() << "]捕获到SIGINT信号(编号:" << signum << "),未终止!" << std::endl;
}

int main() 
{
    std::cout << "进程PID:" << getpid() << ",等待信号(按Ctrl+C测试)..." << std::endl;
    // 注册SIGINT信号的处理函数
    signal(SIGINT, sigint_handler);
    // 死循环等待信号
    while (true) 
    {
        sleep(1);
        std::cout << "运行中..." << std::endl;
    }
    return 0;
}

编译运行

bash 复制代码
g++ sigint_demo.cc -o sigint_demo
./sigint_demo

效果 :按下Ctrl+C 后,进程不会终止 ,而是执行自定义处理函数也就是打印出一段文字并继续运行。

补充知识:前台进程/后台进程及两者如何切换?
什么叫做给进程发送信号?发送信号的本质是什么?

2.2.2 Ctrl+\:SIGQUIT(3 号信号)

默认动作:终止进程 + 生成 Core Dump 文件(用于事后调试),同样可自定义捕捉。

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

void sigquit_handler(int signum) 
{
    std::cout << "\n进程[" << getpid() << "]捕获到SIGQUIT信号(编号:" << signum << ")" << std::endl;
}

int main() 
{
    std::cout << "进程PID:" << getpid() << ",等待信号(按Ctrl+\\测试)..." << std::endl;
    signal(SIGQUIT, sigquit_handler);
    while (true) 
    {
        sleep(1);
    }
    return 0;
}

关键说明:Core Dump 文件默认关闭,可通过ulimit -c 1024开启(允许最大 1024KB 的 Core 文件),调试时用gdb ./程序名 core.进程号分析。

2.1.3 Ctrl+Z:SIGTSTP(20 号信号)

默认动作:暂停前台进程 ,将其挂入后台 ,可通过 fg 命令 恢复前台运行 ,也可以通过bg 把这个后台进程运行起来

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

void sigtstp_handler(int signum) 
{
    std::cout << "\n进程[" << getpid() << "]捕获到SIGTSTP信号(编号:" << signum << ")" << std::endl;
}

int main() 
{
    std::cout << "进程PID:" << getpid() << ",等待信号(按Ctrl+Z测试)..." << std::endl;
    signal(SIGTSTP, sigtstp_handler);
    while (true) 
    {
        sleep(1);
        std::cout << "运行中..." << std::endl;
    }
    return 0;
}

后台操作示例

bash 复制代码
# 运行程序后按Ctrl+Z,进程暂停
[1]+  Stopped                 ./sigtstp_demo
# 查看后台进程
jobs
# 将后台进程恢复到前台
fg 1

# 将后台进程运行起来
# bg 1

2.3 函数调用产生信号(编程触发)

通过系统函数主动发送信号,核心函数包括kill、raise、abort,适用于编程场景下的信号触发。

2.3.1 kill 函数:向指定进程发送信号

函数原型:

cpp 复制代码
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
  • **pid:**目标进程 PID(正数);进程组 ID(负数,发送给组内所有进程);0(发送给当前进程组);-1(发送给所有有权限的进程);
  • sig: 信号编号(0 表示检测进程是否存在,不发送信号);
  • **返回值:**成功返回 0,失败返回 - 1。

实战:实现简易版 kill 命令(我最开始给的那个mykill也是可以的)

cpp 复制代码
#include <iostream>
#include <signal.h>
#include <string>

//执行进程进行kill指令

//int kill(pid_t pid, int sig);
//./mykill signumber pid
int main(int argc, char *argv[])
{
    if(argc != 3)
    {
        std::cout << "./mykill signumber pid" << std::endl;
        return 1;
    }
    int signum = std::stoi(argv[1]);
    pid_t target = std::stoi(argv[2]);

    int n = kill(target, signum);
    if(n == 0)
    {
        std::cout << "send " << signum << "to " << "pid:" << target << " success" << std::endl;
    }
    else
    {
        std::cout << "kill error" << std::endl;
        return 2;
    }
    return 0;
}

2.3.2 raise 函数:向当前进程发送信号

函数原型:

cpp 复制代码
#include <signal.h>
int raise(int sig);
  • 作用 :自己给自己发送信号,等价于kill(getpid(), sig)
  • 返回值:成功返回 0,失败返回非 0。

实战案例利用raise函数查找1-31号信号有哪些信号无法被自定义捕捉

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

void handlerSig(int sig)
{
    std::cout << "获得了一个信号: " << sig << std::endl;
    sleep(1);
}

int main()
{
    // signal(SIGINT, handlerSig);

    // 将1 - 31所有常规信号全部进行自定义处理操作
    for (int i = 1; i < 32; i++)
    {
        signal(i, handlerSig);
    } // 判断一个进程通过这个操作是否将无法被杀死

    // 通过上面代码我们碰巧发现9信号是无法被自定义捕捉的
    // 那我们能不能将所有不能自定义捕捉的信号找到呢?可以的->raise函数
     for(int i = 1; i < 32; i++)
     {
         sleep(1);
         if(i == 9) continue; //跳过不可自定义捕捉的9信号
         raise(i); //自己给自己指定发送对应信号
     }
    return 0;
}

2.3.3 abort 函数:异常终止进程

函数原型:

cpp 复制代码
#include <stdlib.h>
void abort(void);
  • 作用 :向当前进程发送 SIGABRT(6 号信号 ),强制异常终止 ,不可被忽略或自定义捕捉;但是他能执行自定义的操作 只不过最后依旧会终止
  • 无返回值必然终止进程)。

实战案例

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

void handlerSig(int sig)
{
    std::cout << "获得了一个信号: " << sig << std::endl;
    sleep(1);
}

int main()
{
    // signal(SIGINT, handlerSig);

    // 将1 - 31所有常规信号全部进行自定义处理操作
    for (int i = 1; i < 32; i++)
    {
        signal(i, handlerSig);
    } // 判断一个进程通过这个操作是否将无法被杀死

    int cnt = 0;
    while (true)
    {
        std::cout << "hello world, " << cnt++ << ", pid:" << getpid() << std::endl;
        abort();
        sleep(3);
    }
    return 0;
}

2.3.4 三个系统调用层层递进

前面的kill、raise、abort 三个系统调用我们发现是层层递进的!

OS先收到键盘输入,再由操作系统转换成信号发送给目标进程

接下来,先不往后面看,先复盘一个问题:

我们已经说过啦,必须只能是OS!

只有操作系统 可以在内核操作系统里面对PCB内部的位图进行修改

换句话说,通过键盘产生信号也并不是键盘直接产生的信号,必然是OS先收到键盘的输入的,再由操作系统转换成信号发送给目标进程!

2.4 硬件异常产生信号(CPU / 内存错误)

由硬件检测到的异常触发,内核将其解释为对应信号发送给进程,所有硬件异常信号基本上均触发 Core Dump(后续有补充) ,核心场景包括除零错误(SIGFPE)非法内存访问(SIGSEGV),也就是8号和11号信号

2.4.1 除零错误:SIGFPE(8 号信号)

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

void sigfpe_handler(int signum) 
{
    std::cout << "捕获到SIGFPE信号(编号:" << signum << "),除零错误!" << std::endl;
    // 注意:除零后CPU状态未清理,信号会持续触发,需退出进程
    exit(1);
}

int main() 
{
    signal(SIGFPE, sigfpe_handler);
    std::cout << "模拟除零错误..." << std::endl;
    sleep(1);
    int a = 10;
    a /= 0; // 除零,触发SIGFPE
    return 0;
}

保留问题:进程怎么知道硬件出错的?

2.4.2 非法内存访问:SIGSEGV(11 号信号)

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

void sigsegv_handler(int signum) 
{
    std::cout << "捕获到SIGSEGV信号(编号:" << signum << "),非法内存访问!" << std::endl;
    exit(1);
}

int main() 
{
    signal(SIGSEGV, sigsegv_handler);
    std::cout << "模拟野指针访问..." << std::endl;
    sleep(1);
    int* p = nullptr;
    *p = 100; // 访问空指针,触发SIGSEGV
    return 0;
}

2.4.3 关键说明

硬件异常产生的信号,本质是 CPU 或硬件检测到错误后,通知内核,再由内核转化为信号发送给进程:

  • 除零错误:CPU 运算单元检测到异常,状态寄存器标记错误,内核解释为 SIGFPE;

  • 非法内存访问:MMU(内存管理单元)检测到地址无效,内核解释为 SIGSEGV;

  • 若不退出进程,异常状态会持续存在,导致信号反复触发而死循环

2.5 软件条件产生信号(定时 / 管道异常)

软件内部 状态触发的信号,核心场景包括定时器超时(alarm函数) 、**管道破裂(SIGPIPE)**等。

2.5.1 管道破裂信号(SIGPIPE)

当向无读端 的管道写入数据 时,内核会向写进程发送SIGPIPE(13 号信号),默认动作是终止进程。

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

void sigpipe_handler(int signum) 
{
    std::cout << "捕获到SIGPIPE信号(编号:" << signum << "),管道破裂!" << std::endl;
    exit(1);
}

int main() 
{
    int pipefd[2];
    pipe(pipefd); // 创建管道
    close(pipefd[0]); // 关闭读端
    signal(SIGPIPE, sigpipe_handler);
    
    char buf[1024] = "hello";
    write(pipefd[1], buf, sizeof(buf)); // 向无读端的管道写数据,触发SIGPIPE
    return 0;
}

2.5.2 alarm 函数:定时器信号(SIGALRM)

函数原型:

cpp 复制代码
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
  • 作用设置定时器 ,seconds秒后向当前进程发送 SIGALRM(14 号信号)
  • 返回值:0(无之前的定时器)或之前定时器的剩余秒数;
  • 特性:一个进程同时只能有一个活跃的alarm定时器,重复调用会覆盖之前的设置。
基本alarm验证-体会IO效率问题:
cpp 复制代码
#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>

int cnt = 0;
void handlerSig(int sig)
{
    std::cout << "获得了一个信号: " << sig << " cnt: " << cnt << std::endl;
    exit(13);
}

//IO效率问题
int main()
{
    signal(SIGALRM, handlerSig);
    alarm(1); //设定1s闹钟,1s之后,当前进程就会收到一个信号
    //int cnt = 0;

    // //循环中有IO操作:std::cout、std::endl
    // while (true)
    // {
    //     std::cout << "hello world, " << cnt++ << std::endl;
    // }

    //循环中没有IO操作
    while (true)
    {
        cnt++;
    }
    return 0;
}
设置重复闹钟:
cpp 复制代码
#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <functional>
#include <vector>

using func_t = std::function<void()>; // 包装器
std::vector<func_t> funcs;

int gcount = 0;
void handlerSig(int sig)
{
    // std::cout << "获得了一个信号: " << sig << " , pid: " << getpid() << std::endl;
    std::cout << "###############################" << std::endl;
    for (auto &f : funcs)
    {
        f();
    }
    std::cout << "gcount : " << gcount << std::endl;
    int n = alarm(1); // 重设闹钟,会返回上⼀次闹钟的剩余时间
    std::cout << "剩余时间 : " << n << std::endl;
    std::cout << "###############################" << std::endl;
}

int main()
{
    // lambda表达式插入任务
    funcs.push_back([]()
                    { std::cout << "我是一个进程调度" << std::endl; });
    funcs.push_back([]()
                    { std::cout << "我是⼀个检测进程时间⽚的操作,如果时间⽚到了,我会切换进程" << std::endl; });
    funcs.push_back([]()
                    { std::cout << "我是⼀个内存管理操作,定期清理操作系统内部的内存碎⽚" << std::endl; });

    signal(SIGALRM, handlerSig);
    alarm(1); // ⼀次性的闹钟,超时alarm会自动被取消,后续不再被触发

    while (true)
    {
        pause();
        std::cout << "我醒来了..." << std::endl;
        gcount++;
    }
    // 这就是操作系统的运行原理
    return 0;
}

2.5.3 关于 alarm 的应用场景和管理(先描述,再组织)

结束语

本章节我们从生活场景切入,逐层讲解了信号的概念、系统指令、五大产生方式以及各类典型信号的特性与使用场景。信号是 Linux 进程间交互、异常处理的核心机制,也是后端开发与系统编程的必备基础。希望大家结合理论多加动手实践,熟练掌握信号的收发逻辑与使用规则,为后续学习筑牢根基。

下一篇我们将继续讲解信号的保存递达,深入剖析未决信号集、阻塞信号集以及信号捕捉流程等底层逻辑。

相关推荐
Dxy12393102161 小时前
BAT 窗口不输出日志:三种静默方案,从半隐藏到完全消失
linux·运维·服务器
Tian_Hang2 小时前
Linux基础知识(一)
linux·运维·服务器
行智科技2 小时前
ORB-SLAM3代码详解 - 第 01 篇 · 系统总览与三线程架构
linux·ubuntu·架构·自动驾驶
fishwww_ww3 小时前
服务器免密登录与流量端口转发
linux
开开心心_Every4 小时前
解决打印机共享难题的实用工具
linux·b树·安全·游戏·随机森林·pdf·计算机外设
江华森6 小时前
操作系统与 Linux 内核实战教程
linux·运维·服务器
齐潇宇6 小时前
Redis数据库基础
linux·数据库·redis·缓存
嵌入式学习和实践6 小时前
Ubuntu 系统 socat 详细介绍与使用教程 - 映射任意两种数据通道
linux·ubuntu·虚拟串口·数据映射·socat
小此方6 小时前
Re:Linux系统篇(二十八)文件篇·一:理解 Linux 文件基础I/O、Linux 文件操作与系统调用机制
linux·运维·服务器