现代C:标准库:信号与操作系统软中断有什么关系?
相信你在第一次学习编程时也写出过与下面这段类似的代码#include stdio.h int main(void) { int x 10; int y 0; printf(%d, x / y); return 0; }可以很明显地看到这里在代码中我们通过 printf 函数打印出了除法表达式 x / y 的计算结果。但定睛一看你就会发现在这个表达式中除数变量 y 对应的值为 0。因此程序在运行时便会发生“除零异常”。当然你可能会说这只是我们故意构造的一段带有问题的程序。但在真实场景中四则运算作为最基本的数学运算被广泛应用在各类大型 C 项目中。而当参与运算的具体操作数可以被来自于用户的输入影响且程序实现并没有进行完备的边界条件检查时类似的运行时异常难免会发生。除此之外程序在运行过程中都会直接或间接地与操作系统甚至底层硬件进行交互。因此你可能会遇到下面这几种情况程序运行时由于访问了非法的内存资源导致出现 “Segmentation Fault” 等异常用户想要结束程序急切地按下了 CtrlC / CommandC 键计算机底层硬件系统出现故障导致无法实现某项特定功能……在这些情况下应用程序应该如何响应其实我上面提到的所有问题都可以通过“信号Signal”来解决。今天我们就来看看什么是信号以及如何在 C 代码中通过标准库提供的相关接口与信号进行交互。什么是信号从广义上来讲信号实际上是操作系统提供的一种可以用来传递特定消息的机制。通过这种方式操作系统可以将程序运行过程中发生的各类特殊情况转发给程序并按照其指定的逻辑进行处理。每一种具体的信号都有它对应的名称这些名称以 “SIG” 作为前缀。比如当程序访问非法内存时便会产生名为 SIGSEGV 的信号。而除零异常则对应着名为 SIGFPE 的信号。信号的产生是一个随机的过程。毕竟异常的出现并不是人们预期中会发生的事情。而这也就意味着程序无法通过简单轮询某个全局变量值的方式来检测信号是否出现然后再进行相应处理。相对地程序需要提前“告诉”操作系统当某个信号到来时应该按怎样的既定方式进行处理。而这就是典型的异步事件处理方式。如果我们再进一步深入到操作系统内核你会发现信号其实是一种“软中断”。信号与软件中断在计算机中中断是指 CPU 需要临时暂停当前正在进行的活动保存相应状态然后转而去执行某个具体的中断服务程序后面我将其简称为 ISR以响应外部环境变化的过程。而在 ISR 执行完毕后在大多数情况下CPU 将继续执行之前暂停的任务。通常来说中断的触发分为两种形式即硬件中断与软件中断也被简称为硬中断与软中断。其中硬件中断是指与计算机硬件特定状态相关的中断过程该过程直接由硬件触发。比如当磁盘完成了某次由用户程序指定的 IO 操作后便会通过硬件中断的方式来通知 CPU 这一消息并让 CPU 进行后续的处理。在这个过程中便存在着 CPU 执行流程从应用程序的某行机器指令到磁盘中断处理程序代码的转移。相对地软件中断则是指由计算机软件通过 int 等机器指令引起的 CPU 执行流程的临时转移过程。而我在这门课中多次提到过的“系统调用”便是其中的一种。在早期的 i386 架构中用户程序需要通过指令 int 0x80 才能够借由软件中断从用户态进入到内核态并使用内核提供的系统调用函数。同样地在这个过程中也存在着 CPU 从用户代码到内核代码的执行流程转移。回过头我们再来看看为什么说信号也是一种软中断呢其实很好理解。当特定事件比如“除零”这个操作发生时操作系统会将对应的信号值即对应的 SIGFPE发送给相关程序。而通常情况下如果应用程序并未设置自定义的信号处理程序则操作系统将会执行默认信号处理程序中的逻辑。对于大多数信号来说这个逻辑便是直接终止当前程序的运行。同样地整个信号处理的过程中也存在着 CPU 从用户程序到信号处理程序的执行流程转移。讲到这里我相信你已经基本理解了信号的概念。那么在 C 程序中应该如何与信号进行交互呢在 C 代码中与信号交互C 语言自 C90 标准开始便为我们提供了名为 signal.h 的头文件以用于支持信号的相关功能。同样地还是先来看一段代码#include stdio.h #include signal.h #include stdlib.h void sigHandler(int sig) { printf(Signal %d catched!\n, sig); exit(sig); } int main(void) { signal(SIGFPE, sigHandler); int x 10; int y 0; printf(%d, x / y); }这里我在本讲开头处代码的基础上进行了适当改进。在这段代码中我们首先在 main 函数的开头处使用标准库提供的 signal 函数为当前程序设置了一段针对信号 SIGFPE 的自定义处理逻辑。其中该函数的第一个参数为具体的某个信号。C 标准库一共提供了 6 种不同类型的信号它们均以宏常量的形式被定义。我将它们的具体信息整理在了下面这个表格中供你参考。需要注意的是该参数也支持使用某些与具体操作系统实现相关的其他信号值但需要确保程序运行所在的系统支持这些非 C 标准信号。第二个参数为具体的信号处理函数它的原型需要满足 void (*handler) (int) 这样一种形式即接收一个整型的信号值但不返回任何内容void。随着程序的运行当位于代码第 12 行的除法运算发生除零异常时操作系统便会将信号 SIGFPE 发送给当前进程并根据之前通过 signal 函数注册的信号处理信息调用用户自定义或默认的信号处理函数。需要注意的是信号处理函数的调用过程是由来自操作系统内核的软中断触发的因此这个过程与我们平时见到的通过 call 指令进行的函数调用过程并不相同。总的来看上述应用程序和操作系统围绕信号的基本交互逻辑可以被粗略地描述为以下几个步骤CPU 执行除法指令 idiv发现除零异常CPU 暂停当前程序运行并将控制权转交给操作系统操作系统将信号 SIGFPE 发送给出错的程序操作系统根据情况执行相应的信号处理程序函数信号处理程序执行完毕后若程序未退出则将程序执行恢复到之前的中断点即 CPU 会重新执行 idiv 指令。当然除了上面这种自定义信号处理函数的方式外C 标准库还为我们提供了两种基本的信号处理方式。同样地它们也以宏的形式被定义可以直接作为 signal 函数的第二个参数来使用。我将它们整理在了下面的表格中供你参考其中第一种 SIG_DFL 表示对信号进行默认处理操作系统会按照信号既定的默认处理方式来处理程序SIG_IGN 则会忽略程序收到的信号。但需要注意的是并非所有类型的信号都可以被忽略比如某些难以恢复的软硬件异常对应的信号或是 Linux 上专有的 SIGKILL 和 SIGSTOP 信号等。关于它们的一个简单使用示例如下所示#include signal.h #include stdio.h int main(void) { signal(SIGTERM, SIG_IGN); // 忽略信号 SIGTERM raise(SIGTERM); // 向当前程序发送 SIGTERM 信号 printf(Reachable!\n); // Reachable code! return 0; }这里在代码的第 5 行我们还使用了由标准库提供的 raise 函数。该函数可以让我们在代码中直接向当前程序发送指定的信号。在上面的代码中我们为信号 SIGTERM 设置了 SIG_IGN 作为它的处理方式因此当执行到 raise 函数后虽然它向程序发送了 SIGTERM 信号但程序却不会被立即终止。相反它将继续执行第 6 行的 printf 函数然后以正常方式退出。可以看到操作系统对信号处理函数的调用可能会发生在整个应用程序运行过程中的任意时刻。而在某些情况下这可能会给程序的实际执行带来影响。接下来让我们进一步看看这个过程是如何影响程序运行的。可重入函数试想这样一种情况某一时刻CPU 还在正常执行 main 函数内函数 A 的代码而由于某些外部原因程序在此刻收到了某个信号操作系统便暂停当前程序运行并将执行流程调度至对应的信号处理函数。而在这个信号处理函数中函数 A 又被再次调用。当信号处理完毕后执行流返回了之前的“中断点”并继续处理 main 函数内函数 A 中还未被执行到的指令。那么在这种情况下信号处理函数中对函数 A 的再次调用会不会影响之前还未调用完成的函数 A 的执行状态呢让我们来看下面这段“模拟”代码#include stdio.h #include signal.h #include string.h #define BUF_SIZE 16 // 全局静态数组大小 #define FORMAT_NUM_(N) $#N #define FORMAT_NUM(N) FORMAT_NUM_(N) #define RAISE_EXP_false_ASM() // 调用 raise 函数向当前程序发送信号 #define RAISE_EXP_true_ASM() \ movl $4, %%edi\n\t \ call raise\n\t // 内联汇编实现 #define INLINE_ASM(ID, HAS_EXP) \ mov %0, %%r8\n\t /* 复制传入的字符串数据到全局静态数组 */ \ testq %%rsi, %%rsi\n\t \ je .L1 #ID \n\t \ xorl %%eax, %%eax\n\t \ .L3 #ID :\n\t \ movzbl (%%rdi,%%rax), %%ecx\n\t \ movb %%cl, (%%r8,%%rax)\n\t \ addq $1, %%rax\n\t \ cmpq %%rsi, %%rax\n\t \ jne .L3 #ID \n\t \ .L1 #ID :\n\t \ RAISE_EXP_##HAS_EXP##_ASM() /* 选择性调用 raise 函数 */ \ mov $1, %%rax\n\t \ mov $1, %%rdi\n\t \ mov %0, %%rsi\n\t \ mov FORMAT_NUM(BUF_SIZE) , %%rdx\n\t \ syscall\n\t /* 触发系统调用打印内容 */ static char buf[BUF_SIZE]; // 用于保存字符的全局静态数组 void print_with_exp(const char* str, size_t len) { // 会引起信号中断的版本 asm(INLINE_ASM(a, true) :: g (buf)); } void print_normal(const char* str, size_t len) { // 正常的版本 asm(INLINE_ASM(b, false) :: g (buf)); } void sigHandler(int sig) { const char* str Hello; print_normal(str, strlen(str)); } int main(void) { signal(SIGILL, sigHandler); const char* str , world!; print_with_exp(str, strlen(str)); return 0; }第一眼看上去这段代码可能稍显复杂但它的执行逻辑实际上十分简单。在 main 函数中我们首先通过 signal 函数为信号 SIGILL 注册了一个自定义信号处理函数。接下来我们调用了名为 print_with_exp 的函数并传入了内容为 “Hello” 的字符串 str以及相应的长度信息。函数在调用后会打印出这个字符串的完整内容。这里借助函数 print_with_exp我们将会模拟上面提到的位于 main 函数中的函数 A 的执行流程。但与真实案例稍有不同的是为了模拟操作系统向程序随机发送信号的过程这里我们选择直接在函数 print_with_exp 中通过调用 raise 函数的方式来做到这一点。当然你可能会说“这并不随机”但在现实案例中类似的情况却可能发生。为了从更加细微的角度直观地观察信号处理函数的调用对“函数重入”的影响这里我们选择直接用内联汇编的方式来实现 print_with_exp 函数。相应地名为 print_normal 的函数其功能与 print_with_exp 相同只是在这个函数中我们不会调用 raise 函数。你可以简单地将这两个函数视为功能完全一致的 “print” 函数。为了方便组织代码以将相同功能的实现部分进行抽离我们通过宏的方式“封装”了这两个函数的内联汇编内容。位于代码第 13 行的宏函数 INLINE_ASM将会根据传入参数的不同来选择性地生成不同的汇编代码。在这两个函数的汇编实现中我们首先会将由第一个参数传入的字符串内容根据第二个参数传入的长度信息逐字符复制到程序中的全局静态数组 buf 中。然后通过相应的系统调用再将该数组中的内容输出到终端。只是对于 print_with_exp 函数来说在此之前我们会通过调用 raise 函数将信号 SIGILL 发送给当前程序。你可以参考代码中的注释来了解这部分实现。到这里按照我们的理解程序正常运行后应该会按顺序进行两次输出对应两次系统调用并完整打印字符串 “Hello, world!”。其中前半段由首先调用完成的信号处理函数 sigHandler 中的 print_normal 函数输出后半段由 main 函数内的 print_with_exp 函数输出。但真实情况却并非如此。程序运行后你会发现实际的输出内容是 “Hellold!Hellold!”。这是为什么呢仔细查看代码可以看到函数 print_with_exp 在调用时存入全局静态数组 buf 中的字符串内容 “, world!”会被信号处理代码中 print_normal 函数调用时存入的字符串 “Hello”覆盖了一部分。而当执行流再次退回到 print_with_exp 函数中继续执行时由于此时数组 buf 中的内容已经发生改变因此便打印出了错误的结果。你可以参考下图来理解上述程序的执行流程。对于这种一旦在执行中途被临时打断在此刻再次调用就可能影响前一次调用正常执行的函数我们一般称之为“不可重入函数”。相反不受中途打断和重新调用影响的函数便被称为“可重入函数”。而在信号处理程序中“尽量使用可重入函数”便是我们需要遵守的首要规则。虽然大多数情况下在信号处理代码中对它们的调用都可以正常工作但从程序健壮性的角度来看却并不推荐这样做。实际上包括 printf、exit以及 malloc 在内的众多常用 C 标准库函数它们都并非可重入函数。因此我们能够在信号处理代码中进行的操作也并不多。而为了进一步简化信号处理模型C 标准库为我们提供了一个名为 sig_atomic_t 的整数类型该类型可以保证即使存在可能由于中断导致的执行流程转移对该类型变量的读写过程也都是原子的注意不要与并发编程中的原子类型混淆标准中仅规定了 sig_atomic_t 类型在异步信号处理场景下的原子性。在这种情况下对信号的处理过程可以变为如下代码所示的模式#include signal.h #include stdio.h #include unistd.h #include stdlib.h volatile sig_atomic_t sig 0; void sigHandler(int signal) { sig signal; } int main(void) { signal(SIGINT, sigHandler); int counter 0; // 计数器变量 while(1) { switch (sig) { // 信号筛选与处理 case SIGINT: { printf(SignalValue: %d, sig); /* 异常处理的主要逻辑 */ exit(SIGINT); } } if (counter 5) raise(SIGINT); printf(Counter: %d\n, counter); sleep(1); } return 0; }这里在 main 函数中我们使用死循环来表示一个常驻程序比如 HTTP 服务器的执行状态。在代码的第 10 行通过 signal 函数我们为信号 SIGINT 注册了相应的处理函数 sigHandler。在该函数内部sig_atomic_t 类型变量 sig 的值被更新为收到的信号值。回到主函数在代码的第 13~19 行我们在主循环每次迭代的开头处通过检测变量 sig 的值来判断当前程序是否收到信号并作出相应处理。而在第 18 行当 counter 变量的值被递增到 5 时raise 函数向当前程序发送了信号 SIGINT从而模拟了程序收到信号发生软中断时的场景。这是一种常见的信号处理模式并非适合所有场景。但无论如何你都需要注意对不可重入函数在信号处理程序中的不当使用可能会给程序的运行埋下问题。多线程应用的信号处理学习到这里对于单线程应用我们已经可以很好地在程序中与信号进行交互。但遗憾的是C 语言并没有对并发编程中的信号处理做任何规范上的约束和建议。这意味着在多线程应用中使用 signal 和 raise 函数可能会产生未定义行为。这些函数会如何影响程序执行则取决于不同 C 标准库在具体操作系统上的实现。所以如果你对程序的可移植性及健壮性有要求那么请不要在多线程应用中使用信号处理。作为替代你可以考虑通过宏的方式来区分程序运行所在的不同操作系统然后分别使用 POSIX、Win32 等 API 来以不同方式实现多线程模式下的信号处理过程。总结好了讲到这里今天的内容也就基本结束了。最后我来给你总结一下。这一讲我主要介绍了 C 语言中与信号处理相关的内容包括信号的基本概念如何在 C 程序中使用信号处理函数以及信号中断可能对程序运行带来的影响等。借助信号操作系统可以将程序运行过程中发生的特殊情况及时通知给程序并按照其要求的特定方式或系统的默认方式进行处理。而信号本质上是一种软件中断即操作系统通过特殊机器指令引起的 CPU 执行流程的临时转移。在 C 语言中可以通过引入标准库头文件 signal.h 的方式来使用相关的信号处理接口。其中通过 signal 函数我们可以为当前程序设置针对某个信号的自定义处理程序而 raise 函数则为我们提供了可以向当前程序发送指定信号的能力。C 标准中一共规定了 6 种不同类型的信号以及两种默认的信号处理方式它们均以宏的形式被定义。信号的中断过程可以在任意时刻发生因此为了保证程序的健壮性我们只能在信号处理程序中使用“可重入函数”。这类函数的重新执行不会对前一个还未执行完成的函数的最终执行结果产生影响。为了简化信号的处理方式C 标准还为我们提供了名为 sig_atomic_t 的整数类型。这也就意味着标准可以保证即使在发生信号中断的情况下对该类型变量的读写也都是原子性的。最后C 标准并未对如何在多线程应用中进行信号处理作过多说明。因此在并发编程场景中对 signal 和 raise 等函数的不当使用方式可能会产生未定义行为。

