linux学习进展 信号

在Linux进程管理中,信号(Signal)是最基础、最核心的进程间通信方式,也是内核向进程传递"事件通知"的唯一途径。无论是我们日常使用的Ctrl+C终止程序,还是子进程退出时通知父进程,亦或是程序异常崩溃(如段错误),本质上都是信号在发挥作用。结合之前学的fork、exec进程控制,以及系统调用与库函数的区别,本节课将彻底讲透Linux信号------从信号的本质、常见信号,到信号的处理方式、实操函数,再到信号在进程回收、异常处理中的实际应用,帮大家打通进程控制的最后一环,为后续网络编程、后台服务开发打下基础。

一、先搞懂:什么是信号?(通俗+本质)

简单来说,信号是Linux内核发送给进程的"通知",用来告知进程发生了某个事件,要求进程做出相应的响应。信号就像我们生活中的"门铃"------不用一直盯着门口(轮询),门铃响了(收到信号),就知道有人来了(有事件发生),再去开门(处理信号)。

(一)信号的核心本质

信号的本质是一个整数编号 (每个信号对应唯一的编号),内核通过向进程发送这个"编号",传递事件信息。进程收到信号后,会根据自身的配置,选择"忽略信号""执行默认动作"或"执行自定义处理函数",整个过程是异步的------进程无需主动等待信号,正常执行自身逻辑即可,收到信号后再中断当前逻辑,处理信号。

关键补充:信号是"软中断",与硬件中断(如键盘输入、磁盘IO)类似,会中断进程当前的执行流程,优先处理信号,处理完成后再回到原来的执行位置(除非信号导致进程终止)。

(二)信号的产生场景(高频场景)

日常开发和使用中,信号的产生主要有5种场景,结合我们已学知识,很容易理解:

  1. 用户输入触发 :最常见的场景,比如Ctrl+C(发送SIGINT信号)终止前台进程,Ctrl+\(发送SIGQUIT信号)终止进程并生成核心转储文件。

  2. 进程异常触发:程序运行出错时,内核自动发送信号,比如非法内存访问(段错误,SIGSEGV)、除零错误(SIGFPE)、总线错误(SIGBUS)。

  3. 进程间主动发送:通过系统调用(kill、raise),一个进程向另一个进程发送信号,比如父进程发送SIGTERM信号,优雅终止子进程。

  4. 定时器触发:通过alarm系统调用设置定时器,超时后内核向进程发送SIGALRM信号,默认终止进程。

  5. 内核通知触发:进程触发某些内核事件时,内核发送信号,比如子进程退出时,内核向父进程发送SIGCHLD信号(默认忽略,需手动处理以避免僵尸进程)。

(三)信号的生命周期(核心流程)

一个信号从产生到被处理,分为4个阶段,理解这个流程,能避免后续学习信号处理时踩坑:

  1. 产生:通过上述5种场景,内核生成一个信号(本质是整数编号),标记给目标进程。

  2. 递达:内核将信号传递给目标进程,此时进程知道有信号需要处理(信号处于"待处理"状态)。

  3. 阻塞:进程可以通过设置"信号屏蔽字",暂时阻止某个信号被递达(信号会被暂存,直到阻塞解除后再递达)。注意:阻塞≠忽略,忽略是信号递达后不处理,阻塞是信号不递达。

  4. 处理:进程收到递达的信号后,执行预设的处理动作(默认、忽略、自定义),处理完成后,恢复原执行流程。

核心提醒:有两个信号无法被阻塞、忽略、自定义处理------SIGKILL(编号9)和SIGSTOP(编号19),这两个信号只能执行默认动作(SIGKILL强制终止进程,SIGSTOP暂停进程),目的是为了让管理员能强制控制进程(比如杀死无法正常终止的进程)。

二、Linux常见信号(必记,面试高频)

Linux系统中共有64种信号(编号1~64),其中前31种是"标准信号"(常用、稳定),后32种是"实时信号"(用于实时系统,日常开发较少用到)。我们重点掌握前31种中最常用的10种,结合其编号、含义、默认动作记忆,搭配场景理解更高效。

(一)常用标准信号汇总表

