【Linux】24.进程信号(1)

文章目录

  • 1. 信号入门
    • 1.1 进程与信号的相关知识
    • 1.2 技术应用角度的信号
    • 1.3 注意
    • 1.4 信号概念
    • 1.5 信号处理常见方式概览
  • 2. 产生信号
    • 2.1 通过终端按键产生信号
    • 2.2 调用系统函数向进程发信号
    • 2.3 由软件条件产生信号
    • 2.4 硬件异常产生信号
    • 2.5 信号保存
  • 3. 阻塞信号
    • 3.1 信号其他相关常见概念
    • 3.2 在内核中的表示
    • 3.3 sigset_t
    • 3.4 信号集操作函数
      • sigprocmask
      • sigpending


1. 信号入门

1.1 进程与信号的相关知识

  1. 进程 必须 识别+能够处理信号(信号没有产生,也要具备处理信号的能力)信号的处理能力,属于进程内置功能的一部分

  2. 进程即便是没有收到信号,也能知道哪些信号该怎么处理

  3. 当进程真的收到了一个具体的信号的时候,进程可能并不会立即处理这个信号,需要等到合适的时候

  4. 一个进程,当信号产生到信号开始被处理,就一定会有时间窗口,进程具有临时保存哪些信号已经发生了的能力


1.2 技术应用角度的信号

用户输入命令,在Shell下启动一个前台进程。

用户按下Ctrl+C ,这个键盘输入产生一个硬件中断,被OS获取,解释成信号,发送给目标前台进程

前台进程因为收到信号,进而引起进程退出

ctrl+c为什么能够杀掉我们前台进程呢?

Linux中,一次登陆中,一个终端一般会配上一个bash,每一个登陆,只允许一个进程是前台进程,可以允许多个进程是后台进程。

键盘输入首先是被前台进程收到的。(这是前台进程和后台进程的本质区别)

ctrl +c本质是被进程解释成为收到了信号。ctrl+c 会触发SIGINT信号(信号编号2),然后终端驱动程序捕获这个按键组合,将SIGINT信号发送给前台进程组的所有进程。

  1. 前台进程特性

    • 与终端关联

    • 能够接收终端输入

    • 属于当前终端的前台进程组

  2. 只能终止前台进程的原因

    • 终端只与前台进程组关联

    • 后台进程组收不到终端产生的信号

关键点:ctrl+c 本质是通过信号机制来终止进程的,而不是直接"杀死"进程。

b1cca7f77b86b1a13f67caf132fd63bf

1-31是普通信号,34-64是实时信号。

信号的处理方式:

  1. 默认动作

  2. 忽略

  3. 自定义动作(信号的捕捉)

例如红灯亮了就等绿灯是默认动作,不管红灯闯红灯就是忽略,红灯了唱歌跳舞就是自定义动作。

进程收到2号信号的默认动作,就是终止自己。

不是所有的信号都是可以被signal捕捉的,比如:9,19。

但是无论信号如何产生,最终一定是谁发送给进程的?

OS,因为OS是进程的管理者。


1.3 注意

  1. Ctrl+C 产生的信号只能发给前台进程。一个命令后面加个&可以放到后台运行,这样Shell不必等待进程结束就可以接受新的命令,启动新的进程。
  2. Shell可以同时运行一个前台进程和任意多个后台进程,只有前台进程才能接到像 Ctrl+C 这种控制键产生的信号。
  3. 前台进程在运行过程中用户随时可能按下 Ctrl+C 而产生一个信号,也就是说该进程的用户空间代码执行到任何地方都有可能收到 SIGINT 信号而终止,所以信号相对于进程的控制流程来说是异步(Asynchronous)的。

1.4 信号概念

信号是进程之间事件异步通知的一种方式,属于软中断。


1.5 信号处理常见方式概览

(sigaction函数稍后详细介绍),可选的处理动作有以下三种:

  1. 忽略此信号。
  2. 执行该信号的默认处理动作。
  3. 提供一个信号处理函数,要求内核在处理该信号时切换到用户态执行这个处理函数,这种方式称为捕捉(Catch)一个信号。

2. 产生信号

2.1 通过终端按键产生信号