相关新闻

【jetson】yolo8+DeepStream测试

【jetson】yolo8+DeepStream测试

本文档记录了在 Jetson Orin Nano(JetPack 6.0 DeepStream 7.x)平台,基于 DeepStream-Yolo 开源仓库完成 YOLOv8n 模型从 PyTorch 到 ONNX 再到 TensorRT FP16 引擎的转换、自定义推理插件编译与配置调优的全流程 一、基础环境 硬件&#…

2026/7/17 19:39:23阅读更多 →
YOLO26涨点改进 | 全网独家创新、注意力改进篇、ICLR2025顶会力作、哈尔滨工业大学极性感知线性注意力PolaLinearAttention、精准聚焦目标关键区域、重塑高低维特征判别能力、助

YOLO26涨点改进 | 全网独家创新、注意力改进篇、ICLR2025顶会力作、哈尔滨工业大学极性感知线性注意力PolaLinearAttention、精准聚焦目标关键区域、重塑高低维特征判别能力、助

目录 一、YOLO26原生注意力与常规线性注意力全域核心缺陷 1.1 YOLO26原生局部注意力缺陷 1.2 传统普通线性注意力核心短板 1.3 通用注意力机制共性问题 二、ICLR2025哈工大PolaLinearAttention极性感知线性注意力核心原理(顶会深度拆解) 2.1 核心创新设计逻辑 2.1.1 特…