信号编号 信号名称 核心含义 默认动作 常见场景
1 SIGHUP 终端挂起或进程脱离终端 终止进程 终端关闭时,通知后台进程终止
2 SIGINT 中断信号(用户触发) 终止进程 按下Ctrl+C,终止前台进程
3 SIGQUIT 退出信号(用户触发) 终止进程 + 核心转储 按下Ctrl+\,终止进程并生成core文件(用于调试)
9 SIGKILL 强制终止信号 强制终止进程 sudo kill -9 PID,强制杀死进程(无法拦截)
11 SIGSEGV 段错误(非法内存访问) 终止进程 + 核心转储 指针越界、访问空指针、非法修改只读内存
14 SIGALRM 定时器超时信号 终止进程 alarm(5)设置5秒超时,超时后触发
15 SIGTERM 优雅终止信号 终止进程 kill PID(默认发送此信号),允许进程清理资源后终止
17 SIGCHLD 子进程状态改变(退出/暂停) 忽略信号 子进程退出时,内核通知父进程(需手动处理回收僵尸进程)
19 SIGSTOP 暂停进程 暂停进程 kill -19 PID,暂停进程,用SIGCONT(18)恢复
18 SIGCONT 恢复暂停的进程 恢复进程运行 kill -18 PID,恢复被SIGSTOP暂停的进程

(二)关键补充:核心转储(Core Dump)

上述信号中,SIGQUIT、SIGSEGV等信号的默认动作包含"核心转储"(Core Dump),这里重点补充:

核心转储是指进程异常终止时,内核将进程当前的内存快照(代码段、数据段、堆、栈、CPU寄存器状态等)保存到一个名为core(或core.PID)的文件中,用于后续调试------通过gdb工具分析core文件,可以快速定位程序崩溃的原因(比如段错误的具体位置)。

注意:Linux系统默认关闭核心转储功能(避免生成过大的core文件占用磁盘),可通过以下命令临时启用:

bash 复制代码
ulimit -c unlimited  # 临时允许生成core文件(大小无限制)
ulimit -c 10240     # 可选:设置core文件最大大小(单位:块,1块=512字节)

查看core文件是否启用:ulimit -c,返回0表示未启用,返回unlimited表示已启用。

(三)查看所有信号的命令

日常开发中,可通过以下命令查看系统所有信号的详细信息(编号、名称、默认动作):

bash 复制代码
kill -l  # 查看所有信号的编号和名称(最常用)
man 7 signal  # 查看信号的详细手册(包含默认动作、场景说明)

三、信号的三种处理方式(核心重点)

进程收到信号后,有且只有三种处理方式,开发者可根据需求选择,这也是信号实操的核心:

(一)默认处理(SIG_DFL)

这是最常见的处理方式------进程不对信号做任何自定义配置,收到信号后,执行内核预设的默认动作(如终止、暂停、忽略、核心转储)。

示例:我们编写一个死循环程序,按下Ctrl+C(发送SIGINT信号),进程会执行默认动作(终止),这就是默认处理。

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

int main() {
    printf("进程运行中,PID:%d,按下Ctrl+C终止\n", getpid());
    while (1) {  // 死循环,等待信号
        sleep(1);
    }
    return 0;
}

(二)忽略信号(SIG_IGN)

进程通过配置,忽略指定的信号------收到信号后,不执行任何动作,继续执行自身逻辑,相当于"没收到信号"。

注意:SIGKILL和SIGSTOP无法被忽略,即使设置忽略,内核也会执行默认动作。

示例:忽略SIGINT信号(Ctrl+C),按下Ctrl+C后,进程不会终止,继续运行:

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

int main() {
    // 设置忽略SIGINT信号
    signal(SIGINT, SIG_IGN);
    printf("进程运行中,PID:%d,按下Ctrl+C无效(已忽略SIGINT)\n", getpid());
    while (1) {
        sleep(1);
    }
    return 0;
}

补充:此时若想终止进程,需使用kill -9 PID(发送SIGKILL信号),因为SIGKILL无法被忽略。

(三)自定义处理(捕捉信号)

这是开发中最常用的处理方式------进程自定义一个"信号处理函数",收到指定信号后,不执行默认动作,而是执行我们编写的处理函数,实现灵活的信号响应(如资源清理、日志记录、进程重启)。

