C语言实现Async/Await:基于无栈协程的异步编程实践
如果你是一名C语言开发者面对一个需要等待网络响应或文件读写的任务你的第一反应是什么是立刻想到多线程、回调函数还是条件变量加状态机这些传统方案往往让清晰的业务逻辑淹没在复杂的线程管理和状态跳转中。而当你看到JavaScript、Python甚至Rust中优雅的async/await语法时是否曾想过在C语言这片“系统编程的土壤”上能否也种出这样简洁的异步代码答案是肯定的。在C语言中实现async/await并非天方夜谭它本质上是一种基于协程Coroutine或状态机的“语法糖”和编程范式封装。其核心价值不在于创造新的运行时而在于通过编译期代码生成或宏技巧将异步流程的控制逻辑“扁平化”让开发者能用看似同步的代码结构写出高性能的异步程序。这对于嵌入式系统、网络服务器和高性能中间件开发具有现实意义。本文将为你彻底拆解如何在C语言中模拟实现async/await。我们不只讲概念更会从零开始手把手带你实现一个简易的异步框架并分析其背后的协程原理、状态机转换和上下文切换。你会看到那些在高级语言中神秘的async和await关键字在C语言里是如何通过switch-case、函数指针和自定义栈等基础构件搭建起来的。1. 为什么要在C语言中追求async/await在深入代码之前我们必须先回答一个根本问题既然C语言本身没有原生支持我们为何要费尽心思去模拟async/await1.1 传统C语言异步编程的痛点在经典的C语言网络编程中如使用socket处理多个连接通常有以下几种方式多进程/多线程模型每个连接一个线程。简单直观但资源消耗大上下文切换开销高且线程间的同步与数据共享复杂。I/O多路复用select/poll/epoll回调函数这是高性能服务器的基石。然而业务逻辑被拆分成多个回调函数通过事件驱动串联。这导致了著名的“回调地狱”Callback Hell——代码逻辑支离破碎错误处理困难状态管理复杂。手动状态机将异步操作分解为多个步骤用显式的状态变量控制流程。这种方式虽然高效但代码极其冗长且难以维护任何流程改动都可能牵一发而动全身。这三种方式都让业务逻辑的连贯性让位于并发管理的复杂性。1.2 Async/Await带来的范式转变async/await模式的核心思想是**“用同步的代码风格写异步的逻辑”**。async标记一个函数是异步的意味着这个函数内部可以包含暂停点await。await标记一个异步调用点。执行到这里时如果结果未就绪当前异步函数会被挂起让出执行权而不是阻塞线程。当结果就绪后函数从挂起点恢复执行。这种模式将开发者从手动管理回调、状态和线程的泥潭中解放出来让代码专注于“要做什么”而不是“怎么被调度”。1.3 C语言实现的独特价值在C语言中实现这一机制有其特殊的应用场景和优势极致性能与可控性无需引入庞大的运行时或垃圾回收器所有资源如协程栈均可手动精细控制适合嵌入式、内核驱动等资源受限或实时性要求高的场景。深入理解异步本质实现过程本身就是对协程、上下文切换、事件循环等底层概念最深刻的学习。改造遗留系统为现有的基于事件循环的C语言项目如Nginx模块、Redis模块提供更友好的异步编程接口提升开发效率而不改变其核心架构。2. 核心概念与实现原理剖析在C语言中模拟async/await主流思路是基于协程Coroutine。我们需要先理解几个关键概念。2.1 协程Coroutine是什么协程是比线程更轻量的“用户态线程”。它与线程的主要区别在于调度方式线程由操作系统内核调度抢占式执行协程由程序员在用户态显式调度协作式执行。上下文切换开销线程切换涉及内核态与用户态切换开销大协程切换只需保存/恢复寄存器、栈指针等少量上下文开销极小。内存占用每个线程有独立的固定大小的栈通常MB级协程栈可以按需分配和调整通常更小KB级。一个协程可以在执行过程中主动挂起yield将控制权交还给调度器并在未来某个时刻从挂起处恢复resume执行。2.2 从协程到Async/Awaitasync函数本质上就是一个协程。await点就是协程的挂起点yield。一个异步操作如read未完成时协程在此挂起。当事件循环Event Loop检测到该操作完成如文件描述符可读便恢复对应的协程继续执行。2.3 C语言实现的两种技术路线栈式协程Stackful Coroutine原理为每个协程分配独立的调用栈。挂起时保存当前栈上下文寄存器、栈指针等恢复时切换回来。上下文切换通常使用setjmp/longjmp或汇编直接操作寄存器如ucontext库或自制汇编。优点可以在嵌套函数中任意位置挂起对编程范式限制少。缺点每个协程需要独立栈空间内存开销相对大上下文切换涉及内存拷贝稍慢。代表libco,libtask。无栈协程Stackless Coroutine原理协程没有独立的调用栈其局部变量和挂起状态被放置在堆上通常是一个结构体。通过switch-case或goto标签来实现流程跳转。编译器如C20或宏将async/await函数重写为一个状态机。优点内存开销极小只有一个状态结构体切换速度极快。缺点协程内不能调用可能挂起的其他函数除非它们也被设计为无栈协程限制较多。代表C20协程的底层原理以及很多C语言的async/await宏实现。本文将重点讲解无栈协程的实现方式因为它更轻量且能清晰地揭示async/await语法糖是如何“脱糖”成状态机的理解成本相对较低。3. 环境准备与项目结构我们的目标是实现一个最简单的、基于无栈协程和宏的async/await框架。它包含一个事件循环来驱动异步任务的完成。环境要求操作系统Linux / macOS (Windows需使用WSL或MinGW)编译器GCC 或 Clang支持C11标准调试工具GDB (可选)项目文件结构c_async_demo/ ├── async.h // 核心宏定义、异步任务结构体声明 ├── async.c // 事件循环、调度器实现 ├── demo_http.c // 使用async/await的示例模拟HTTP请求 └── Makefile // 构建文件我们将从最核心的宏定义开始。4. 核心宏定义与异步任务结构在async.h中我们定义实现无栈协程async/await所需的核心数据结构和宏。// async.h #ifndef ASYNC_H #define ASYNC_H #include stdint.h // 异步任务状态 typedef enum { ASYNC_STATUS_PENDING, // 挂起等待中 ASYNC_STATUS_READY, // 就绪可执行 ASYNC_STATUS_DONE, // 完成 ASYNC_STATUS_ERROR // 错误 } async_status_t; // 异步任务控制块 (Async Task Control Block) typedef struct async_task { async_status_t status; int id; // 任务ID可用于调试或映射 void* context; // 指向保存局部变量和状态的结构体 int (*step)(struct async_task*); // 状态机步进函数 void* result; // 异步操作的结果 void* error; // 错误信息 } async_task_t; // 事件循环句柄 typedef struct event_loop event_loop_t; // --- 核心宏用于定义异步函数和等待点 --- // 这些宏会将一个函数重写为状态机 // 开始定义一个异步函数。它声明一个静态的步进函数和对应的上下文结构体。 #define ASYNC_FUNC(name) \ static int name##_step(async_task_t* _task); \ typedef struct { \ int _state; \ async_task_t* _task; // 异步函数的“函数体”开始。它初始化状态机并返回任务指针。 #define ASYNC_BEGIN(name) \ async_task_t* name() { \ async_task_t* task (async_task_t*)malloc(sizeof(async_task_t)); \ task-status ASYNC_STATUS_PENDING; \ task-step name##_step; \ typedef struct { int _state; } _ctx_t; \ task-context malloc(sizeof(_ctx_t)); \ ((_ctx_t*)(task-context))-_state 0; \ return task; \ } \ static int name##_step(async_task_t* _task) { \ typedef struct { int _state; // 声明一个局部变量它将被保存在上下文结构体中在挂起后依然有效。 #define ASYNC_LOCAL(type, var) type var; // 模拟 await。实际上是将一个子异步任务关联到当前任务并挂起当前任务。 // 这里做了极大简化我们假设 awaited_task 是一个已经创建好的 async_task_t*。 #define AWAIT(awaited_task) \ do { \ _task-status ASYNC_STATUS_PENDING; \ /* 在实际实现中这里需要建立父子任务关联由调度器处理 */ \ /* 为了简化我们直接检查子任务状态 */ \ if ((awaited_task)-status ! ASYNC_STATUS_DONE) { \ ((int*)(_task-context))[0] __LINE__; \ return __LINE__; \ } \ } while(0); \ switch(0) case 0: // 异步函数结束清理并标记任务完成。 #define ASYNC_END \ } *_ctx _task-context; \ _task-status ASYNC_STATUS_DONE; \ return -1; \ } // 简化版模拟一个立即完成的异步任务用于演示 async_task_t* async_dummy_done(); // 事件循环相关函数 event_loop_t* event_loop_create(); void event_loop_run(event_loop_t* loop); void event_loop_add_task(event_loop_t* loop, async_task_t* task); #endif // ASYNC_H关键点解释async_task_t这是异步任务的“身份证”和“病历本”。它保存了任务的状态、一个指向其所有局部变量上下文的指针、以及最重要的step函数即状态机。宏的魔法ASYNC_FUNC、ASYNC_BEGIN、ASYNC_LOCAL、AWAIT、ASYNC_END这一系列宏其作用是在编译前对代码进行“重写”。ASYNC_BEGIN会生成一个真正的C函数如my_async_func它负责创建并初始化一个async_task_t。同时它还会生成一个静态的_step函数如my_async_func_step。这个_step函数才是状态机的本体。原函数体中所有在ASYNC_LOCAL中声明的变量都会被放入由task-context指向的一个匿名结构体内从而在函数挂起后得以保存。AWAIT是核心。__LINE__宏记录了当前代码行号作为状态。当执行到AWAIT且子任务未完成时函数通过return __LINE__;挂起。下次被调度器恢复时_step函数会利用保存的_state即行号通过一个大的switch语句这里由宏隐式生成直接跳转到对应的case标签处继续执行。为了代码清晰上面的宏省略了完整的switch生成逻辑下一节会在.c文件中展示更完整的实现。5. 事件循环与调度器实现有了异步任务我们需要一个驱动它们执行的引擎——事件循环。在async.c中我们实现一个极简的调度器。// async.c #include async.h #include stdlib.h #include stdio.h #include unistd.h // 简单的事件循环结构 struct event_loop { async_task_t** tasks; int task_count; int task_capacity; }; event_loop_t* event_loop_create() { event_loop_t* loop (event_loop_t*)malloc(sizeof(event_loop_t)); loop-task_capacity 16; loop-tasks (async_task_t**)malloc(sizeof(async_task_t*) * loop-task_capacity); loop-task_count 0; return loop; } void event_loop_add_task(event_loop_t* loop, async_task_t* task) { if (loop-task_count loop-task_capacity) { loop-task_capacity * 2; loop-tasks (async_task_t**)realloc(loop-tasks, sizeof(async_task_t*) * loop-task_capacity); } loop-tasks[loop-task_count] task; task-status ASYNC_STATUS_READY; } // 极简调度轮询所有任务执行其 step 函数 void event_loop_run(event_loop_t* loop) { int active_tasks; do { active_tasks 0; for (int i 0; i loop-task_count; i) { async_task_t* task loop-tasks[i]; if (task-status ASYNC_STATUS_READY || task-status ASYNC_STATUS_PENDING) { active_tasks 1; int next_state task-step(task); if (next_state -1) { // 任务完成 task-status ASYNC_STATUS_DONE; printf(Task %d finished.\n, task-id); } else { // 任务挂起更新状态以便下次从此处恢复 // 注意这里简化处理实际应将next_state保存到context的_state中 task-status ASYNC_STATUS_PENDING; } } } // 在实际项目中这里应该调用如epoll_wait等系统调用等待IO事件而不是空转。 usleep(1000); // 睡眠1毫秒避免CPU空转 } while (active_tasks); } // 一个立即完成的模拟异步任务 async_task_t* async_dummy_done() { async_task_t* task (async_task_t*)malloc(sizeof(async_task_t)); task-status ASYNC_STATUS_DONE; task-step NULL; task-context NULL; return task; }这个事件循环是最简单的“忙等待”轮询仅用于演示原理。真实系统应基于epoll/kqueue/IOCP在IO事件就绪时才唤醒对应的任务实现真正的异步非阻塞。6. 完整示例模拟异步HTTP请求现在让我们在demo_http.c中使用上面的框架编写一个模拟异步HTTP请求的示例。为了聚焦于流程控制我们用sleep模拟网络延迟。// demo_http.c #include async.h #include stdio.h #include stdlib.h #include unistd.h #include string.h // 模拟一个异步的“网络读取”操作延迟后返回数据 async_task_t* async_http_get(const char* url) { // 使用宏定义异步函数 ASYNC_FUNC(http_get) ASYNC_BEGIN(http_get) // 声明异步函数内的“局部变量”它们会被保存到context中 ASYNC_LOCAL(int, elapsed_time); ASYNC_LOCAL(const char*, saved_url); saved_url url; printf([Task %d] Starting HTTP GET to %s\n, _task-id, saved_url); // 模拟连接建立耗时 elapsed_time 0; printf([Task %d] Connecting...\n, _task-id); while (elapsed_time 2) { // 模拟2个时间单位 // 这里本应AWAIT一个定时器或非阻塞IO。 // 我们简化直接sleep并让出控制权。 // 注意真实实现中sleep是阻塞的会卡住整个线程。 // 这里仅为演示流程假设有一个非阻塞的delay异步函数。 // 我们用一个假的、立即完成的任务来模拟一次“等待”。 async_task_t* dummy async_dummy_done(); AWAIT(dummy); free(dummy); elapsed_time; printf([Task %d] Connecting... %d/2\n, _task-id, elapsed_time); } // 模拟接收数据耗时 printf([Task %d] Receiving data...\n, _task-id); elapsed_time 0; while (elapsed_time 3) { // 模拟3个时间单位 async_task_t* dummy async_dummy_done(); AWAIT(dummy); free(dummy); elapsed_time; printf([Task %d] Receiving... %d/3\n, _task-id, elapsed_time); } // 模拟得到结果 _task-result strdup(htmlHello from simulated server!/html); printf([Task %d] HTTP GET finished.\n, _task-id); ASYNC_END // 调用上面宏生成的函数返回一个初始化的异步任务 return http_get(); } // 主异步函数并发发起多个HTTP请求 async_task_t* async_main() { ASYNC_FUNC(main) ASYNC_BEGIN(main) ASYNC_LOCAL(async_task_t*, req1); ASYNC_LOCAL(async_task_t*, req2); ASYNC_LOCAL(char*, response1); ASYNC_LOCAL(char*, response2); printf([Main Task] Launching concurrent HTTP requests...\n); // 发起第一个请求 req1 async_http_get(http://api.example.com/data1); req1-id 1; // 发起第二个请求 req2 async_http_get(http://api.example.com/data2); req2-id 2; printf([Main Task] Requests launched, waiting for both...\n); // 等待第一个请求完成 AWAIT(req1); response1 (char*)req1-result; printf([Main Task] Got response 1: %.30s...\n, response1); free(req1); // 等待第二个请求完成 AWAIT(req2); response2 (char*)req2-result; printf([Main Task] Got response 2: %.30s...\n, response2); free(req2); printf([Main Task] All requests completed successfully.\n); // 清理结果 free(response1); free(response2); ASYNC_END return main(); } int main() { printf( C语言 Async/Await 模拟演示 \n); // 创建事件循环 event_loop_t* loop event_loop_create(); // 创建主异步任务并加入事件循环 async_task_t* main_task async_main(); main_task-id 0; event_loop_add_task(loop, main_task); // 运行事件循环直到所有任务完成 event_loop_run(loop); // 清理 free(main_task); // 注意实际context和result需要更深度的清理此处简化 free(loop-tasks); free(loop); printf( 演示结束 \n); return 0; }7. 构建与运行创建一个简单的Makefile来编译和运行我们的示例# Makefile CC gcc CFLAGS -stdc11 -Wall -Wextra -g -I. TARGET demo OBJS async.o demo_http.o all: $(TARGET) $(TARGET): $(OBJS) $(CC) $(CFLAGS) -o $ $^ async.o: async.c async.h $(CC) $(CFLAGS) -c async.c demo_http.o: demo_http.c async.h $(CC) $(CFLAGS) -c demo_http.c clean: rm -f $(OBJS) $(TARGET) run: $(TARGET) ./$(TARGET) .PHONY: all clean run在终端中执行以下命令make clean make ./demo预期输出 C语言 Async/Await 模拟演示 [Main Task] Launching concurrent HTTP requests... [Task 1] Starting HTTP GET to http://api.example.com/data1 [Task 1] Connecting... [Task 2] Starting HTTP GET to http://api.example.com/data2 [Task 2] Connecting... [Main Task] Requests launched, waiting for both... [Task 1] Connecting... 1/2 [Task 2] Connecting... 1/2 [Task 1] Connecting... 2/2 [Task 2] Connecting... 2/2 [Task 1] Receiving data... [Task 2] Receiving data... [Task 1] Receiving... 1/3 [Task 2] Receiving... 1/3 [Task 1] Receiving... 2/3 [Task 2] Receiving... 2/3 [Task 1] Receiving... 3/3 [Task 2] Receiving... 3/3 [Task 1] HTTP GET finished. Task 1 finished. [Task 2] HTTP GET finished. Task 2 finished. [Main Task] Got response 1: htmlHello from simulated server!/ht... [Main Task] Got response 2: htmlHello from simulated server!/ht... [Main Task] All requests completed successfully. Task 0 finished. 演示结束 输出分析主任务启动并发起了两个HTTP GET模拟任务Task 1和Task 2。事件循环开始调度。由于我们的AWAIT宏和简易调度器两个任务交替执行你看到它们的打印信息交错出现模拟了并发。每个任务内部的“连接”和“接收”阶段都通过AWAIT点挂起和恢复从而让出执行权给另一个任务。两个子任务完成后主任务中的两个AWAIT依次获得结果最后主任务完成。整个过程没有创建任何线程所有“并发”都是在单线程内通过协作式任务切换完成的。8. 实现原理深度解析与关键问题上面的示例是一个高度简化的模型。要实现一个生产可用的C语言async/await框架需要解决以下几个关键问题8.1 状态机生成的完整形态我们简化了AWAIT宏中switch-case的生成。一个更完整的实现思路如下通常借助额外的代码生成工具或更复杂的宏// 伪代码展示async函数如何被“脱糖”为状态机 int my_async_func_step(async_task_t* task) { struct context { int state; int local_a; char* local_b; } *ctx task-context; switch(ctx-state) { case 0: goto LABEL_0; case __LINE__1: goto LABEL_1; // __LINE__1 是第一个AWAIT所在的行号 case __LINE__2: goto LABEL_2; // 第二个AWAIT所在的行号 default: return -1; } LABEL_0: // 函数开头代码 ctx-local_a 10; // ... // 第一个AWAIT ctx-state __LINE__1; if (sub_task_not_done) return __LINE__1; LABEL_1: // AWAIT之后的代码 // 第二个AWAIT ctx-state __LINE__2; if (sub_task_not_done) return __LINE__2; LABEL_2: // ... 函数结束 return -1; }编译器或预处理器的工作就是把开发者写的async函数转换成类似上面的_step函数。开源库如async.hby 云风或libdill中的go宏就实现了这种转换。8.2 真正的非阻塞I/O集成示例中用sleep和忙等待模拟延迟是低效的。真实框架必须与操作系统提供的异步I/O接口集成Linux:epollmacOS/BSD:kqueueWindows:IOCP事件循环的核心应是一个epoll_wait/kevent调用它阻塞直到注册的文件描述符上有事件发生可读、可写等。当一个异步读操作如async_read被调用时发起非阻塞的系统调用read(fd, buf, len)。如果返回EAGAIN或EWOULDBLOCK则将对应的文件描述符和期待的事件EPOLLIN注册到epoll实例并挂起当前任务。事件循环在epoll_wait返回后找到被唤醒的文件描述符进而找到等待它的任务将其状态设为就绪下次调度时该任务从AWAIT点之后恢复并再次尝试read此时应该能成功。8.3 任务调度与依赖管理我们的示例只有一个简单的轮询调度器。真实框架需要就绪队列存放状态为READY的任务。等待队列任务因AWAIT挂起时需要记录它在等待哪个子任务或哪个I/O事件。当子任务完成或I/O就绪时需要精确地唤醒父任务而不是盲目轮询。任务窃取在多核CPU上可以实现多个工作者线程从同一个任务队列中窃取任务执行实现并行。8.4 内存管理与资源清理上下文内存task-context指向的内存需要随任务创建而分配随任务结束而释放。我们的示例有内存泄漏。任务结果传递子任务的结果如何安全地传递给父任务需要定义清晰的所有权转移规则如移动语义或引用计数。错误传播async_task_t中的error字段应被充分利用当子任务出错时错误需要能沿着AWAIT链向上传播。9. 生产级框架推荐与选择自己实现完整的async/await框架是一个很好的学习过程但在实际项目中更推荐使用成熟的开源库。9.1 栈式协程库libco (来自腾讯): 高性能广泛应用于微信后台。接口简洁但需要使用者手动管理协程调度和I/O。libtask (来自Go语言早期实现者): 仿Go风格的并发库自带信道channel和调度器。Boost.Coroutine2: C库提供强大的栈式协程支持与Asio网络库集成良好。9.2 无栈协程/状态机库async.h (云风): 一个单头文件的C语言协程库通过宏实现await语义非常轻量适合学习和小型项目。libdill: 提供了类似Go语言的并发原语协程、信道其协程是无栈的性能极高。C20 Coroutines: 如果你是C开发者直接使用语言标准支持的协程是未来方向但当前编译器支持和生态仍在完善中。9.3 选择建议追求极致性能和学习底层选择async.h或libdill。需要与现有C项目集成选择Boost.Coroutine2或直接使用C20协程。需要成熟生态和复杂调度选择libco或libtask。嵌入式等资源极度受限环境无栈协程如async.h模式是更优选择。10. 总结C语言异步编程的演进思考在C语言中实现async/await是一次将现代高级语言编程范式回溯到系统编程基础的精彩实践。它向我们证明异步编程的核心是控制流管理而非语法本身。async/await只是让这种管理变得更符合人类线性思维的工具。C语言的强大在于其底层控制能力。通过宏、函数指针和内存操作我们完全可以构建出高效的并发抽象而不需要语言层面的原生支持。没有银弹。C语言的async/await实现需要开发者对底层机制上下文、状态机、事件循环有深刻理解否则调试将异常困难。它降低了业务逻辑的复杂度但增加了框架本身的复杂度。对于开发者而言理解这套机制的价值远大于使用它。它能让你在阅读Nginx、Redis等高性能C项目源码时更清晰地把握其事件驱动架构的精髓也能让你在使用Go、Rust、JavaScript等语言的异步特性时明白其底层究竟在做什么。最终是否要在C项目中使用此类框架取决于项目的规模、团队的技术栈以及对性能和可维护性的权衡。但对于任何一个致力于深入系统编程的开发者来说亲手实现或深入理解一遍这个过程无疑是一次宝贵的技术修炼。