SIGINT的默认处理动作是终止进程,SIGQUIT的默认处理动作是终止进程并且Core Dump。

Core Dump

首先解释什么是Core Dump。当一个进程要异常终止时,可以选择把进程的用户空间内存数据全部 保存到磁盘上,文件名通常是core,这叫做Core Dump。

进程异常终止通常是因为有Bug,比如非法内存访问导致段错误,事后可以用调试器检查core文件以查清错误原因,这叫做Post-mortem Debug(事后调试)。一个进程允许产生多大的core文件取决于进程的Resource Limit(这个信息保存 在PCB中)。

默认是不允许产生core文件的,因为core文件中可能包含用户密码等敏感信息,不安全。在开发调试阶段可以用ulimit命令改变这个限制,允许产生core文件。


2.2 调用系统函数向进程发信号

首先在后台执行死循环程序,然后用kill命令给它发SIGSEGV信号。

kill命令是调用kill函数实现的。kill函数可以给一个指定的进程发送指定的信号。

raise函数可以给当前进程发送指定的信号(自己给自己发信号)。

#include <signal.h>
int kill(pid_t pid, int signo);
int raise(int signo);
这两个函数都是成功返回0,错误返回-1

abort函数使当前进程接收到信号而异常终止。

#include <stdlib.h>
void abort(void);
就像exit函数一样,abort函数总是会成功的,所以没有返回值。

2.3 由软件条件产生信号

#include <unistd.h>
unsigned int alarm(unsigned int seconds);
调用alarm函数可以设定一个闹钟,也就是告诉内核在seconds秒之后给当前进程发SIGALRM信号, 该信号的默认处理动作是终止当前进程。

2.4 硬件异常产生信号

硬件异常被硬件以某种方式被硬件检测到并通知内核,然后内核向当前进程发送适当的信号。以下是几种常见的硬件异常及其对应的信号:

  1. 除零异常(SIGFPE):
    当程序执行除以0的操作时,CPU的算术逻辑单元会检测到这个异常。例如:
int a = 1;
int b = 0;
int c = a / b;  // 触发SIGFPE信号
  1. 段错误(SIGSEGV):
    当程序访问了非法内存地址时,内存管理单元(MMU)会产生异常。例如:
int *p = NULL;
*p = 1;         // 访问空指针,触发SIGSEGV信号int arr[10];
arr[10000] = 1; // 数组越界,可能触发SIGSEGV信号
  1. 非法指令(SIGILL):
    当CPU执行了非法指令时产生此信号:
void (*bad_func_ptr)() = (void (*)())0x12345678;
bad_func_ptr();  // 执行非法地址的代码,触发SIGILL信号
  1. 总线错误(SIGBUS):
    当访问未对齐的内存地址时可能产生此信号:
char *ptr = (char *)0x12345;
int *iptr = (int *)ptr;
*iptr = 1;      // 可能触发SIGBUS信号

在系统层面,这些硬件异常的处理流程是:

  1. 硬件检测到异常
  2. 触发CPU中断
  3. CPU切换到内核态
  4. 内核将硬件异常转换为相应的信号
  5. 内核向进程发送信号
  6. 如果进程注册了信号处理函数,则执行该函数
  7. 如果没有注册处理函数,则执行信号的默认处理动作(通常是终止进程)

这就是为什么C/C++中的很多运行时错误(如除零、空指针解引用、数组越界等)最终都表现为进程收到信号并终止。这种机制让操作系统能够及时发现并处理程序中的严重错误,防止错误程序继续运行可能造成的更大危害。


2.5 信号保存

为什么要信号保存?

进程收到信号之后,可能不会立即处理这个信号。信号不会被处理,就要有一个时间窗口。


3. 阻塞信号

3.1 信号其他相关常见概念

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

  • 信号从产生到递达之间的状态,称为信号未决(Pending)。

  • 进程可以选择阻塞 (Block )某个信号。

  • 被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作.

  • 注意,阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。


3.2 在内核中的表示

信号在内核中的表示示意图