实现方式:通过signal或sigaction系统调用,将信号与自定义处理函数绑定(sigaction比signal更可靠、灵活,推荐使用)。

示例:捕捉SIGINT信号,按下Ctrl+C后,不终止进程,而是打印提示信息:

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

// 自定义信号处理函数(参数是收到的信号编号)
void sigint_handler(int signum) {
    printf("\n收到SIGINT信号(编号:%d),不终止进程,继续运行!\n", signum);
}

int main() {
    // 绑定SIGINT信号和自定义处理函数
    signal(SIGINT, sigint_handler);
    printf("进程运行中,PID:%d,按下Ctrl+C测试自定义处理\n", getpid());
    while (1) {
        sleep(1);
    }
    return 0;
}

运行结果:按下Ctrl+C后,不会终止进程,而是打印提示信息,进程继续执行死循环。

四、信号处理实操:核心函数(signal & sigaction)

Linux中,处理信号的核心是两个系统调用:signal(简单易用,有局限性)和sigaction(功能强大,推荐使用)。结合之前学的系统调用与库函数区别,这两个都是系统调用,用于配置信号的处理方式。

(一)signal函数(简单入门)

1. 函数原型
cpp 复制代码
#include <signal.h>

// 定义信号处理函数的类型(参数:信号编号,返回值:无)
typedef void (*sighandler_t)(int);

// 函数功能:绑定信号与处理方式
// 参数1:signum:要处理的信号编号(如SIGINT、SIGCHLD)
// 参数2:handler:处理方式(SIG_DFL默认、SIG_IGN忽略、自定义函数指针)
// 返回值:成功返回之前的处理方式,失败返回SIG_ERR
sighandler_t signal(int signum, sighandler_t handler);
2. 局限性(重点)

signal函数虽然简单,但存在两个明显局限性,导致在实际开发中(尤其是多线程、高并发场景)不推荐使用:

  1. 不可靠:在某些系统中,信号处理函数执行一次后,会自动重置为默认处理方式(SIG_DFL),需要重新绑定。

  2. 功能有限:无法设置信号屏蔽字、无法获取信号的详细信息(如发送信号的进程PID)、无法控制被信号中断的系统调用是否重启。

(二)sigaction函数(推荐使用,功能强大)

sigaction是signal函数的增强版,解决了signal的局限性,支持设置信号屏蔽字、获取信号详细信息、控制系统调用重启等,是实际开发中处理信号的首选。

1. 函数原型与核心结构体
cpp 复制代码
#include <signal.h>

// 信号处理结构体(描述信号的处理方式)
struct sigaction {
    // 信号处理函数(两种形式,二选一)
    union {
        void (*sa_handler)(int);          // 基本处理函数(仅接收信号编号)
        void (*sa_sigaction)(int, siginfo_t *, void *); // 扩展处理函数(获取信号详细信息)
    } __sigaction_handler;
    
    sigset_t sa_mask;  // 信号屏蔽字:处理当前信号时,需要阻塞的其他信号
    int sa_flags;      // 控制信号处理行为的标志位(如是否重启系统调用、是否使用扩展处理函数)
    void (*sa_restorer)(void); // 已废弃,无需使用
};

// 函数功能:配置信号的处理方式(比signal更灵活)
// 参数1:signum:要处理的信号编号
// 参数2:act:指向sigaction结构体的指针,描述新的处理方式(NULL表示仅查询)
// 参数3:oldact:指向sigaction结构体的指针,保存之前的处理方式(NULL表示不保存)
// 返回值:成功返回0,失败返回-1,设置errno
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
2. 核心参数说明
  1. sa_handler / sa_sigaction

    1. sa_handler:基本处理函数,与signal的自定义函数用法一致,仅接收信号编号。

    2. sa_sigaction:扩展处理函数,需设置sa_flags = SA_SIGINFO,可获取信号的详细信息(如发送信号的进程PID、信号产生的原因)。

  2. sa_mask:信号屏蔽字,设置处理当前信号时,需要阻塞的其他信号(避免多个信号同时处理导致混乱)。比如处理SIGINT时,阻塞SIGQUIT,防止两个终止信号同时触发。

  3. sa_flags:常用标志位(重点掌握):

    1. SA_RESTART:被信号中断的系统调用(如read、write、sleep)会自动重启,避免返回EINTR错误。

    2. SA_SIGINFO:使用扩展处理函数sa_sigaction,可获取信号详细信息。

    3. SA_NOCLDWAIT:处理SIGCHLD信号时,子进程退出后会被内核自动回收,不会产生僵尸进程。