相关新闻

C语言实现async/await:单线程高并发编程范式实践

C语言实现async/await:单线程高并发编程范式实践

在 C 语言的世界里,我们习惯了顺序执行、回调函数和状态机来处理异步操作。当看到 JavaScript、Python 等现代语言中优雅的async/await语法时,你是否想过,能否在 C 语言里也实现类似的“异步语法糖”?这篇文章要探讨的&#xff0c…

2026/10/18 1:41:38 阅读更多 →
Windows APK安装三步突破:零门槛实现跨平台应用无缝运行

Windows APK安装三步突破:零门槛实现跨平台应用无缝运行

Windows APK安装三步突破:零门槛实现跨平台应用无缝运行 【免费下载链接】APK-Installer An Android Application Installer for Windows 项目地址: https://gitcode.com/GitHub_Trending/ap/APK-Installer 你是否曾经想过在Windows电脑上直接运行安卓应用&a…

2026/10/16 4:02:56 阅读更多 →
极空间部署docker-wps-office:浏览器远程办公与公网访问

极空间部署docker-wps-office:浏览器远程办公与公网访问

目录 前言 1.docker-wps-office是什么? 2.前提条件 2.1ssh远程连接到极空间 2.2验证docker是否开启 3.安装Portainer 4.安装cpolar 5.配置公网地址 6.保留固定公网地址 总结 前言 在不同电脑之间处理Word、Excel和PPT文件时,最麻烦的不只是文档…