fca6ee5b6f978ae7d0db9dec37b27c60

  • 每个信号都有两个标志位分别表示阻塞(block)和未决(pending),还有一个函数指针表示处理动作。信号产生时,内核在进程控制块中设置该信号的未决标志,直到信号递达才清除该标志。在上图的例子中,SIGHUP信号未阻塞也未产生过,当它递达时执行默认处理动作。

  • SIGINT信号产生过,但正在被阻塞,所以暂时不能递达。虽然它的处理动作是忽略,但在没有解除阻塞之前不能忽略这个信号,因为进程仍有机会改变处理动作之后再解除阻塞。

  • SIGQUIT信号未产生过,一旦产生SIGQUIT信号将被阻塞,它的处理动作是用户自定义函数sighandler。 如果在进程解除对某信号的阻塞之前这种信号产生过多次,将如何处理?POSIX.1允许系统递送该信号一次或多次。Linux是这样实现的:常规信号在递达之前产生多次只计一次,而实时信号在递达之前产生多次可以依次放在一个队列里。本章不讨论实时信号。


3.3 sigset_t

从上图来看,每个信号只有一个bit的未决标志,非0即1,不记录该信号产生了多少次,阻塞标志也是这样表示的。

因此,未决和阻塞标志可以用相同的数据类型sigset_t来存储,sigset_t称为信号集,这个类型可以表示每个信号的“有效”或“无效”状态,在阻塞信号集中“有效”和“无效”的含义是该信号是否被阻塞,而在未决信号集中“有效”和“无效”的含义是该信号是否处于未决状态。下一节将详细介绍信号集的各种操作。 阻塞信号集也叫做当前进程的信号屏蔽字(Signal Mask),这里的“屏蔽”应该理解为阻塞而不是忽略。


3.4 信号集操作函数

sigset_t类型对于每种信号用一个bit表示“有效”或“无效”状态,至于这个类型内部如何存储这些bit则依赖于系统实现,从使用者的角度是不必关心的,使用者只能调用以下函数来操作sigset_ t变量,而不应该对它的内部数据做任何解释,比如用printf直接打印sigset_t变量是没有意义的

#include <signal.h>
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset (sigset_t *set, int signo);
int sigdelset(sigset_t *set, int signo);
int sigismember(const sigset_t *set, int signo); 
  • 函数sigemptyset初始化set所指向的信号集,使其中所有信号的对应bit清零,表示该信号集不包含 任何有效信号。

  • 函数sigfillset初始化set所指向的信号集,使其中所有信号的对应bit置位,表示 该信号集的有效信号包括系统支持的所有信号。

  • 注意:在使用sigset_ t类型的变量之前,一定要调 用sigemptyset或sigfillset做初始化,使信号集处于确定的状态。初始化sigset_t变量之后就可以在调用sigaddset和sigdelset在该信号集中添加或删除某种有效信号。

这四个函数都是成功返回0,出错返回-1。sigismember是一个布尔函数,用于判断一个信号集的有效信号中是否包含某种 信号,若包含则返回1,不包含则返回0,出错返回-1。

sigprocmask

调用函数sigprocmask可以读取或更改进程的信号屏蔽字(阻塞信号集)。

#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oset); 
返回值:若成功则为0,若出错则为-1

如果oset是非空指针,则读取进程的当前信号屏蔽字通过oset参数传出。如果set是非空指针,则 更改进程的信号屏蔽字,参数how指示如何更改。如果oset和set都是非空指针,则先将原来的信号 屏蔽字备份到oset里,然后根据set和how参数更改信号屏蔽字。假设当前的信号屏蔽字为mask,下表说明了how参数的可选值。

b5469757cf1306db6a8ca96d07d949ea

如果调用sigprocmask解除了对当前若干个未决信号的阻塞,则在sigprocmask返回前,至少将其中一个信号递达。


sigpending

#include <signal.h>
sigpending
读取当前进程的未决信号集,通过set参数传出。调用成功则返回0,出错则返回-1

代码:

void printsigset(sigset_t *set)
{// 打印信号集中的信号状态,用1表示信号在集合中,0表示不在for(int i=1; i<32; i++) {if (sigismember(set, i)) {  // 判断信号i是否在信号集set中putchar('1');} else {putchar('0');}}puts("");
}int main()
{sigset_t s, p;sigemptyset(&s);        // 初始化信号集s为空集sigaddset(&s, SIGINT);  // 将SIGINT信号添加到信号集s中,Ctrl+Csigprocmask(SIG_BLOCK, &s, NULL);  // 设置信号屏蔽字,阻塞SIGINT信号while(1) {sigpending(&p);     // 获取未决信号集printsigset(&p);    // 打印未决信号集sleep(1);}return 0;
}