2026/7/17 19:39:23阅读更多 →
AI Agent 面试题 650:如何实现RAG系统的检索延迟优化?

AI Agent 面试题 650:如何实现RAG系统的检索延迟优化?

🔥 AI Agent 面试题 650:如何实现RAG系统的检索延迟优化?摘要:本文深入解析了「如何实现RAG系统的检索延迟优化?」这一 AI Agent 领域的核心面试题。文章从 检索排序优化 的基本概念出发,系统性地剖析了 延…

2026/7/17 19:34:23阅读更多 →
Loop窗口管理工具:5个实用技巧让Mac桌面工作流更高效

Loop窗口管理工具:5个实用技巧让Mac桌面工作流更高效

Loop窗口管理工具:5个实用技巧让Mac桌面工作流更高效 【免费下载链接】Loop Window management made elegant. 项目地址: https://gitcode.com/GitHub_Trending/lo/Loop Loop是一款优雅的macOS窗口管理工具,通过直观的径向菜单系统和丰富的自定义…

2026/7/17 20:39:30阅读更多 →
Linux YUM包管理器核心命令与实战技巧

Linux YUM包管理器核心命令与实战技巧

1. YUM包管理器概述在Red Hat系Linux发行版中,yum(Yellowdog Updater Modified)作为经典的RPM包管理前端工具,至今仍在CentOS 7及以下版本中广泛使用。这个基于Python开发的工具通过自动解决依赖关系,彻底改变了早期需…