3. 实操示例:用sigaction捕捉SIGINT信号

实现功能:捕捉SIGINT信号,处理时阻塞SIGQUIT信号,设置系统调用自动重启,打印信号编号和发送信号的进程PID:

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

// 扩展信号处理函数(获取信号详细信息)
void sigint_handler(int signum, siginfo_t *info, void *context) {
    printf("\n收到SIGINT信号(编号:%d)\n", signum);
    printf("发送信号的进程PID:%d\n", info->si_pid); // 获取发送信号的进程PID
}

int main() {
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa)); // 初始化结构体
    
    // 设置扩展处理函数
    sa.sa_sigaction = sigint_handler;
    // 设置标志位:使用扩展处理函数 + 自动重启系统调用
    sa.sa_flags = SA_SIGINFO | SA_RESTART;
    // 初始化信号屏蔽字,添加SIGQUIT(处理SIGINT时,阻塞SIGQUIT)
    sigemptyset(&sa.sa_mask);
    sigaddset(&sa.sa_mask, SIGQUIT);
    
    // 绑定SIGINT信号
    if (sigaction(SIGINT, &sa, NULL) == -1) {
        perror("sigaction failed");
        return 1;
    }
    
    printf("进程运行中,PID:%d,按下Ctrl+C测试\n", getpid());
    while (1) {
        sleep(1); // 系统调用,被信号中断后会自动重启
    }
    
    return 0;
}

五、信号的实际应用(结合已学知识,重点)

信号不是孤立的,结合之前学的fork、exec、僵尸进程等知识,信号在实际开发中有两个核心应用场景,也是面试高频考点。

(一)应用1:处理SIGCHLD信号,回收僵尸进程

之前我们学过,子进程退出后,若父进程未调用wait()/waitpid()回收,会产生僵尸进程(状态为Z),消耗PID资源。而子进程退出时,内核会向父进程发送SIGCHLD信号(默认忽略),因此我们可以通过捕捉SIGCHLD信号,在信号处理函数中调用waitpid(),自动回收僵尸进程,避免资源泄漏。

实操示例:捕捉SIGCHLD信号,自动回收子进程,避免僵尸进程:

cpp 复制代码
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <string.h>

// SIGCHLD信号处理函数:回收僵尸进程
void sigchld_handler(int signum) {
    // 循环回收所有退出的子进程(避免多个子进程同时退出,只回收一个)
    pid_t pid;
    while ((pid = waitpid(-1, NULL, WNOHANG)) > 0) {
        printf("子进程(PID:%d)退出,已自动回收,避免僵尸进程\n", pid);
    }
}

int main() {
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = sigchld_handler;
    sa.sa_flags = SA_RESTART; // 自动重启被中断的系统调用
    sigemptyset(&sa.sa_mask);
    
    // 绑定SIGCHLD信号
    if (sigaction(SIGCHLD, &sa, NULL) == -1) {
        perror("sigaction failed");
        return 1;
    }
    
    // 创建3个子进程
    for (int i = 0; i < 3; i++) {
        pid_t pid = fork();
        if (pid == -1) {
            perror("fork failed");
            return 1;
        }
        if (pid == 0) {
            // 子进程:运行1秒后退出
            printf("子进程(PID:%d)运行中,1秒后退出\n", getpid());
            sleep(1);
            exit(0);
        }
    }
    
    // 父进程:死循环,等待SIGCHLD信号
    while (1) {
        sleep(1);
    }
    
    return 0;
}

运行结果:3个子进程依次退出,父进程收到SIGCHLD信号后,自动调用waitpid()回收子进程,不会产生僵尸进程(可通过ps -ef | grep Z查看)。

(二)应用2:优雅终止进程(SIGTERM vs SIGKILL)