程序运行时,每秒钟把各信号的未决状态打印一遍,由于我们阻塞了SIGINT信号,按Ctrl+C将会使SIGINT信号处于未决状态。按Ctrl+\仍然可以终止程序,因为SIGQUIT信号没有阻塞。

代码:

// 打印未决信号集的函数
void PrintPending(sigset_t &pending)
{// 从31号信号到1号信号逐个检查for (int signo = 31; signo >= 1; signo--){if (sigismember(&pending, signo))cout << "1";  // 信号处于未决状态elsecout << "0";  // 信号不在未决集中}cout << "\n\n";
}// 信号处理函数
void handler(int signo)
{cout << "catch a signo: " << signo << endl;
}int main()
{// 4. 屏蔽所有可屏蔽信号sigset_t bset, oset;sigemptyset(&bset);    // 清空信号集sigemptyset(&oset);    // 清空旧信号集for (int i = 1; i <= 31; i++){sigaddset(&bset, i); // 将所有信号添加到屏蔽集}sigprocmask(SIG_SETMASK, &bset, &oset); // 设置信号屏蔽字// 循环检测未决信号sigset_t pending;while (true){int n = sigpending(&pending);  // 获取未决信号集if (n < 0)continue;PrintPending(pending);         // 打印未决信号集sleep(1);}// // 0. 对2号信号进行自定义捕捉// signal(2, handler);// // 1. 先对2号信号进行屏蔽 --- 数据预备// sigset_t bset, oset; // 在哪里开辟的空间???用户栈上的,属于用户区// sigemptyset(&bset);// sigemptyset(&oset);// sigaddset(&bset, 2); // 我们已经把2好信号屏蔽了吗?并没有设置进入到你的进程的task_struct// // 1.2 调用系统调用,将数据设置进内核// sigprocmask(SIG_SETMASK, &bset, &oset); // 我们已经把2好信号屏蔽了吗?ok// // 2. 重复打印当前进程的pending 0000000000000000000000000// sigset_t pending;// int cnt = 0;// while (true)// {//     // 2.1 获取//     int n = sigpending(&pending);//     if (n < 0)//         continue;//     // 2.2 打印//     PrintPending(pending);//     sleep(1);//     cnt++;//     // 2.3 解除阻塞//     if(cnt == 20)//     {//         cout << "unblock 2 signo" << endl;//         sigprocmask(SIG_SETMASK, &oset, nullptr); // 我们已经把2好信号屏蔽了吗?ok//     }// }// // 3 发送2号 0000000000000000000000010return 0;
}

被注释的代码:

// 0. 设置2号信号(SIGINT)的处理函数
signal(2, handler);// 1. 先对2号信号进行屏蔽 --- 数据预备
sigset_t bset, oset;    // 在用户栈上创建信号集
sigemptyset(&bset);     // 初始化为空集
sigemptyset(&oset);     // 保存旧的信号屏蔽字
sigaddset(&bset, 2);    // 只添加2号信号到屏蔽集
// 调用系统调用,将数据设置进内核
sigprocmask(SIG_SETMASK, &bset, &oset);// 2. 监控未决信号状态
// 重复打印当前进程的pending 0000000000000000000000000
sigset_t pending;
int cnt = 0;
while (true)
{int n = sigpending(&pending);  // 获取未决信号if (n < 0)continue;PrintPending(pending);         // 打印未决信号状态sleep(1);cnt++;// 20秒后解除2号信号的屏蔽if(cnt == 20){cout << "unblock 2 signo" << endl;// 恢复原来的信号屏蔽字,即解除屏蔽sigprocmask(SIG_SETMASK, &oset, nullptr);}
}
// 3 发送2号 0000000000000000000000010