2026/7/17 20:39:30阅读更多 →
手游流水增长分析:从红警OL、魂斗罗到部落冲突的运营策略

手游流水增长分析:从红警OL、魂斗罗到部落冲突的运营策略

1. 先看这几款游戏为什么能突然强势增长手游流水突然暴涨,通常不是单一因素推动的。红警 OL、魂斗罗:归来、部落冲突这三款游戏能在同一时间段内出现明显增长,背后往往有版本更新、运营活动、外部环境变化或平台推荐策略调整的共同作用。红警…

2026/7/17 20:39:30阅读更多 →
SolidWorks在数控机床设计中的完整应用流程与运动仿真分析

SolidWorks在数控机床设计中的完整应用流程与运动仿真分析

1. 项目背景与设计需求JX143-MCV380立式数控加工中心作为现代制造业的核心装备,广泛应用于精密零件加工领域。随着工业4.0时代的到来,企业对加工设备的精度、效率和自动化程度提出了更高要求。本项目基于SolidWorks平台,从概念设计到运动仿真…

2026/7/17 20:39:30阅读更多 →
源码级拆解:RAG 系统如何用一张 PostgreSQL 表同时搞定向量与全文检索?

源码级拆解:RAG 系统如何用一张 PostgreSQL 表同时搞定向量与全文检索?