日常开发中,我们需要终止进程时,优先使用SIGTERM信号(kill PID,默认发送SIGTERM),而不是SIGKILL信号(kill -9 PID),原因如下:

  1. SIGTERM是"优雅终止"信号,进程收到后,可以执行自定义处理函数(如关闭文件、释放内存、保存数据),然后再终止,避免资源泄漏。

  2. SIGKILL是"强制终止"信号,无法被捕捉、忽略,进程会被立即终止,来不及清理资源,可能导致文件损坏、数据丢失。

实操示例:捕捉SIGTERM信号,实现优雅终止:

cpp 复制代码
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>

// 优雅终止处理函数
void sigterm_handler(int signum) {
    printf("\n收到SIGTERM信号(优雅终止),开始清理资源...\n");
    // 模拟资源清理:关闭文件、释放内存等
    sleep(2);
    printf("资源清理完成,进程正常终止\n");
    exit(0); // 主动终止进程
}

int main() {
    // 绑定SIGTERM信号
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = sigterm_handler;
    sa.sa_flags = SA_RESTART;
    if (sigaction(SIGTERM, &sa, NULL) == -1) {
        perror("sigaction failed");
        return 1;
    }
    
    printf("进程运行中,PID:%d,发送kill %d测试优雅终止\n", getpid(), getpid());
    while (1) {
        sleep(1);
    }
    
    return 0;
}

测试方法:运行程序后,在另一个终端执行kill 进程PID(发送SIGTERM信号),程序会先清理资源,再正常终止;若执行kill -9 进程PID(发送SIGKILL信号),程序会立即终止,不会执行清理逻辑。

六、信号操作常用命令(实操必备)

除了编程中处理信号,日常使用Linux时,也常用命令发送信号、查看信号相关信息,重点掌握以下4个命令:

  1. kill:向指定进程发送信号(最常用)。 kill PID # 向PID对应的进程发送SIGTERM信号(默认) ``kill -9 PID # 向进程发送SIGKILL信号,强制终止(无法拦截) ``kill -19 PID # 向进程发送SIGSTOP信号,暂停进程 ``kill -18 PID # 向进程发送SIGCONT信号,恢复暂停的进程 ``kill -l # 查看所有信号的编号和名称

  2. killall:向指定名称的所有进程发送信号(批量终止进程)。 killall -9 test # 强制终止所有名为test的进程 ``killall -15 nginx # 优雅终止所有nginx进程

  3. pkill:根据进程名称、PID等条件,向进程发送信号(更灵活)。 pkill -9 test # 强制终止所有名为test的进程 ``pkill -u user # 终止所有属于user用户的进程

  4. ps:查看进程状态,判断进程是否被信号影响(如僵尸进程、暂停进程)。 ps -ef | grep Z # 查看所有僵尸进程(状态为Z) ``ps -o pid,ppid,state # 查看进程PID、父进程PID、状态(T表示暂停)

七、常见问题与避坑要点(重点)

  1. 信号丢失问题:标准信号(1~31)不支持排队,若多个相同信号同时发送,进程可能只处理一次(比如同时发送多个SIGINT信号,进程只执行一次处理函数);实时信号(32~64)支持排队,不会丢失。

  2. 信号屏蔽字的坑:设置sa_mask时,仅在当前信号处理期间,阻塞指定信号;处理完成后,阻塞自动解除,不会影响后续信号的递达。

  3. 系统调用被中断:某些系统调用(如read、write、sleep)会被信号中断,返回EINTR错误;设置sa_flags = SA_RESTART,可让被中断的系统调用自动重启,避免手动处理错误。

  4. 无法捕捉的信号:SIGKILL(9)和SIGSTOP(19)无法被捕捉、忽略、修改处理方式,只能执行默认动作,这是系统预留的"强制控制进程"的信号。

  5. 僵尸进程的误区:即使捕捉了SIGCHLD信号,若处理函数中使用wait()(而非waitpid(-1, NULL, WNOHANG)),父进程会被阻塞,无法处理其他信号;使用waitpid的WNOHANG选项,可实现非阻塞回收。

八、与前序知识点的关联