两个场景的区别:

  1. 当前执行的代码:
    • 屏蔽所有可屏蔽信号
    • 持续监控所有信号的未决状态
    • 信号会一直保持在未决状态
  2. 注释掉的代码:
    • 只屏蔽SIGINT(2号)信号
    • 设置了SIGINT的自定义处理函数
    • 20秒后解除屏蔽,让信号能够被处理
    • 可以观察到SIGINT信号从未决变为已处理的过程

注释中的重要说明:

  • task_struct:进程描述符,在内核中保存进程的信号屏蔽字
  • 信号集虽然在用户栈上定义,但实际的屏蔽操作是在内核中完成
  • 通过注释分步骤展示了信号屏蔽、监控和解除屏蔽的完整流程

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

编程题-电话号码的字母组合(中等)

题目&#xff1a; 给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到字母的映射如下&#xff08;与电话按键相同&#xff09;。注意 1 不对应任何字母。 解法一&#xff08;哈希表动态添加&#xff09;&#x…

数据结构课程设计(三)构建决策树

3 决策树 3.1 需求规格说明 【问题描述】 ID3算法是一种贪心算法&#xff0c;用来构造决策树。ID3算法起源于概念学习系统&#xff08;CLS&#xff09;&#xff0c;以信息熵的下降速度为选取测试属性的标准&#xff0c;即在每个节点选取还尚未被用来划分的具有最高信息增益的…

Node.js下载安装及环境配置

目录 一、下载 1. 查看电脑版本&#xff0c;下载对应的安装包 2. 下载路径下载 | Node.js 中文网 二、安装步骤 1. 双击安装包 2. 点击Next下一步 3. 选择安装路径 4. 这里我选择默认配置&#xff0c;继续Next下一步&#xff08;大家按需选择&#xff09; 5. 最后inst…

使用python实现与本地ollama部署的deepseek对话

专栏总目录 按照ollama官方doc的example操作&#xff0c;没有成功与本地ollama上的deepseek-r1:1.5b通讯后&#xff0c;发现vscode可以调用本地ollama上的deepseek模型。 为了实现与ollama上的deepseek模型通讯&#xff0c;我使用wireshark对本地回环地址进行侦听后&#xff0c…

【大模型理论篇】最近大火的DeepSeek-R1初探系列1

1. 背景介绍 这一整个春节&#xff0c;被DeepSeek-R1刷屏。各种铺天盖地的新闻以及老板发的相关信息&#xff0c;着实感受到DeepSeek-R1在国外出圈的震撼。 DeepSeek推出了新的推理模型&#xff1a;DeepSeek-R1-Zero 和 DeepSeek-R1。DeepSeek-R1-Zero 是一个在没有经过监督微调…

Pandoc, Zotero, JabRef 管理论文引用,生成参考文献 | 撰写论文 paper

书接上回&#xff0c;使用 Obsidian, Zotero, JabRef, Pandoc, Markup-Markdown | 撰写论文 paper 管理论文引用&#xff0c;生成参考文献 TL; DR导出 bibliography 文件JabRefZotero 参考文献引用语法reference-docLinks TL; DR 安装 pandoc v3.6.2. 使用一下命令&#xff0c…

计算机网络——流量控制

流量控制的基本方法是确保发送方不会以超过接收方处理能力的速度发送数据包。 通常的做法是接收方会向发送方提供某种反馈&#xff0c;如&#xff1a; &#xff08;1&#xff09;停止&等待 在任何时候只有一个数据包在传输&#xff0c;发送方发送一个数据包&#xff0c;…

【C语言入门】解锁核心关键字的终极奥秘与实战应用(三)

目录 一、auto 1.1. 作用 1.2. 特性 1.3. 代码示例 二、register 2.1. 作用 2.2. 特性 2.3. 代码示例 三、static 3.1. 修饰局部变量 3.2. 修饰全局变量 3.3. 修饰函数 四、extern 4.1. 作用 4.2. 特性 4.3. 代码示例 五、volatile 5.1. 作用 5.2. 代码示例…

2.2 实现双向链表的快速排序

实现一个双向链表的快速排序。 1>程序代码 #include <stdio.h> #include <string.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <pthread.h>…

力扣动态规划-19【算法学习day.113】

前言 ###我做这类文章一个重要的目的还是记录自己的学习过程&#xff0c;我的解析也不会做的非常详细&#xff0c;只会提供思路和一些关键点&#xff0c;力扣上的大佬们的题解质量是非常非常高滴&#xff01;&#xff01;&#xff01; 习题 1.矩形中移动的最大次数 题目链接…