你一定见过这样的场面:把公司三百页的产品手册扔给大模型,问一个手册里明明写着的参数,它一本正经地答错,还答得特别流畅。这不怪模型——你等于让一个没看过手册的人闭卷考试,他只能凭“印象”作答,而他的印象来自互联网语料,不来自你的手册。RAG 干的事,就是把闭卷考…

2026/7/17 20:39:30阅读更多 →
Biotinyl-ACTH (18-39) (human) ;Bio-RPVKVYPNGAEDESAEAFPLEF

Biotinyl-ACTH (18-39) (human) ;Bio-RPVKVYPNGAEDESAEAFPLEF

一、基础信息 英文全称:Biotinyl-ACTH (18-39) (human)中文标准名:生物素标记人促肾上腺皮质激素片段 (18-39)三字母序列:Biotin-Arg-Pro-Val-Lys-Val-Tyr-Pro-Asn-Gly-Ala-Glu-Asp-Glu-Ser-Ala-Glu-Ala-Phe-Pro-Leu-Glu-Phe单字母序列&…

2026/7/17 20:34:30阅读更多 →
VSCode TypeScript 环境配置对比:全局安装 vs 项目本地安装的4个关键差异

VSCode TypeScript 环境配置对比:全局安装 vs 项目本地安装的4个关键差异