与进程控制(fork/exec):子进程退出时发送SIGCHLD信号,父进程通过捕捉该信号回收子进程,避免僵尸进程;exec替换进程后,原进程的信号处理配置会被新程序覆盖,需在新程序中重新配置信号。

与系统调用:signal、sigaction、kill、waitpid都是系统调用,底层由内核实现;库函数(如stdio.h中的函数)可能会被信号中断,需通过sa_flags = SA_RESTART优化。

与IO密集型/计算密集型:信号处理是异步的,适合处理IO密集型场景中的事件通知(如网络连接断开、文件读写完成);计算密集型场景中,需避免频繁触发信号,以免中断计算流程,影响效率。

与引用计数:进程收到信号终止时,内核会自动释放进程的资源(文件描述符、内存等),本质是减少资源的引用计数,当引用计数为0时,资源被彻底释放。

九、实操案例(巩固练习)

结合本节课知识点,通过3个实操案例,巩固信号的处理方式和实际应用,可直接在Linux环境中练习:

  1. 案例1:信号捕捉与忽略。编写程序,忽略SIGQUIT信号,捕捉SIGINT信号,按下Ctrl+C时打印提示信息,按下Ctrl+\时无反应,用kill -9 PID终止程序。

  2. 案例2:自动回收僵尸进程。创建5个子进程,子进程随机睡眠1~5秒后退出,父进程通过捕捉SIGCHLD信号,用waitpid()非阻塞回收所有子进程,确保无僵尸进程产生。

  3. 案例3:优雅终止与资源清理。编写程序,模拟打开一个文件,捕捉SIGTERM信号,收到信号后关闭文件、打印清理日志,再正常终止;测试kill PID和kill -9 PID的区别。

十、总结

本节课重点讲解了Linux信号的核心知识点,核心要点总结如下:

信号是内核向进程传递的"事件通知",本质是整数编号,异步触发,分为标准信号(1~31)和实时信号(32~64),SIGKILL和SIGSTOP无法被捕捉、忽略。

信号的三种处理方式:默认处理(SIG_DFL)、忽略处理(SIG_IGN)、自定义处理(捕捉信号),实际开发中常用自定义处理实现灵活响应。

信号处理的核心函数:signal(简单但有局限性)、sigaction(功能强大,推荐使用),可配置信号处理函数、信号屏蔽字、处理标志位。

信号的核心应用:捕捉SIGCHLD信号回收僵尸进程、捕捉SIGTERM信号实现优雅终止,这两个场景是面试高频考点,也是实际开发必备技能。

信号与进程控制、系统调用、引用计数密切相关,是Linux进程间通信的基础,也是后续学习网络编程、后台服务开发的关键。

本节课的重点是"理解信号的异步特性、掌握信号处理函数的用法、学会解决实际应用中的问题",建议多编译运行代码,测试不同信号的处理效果,熟悉sigaction结构体的配置,避免踩坑。下一篇笔记,我们将讲解Linux进程间通信的其他方式(管道、消息队列),进一步完善进程通信的知识体系。

相关推荐
SiYuanFeng2 小时前
一展使用gpt-5-mini和gemini-3.1-flash-image-preview-0.5k的运行demo代码
linux·python·gpt
不灭锦鲤2 小时前
每天学习新的漏洞,react2shell漏洞
学习
YuanDaima20482 小时前
堆(优先队列)基础原理与题目说明
linux·运维·服务器·人工智能·python··代码
another heaven2 小时前
【软考 对称加密与非对称加密】
服务器·网络
生万千欢喜心2 小时前
linux 安装 人大金仓数据库
linux·运维·数据库
傻啦嘿哟3 小时前
Python多进程编程:用multiprocessing突破GIL限制
服务器·网络·数据库
@insist1233 小时前
网络工程师-网络规划与设计(三):数据中心机房设计规范全解析
服务器·网络·数据库·网络工程师·软考·软件水平考试
悠哉悠哉愿意3 小时前
【物联网学习笔记】TIM
笔记·单片机·嵌入式硬件·物联网·学习
mounter6253 小时前
深度拦截:Linux 内核引入 Firmware LSM 挂钩,eBPF 再下一城!
linux·服务器·ebpf·kernel·firmware