2026/10/18 1:24:03 阅读更多 →

最新新闻

前端面试必考:三次握手与四次挥手深度解析及性能优化实战

前端面试必考:三次握手与四次挥手深度解析及性能优化实战

1. 为什么前端面试总爱问三次握手和四次挥手1.1 一道题背后的考察逻辑面到一定年限之后,你会发现一个规律:越是基础的东西,越容易被反复问。三次握手和四次挥手就是典型。很多做前端的朋友跟我吐槽,说这玩意儿不是网络层的事吗&am…

2026/10/18 1:41:48 阅读更多 →
Burp 联动AI 一句话漏洞挖掘 1.0 (Claude+BurpMCP)部署+实战教程

Burp 联动AI 一句话漏洞挖掘 1.0 (Claude+BurpMCP)部署+实战教程

注意:3.0教学已出,更加高效且自动化,移步Burp 联动AI 一句话漏洞挖掘 3.0 (ClaudeBurpMCPChrome MCPsrc-hunter skills)部署实战教程 全自动化-彻底解放双手-CSDN博客 一、环境配置 需要自行搭建claude环境,我使用的是claudecc-sw…

2026/10/18 1:41:48 阅读更多 →
EtherCAT从站开发:SDO与PDO数据通道机制详解与实战避坑

EtherCAT从站开发:SDO与PDO数据通道机制详解与实战避坑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 1:41:48 阅读更多 →
STM32C5与CubeMX2实战评测:定位、迁移与避坑指南