VSCode TypeScript 环境配置对比:全局安装 vs 项目本地安装的4个关键差异当你在VSCode中启动一个新的TypeScript项目时,第一个技术决策往往从安装方式开始。这个看似简单的选择——全局安装还是项目本地安装——实际上会深刻影响你的开发流程、团队协作和…

2026/7/17 10:42:55阅读更多 →
智慧树刷课插件:5分钟实现自动化学习的智能助手

智慧树刷课插件:5分钟实现自动化学习的智能助手

智慧树刷课插件:5分钟实现自动化学习的智能助手 【免费下载链接】zhihuishu 智慧树刷课插件,自动播放下一集、1.5倍速度、无声 项目地址: https://gitcode.com/gh_mirrors/zh/zhihuishu 智慧树刷课插件是一款专为智慧树在线教育平台设计的Chrome浏…

2026/7/17 8:31:03阅读更多 →
Steam创意工坊下载器WorkshopDL:跨平台游戏模组获取的终极解决方案

Steam创意工坊下载器WorkshopDL:跨平台游戏模组获取的终极解决方案

Steam创意工坊下载器WorkshopDL:跨平台游戏模组获取的终极解决方案 【免费下载链接】WorkshopDL WorkshopDL - The Best Steam Workshop Downloader 项目地址: https://gitcode.com/gh_mirrors/wo/WorkshopDL 你是否在GOG或Epic Games Store购买了心仪的游戏…

2026/7/17 13:22:23阅读更多 →
VS Code 高效配置与个性化定制全攻略

VS Code 高效配置与个性化定制全攻略

1. VS Code 高效配置基础作为一款轻量级但功能强大的代码编辑器,VS Code 的默认配置已经能满足基本需求,但通过合理调整设置可以大幅提升编码效率。我使用 VS Code 已经有五年多时间,期间尝试过各种配置方案,总结出这套适合大多数…

2026/7/17 0:00:01阅读更多 →
从竞赛代码到桌面工具:让 SuperADD 与 SubspaceAD 真正跑进自己的图像

从竞赛代码到桌面工具:让 SuperADD 与 SubspaceAD 真正跑进自己的图像

在异常检测领域,很多优秀算法最初都是以研究代码的形式发布的。它们能够在固定测试集上复现实验结果,却不一定能被普通用户直接拿来测试自己的图片。尤其是最近很多算法仅提供在固定测试集的测试环境,而gradio的demo演示也不会提供。 对工程应用和在自己的图片上进行测试来…

2026/7/17 0:00:01阅读更多 →
WinRAR高效配置指南:从基础安装到高级压缩实战

WinRAR高效配置指南:从基础安装到高级压缩实战

前几天帮同事处理一个客户发来的压缩包,解压时系统自带的工具弹出一串乱码,换用 WinRAR 却顺利打开了。这种看似简单的场景,恰恰暴露了不同压缩工具在处理非标准编码、分卷压缩或加密文件时的差异。WinRAR 作为一款老牌工具,真正价…

2026/7/17 0:00:01阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/16 20:13:14阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/17 13:22:38阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/17 17:26:50阅读更多 →