Gurobi基础语法之 addConstr, addConstrs, addQConstr, addMQConstr

在新版本的 Gurobi 中&#xff0c;向 addConstr 这个方法中传入一个 TempConstr 对象&#xff0c;在模型中就会根据这个对象生成一个约束。更重要的是&#xff1a;TempConstr 对象可以传给所有addConstr系列方法&#xff0c;所以下面先介绍 TempConstr 对象 TempConstr TempC…

neo4j-community-5.26.0 create new database

1.edit neo4j.conf 把 # The name of the default database initial.dbms.default_databasehonglouneo4j # 写上自己的数据库名称 和 # Name of the service #5.0 server.windows_service_nameneo4j #4.0 dbms.default_databaseneo4j #dbms.default_databaseneo4jwind serve…

unity实现回旋镖函数

最近学习unity2D&#xff0c;想实现一个回旋镖武器&#xff0c;发出后就可以在角色周围回旋。 一、目标 1.不是一次性的&#xff0c;扔出去、返回、没有了&#xff1b;而是扔出去&#xff0c;返回到角色后方相同距离&#xff0c;再次返回&#xff1b;再次返回&#xff0c;永远…

【C++基础】字符串/字符读取函数解析

最近在学C以及STL&#xff0c;打个基础 参考&#xff1a; c中的char[] ,char* ,string三种字符串变量转化的兼容原则 c读取字符串和字符的6种函数 字符串结构 首先明确三种字符串结构的兼容关系&#xff1a;string>char*>char [] string最灵活&#xff0c;内置增删查改…

SpringBoot源码解析(九):Bean定义接口体系

SpringBoot源码系列文章 SpringBoot源码解析(一)&#xff1a;SpringApplication构造方法 SpringBoot源码解析(二)&#xff1a;引导上下文DefaultBootstrapContext SpringBoot源码解析(三)&#xff1a;启动开始阶段 SpringBoot源码解析(四)&#xff1a;解析应用参数args Sp…

C++模板编程——可变参函数模板

目录 1. 可变参函数模板基本介绍 2. 参数包展开——通过递归函数 3. 参数包展开——通过编译期间if语句(constexpr if) 4. 重载 5. 后记 进来看的小伙伴们应该对C中的模板有了一定了解&#xff0c;下面给大家介绍一下可变参函数模板。过于基础的概念将不仔细介绍。 1. 可变…

ChatGPT-4o和ChatGPT-4o mini的差异点

在人工智能领域&#xff0c;OpenAI再次引领创新潮流&#xff0c;近日正式发布了其最新模型——ChatGPT-4o及其经济实惠的小型版本ChatGPT-4o Mini。这两款模型虽同属于ChatGPT系列&#xff0c;但在性能、应用场景及成本上展现出显著的差异。本文将通过图文并茂的方式&#xff0…

2025最新源支付V7全套开源版+Mac云端+五合一云端

2025最新源支付V7全套开源版Mac云端五合一云端 官方1999元&#xff0c; 最新非网上那种功能不全带BUG开源版&#xff0c;可以自己增加授权或二开 拥有卓越的性能和丰富的功能。它采用全新轻量化的界面UI&#xff0c;让您能更方便快捷地解决知识付费和运营赞助的难题 它基于…

数据分析系列--[12] RapidMiner辨别分析(含数据集)

一、数据准备 二、导入数据 三、数据预处理 四、建模辨别分析 五、导入测试集进行辨别分析 一、数据准备 点击下载数据集 二、导入数据 三、数据预处理 四、建模辨别分析 五、导入测试集进行辨别分析 Ending, congratulations, youre done.

当卷积神经网络遇上AI编译器:TVM自动调优深度解析

从铜线到指令&#xff1a;硬件如何"消化"卷积 在深度学习的世界里&#xff0c;卷积层就像人体中的毛细血管——数量庞大且至关重要。但鲜有人知&#xff0c;一个简单的3x3卷积在CPU上的执行路径&#xff0c;堪比北京地铁线路图般复杂。 卷积的数学本质 对于输入张…