STM32C5与CubeMX2实战评测:定位、迁移与避坑指南

1. 从一块新板子说起:为什么我要聊 STM32C5 和 CubeMX2前段时间拿到一块搭载 STM32C5 系列芯片的开发板,说实话第一反应是有点懵的——ST 的 MCU 产品线这些年已经密到让人记不住型号了,F 系列、L 系列、G 系列、H 系列、U 系列、W 系列&…

2026/10/18 1:41:48 阅读更多 →
RK3568 Android 14 内存优化实战:系统常驻内存从1.1GB压到890MB的211MB拆解

RK3568 Android 14 内存优化实战:系统常驻内存从1.1GB压到890MB的211MB拆解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 1:41:48 阅读更多 →
XERJ `_ml` 异常检测实战:Kibana ML 用户反馈驱动的透明统计异常检测指南

XERJ `_ml` 异常检测实战:Kibana ML 用户反馈驱动的透明统计异常检测指南

人工智能搜索引擎本地部署后端MCP 服务 【免费下载链接】xerj XERJ is the new way for AI to search data. Its autoindex capability activates agents to know your data without the token waste of grep and sed. One command indexes code, docs, logs and PDFs for sear…

2026/10/18 1:40:48 阅读更多 →

日新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 0:55:02 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 14:23:36 阅读更多 →