发布时间:2026/8/6 17:30:33
详解ucontext ucontext是一个POSIX / Unix 系统级 C API。在 C 里可以用它但本质上它属于#include ucontext.h它的作用是保存和切换程序执行上下文。可以把它理解成“手动保存当前函数执行现场然后跳到另一个执行现场”。它常被用来实现协程用户态线程green threadfiber简单调度器教学版操作系统/运行时但现代 C 项目里一般更推荐用 C20 coroutine、线程库、Boost.Context、Boost.Coroutine、libco 等。ucontext比较底层也有不少坑。一、什么是 context一个“执行上下文”大致包含这些东西当前 CPU 寄存器当前栈指针当前指令位置信号屏蔽字后续返回到哪里比如程序执行到这里foo();它的状态包括当前执行到哪一行、局部变量在哪个栈上、寄存器里有什么值、函数返回后去哪。ucontext可以把这些状态保存下来将来再恢复。二、ucontext_t核心类型是ucontext_t ctx;它通常包含这些字段具体结构由系统实现决定ucontext_t { ucontext_t* uc_link; sigset_t uc_sigmask; stack_t uc_stack; mcontext_t uc_mcontext; };你平时主要关心这几个ctx.uc_stack ctx.uc_link其中uc_stack这个上下文使用哪块栈。uc_link这个上下文对应的函数执行完之后回到哪个上下文。uc_mcontext底层机器寄存器状态一般不要直接碰。三、四个核心函数ucontext主要有四个函数int getcontext(ucontext_t* ucp); int setcontext(const ucontext_t* ucp); void makecontext(ucontext_t* ucp, void (*func)(), int argc, ...); int swapcontext(ucontext_t* oucp, const ucontext_t* ucp);分别是getcontext保存当前上下文getcontext(ctx);把当前执行状态保存到ctx里。注意如果以后用setcontext(ctx)恢复程序会像从getcontext返回一样继续执行。所以它有点像“时间存档点”。setcontext恢复某个上下文setcontext(ctx);跳转到ctx保存的执行状态。这个函数通常不会正常返回因为它直接切走了。makecontext创建一个新的执行上下文makecontext(ctx, func, argc, ...);它把一个ucontext_t初始化成以后切到这个上下文时从func函数开始执行。不过在调用makecontext之前通常要先getcontext(ctx); ctx.uc_stack.ss_sp stack_memory; ctx.uc_stack.ss_size stack_size; ctx.uc_link main_ctx; makecontext(ctx, func, 0);也就是说先getcontext给它分配一块栈设置函数执行完后回到哪里用makecontext指定入口函数swapcontext保存当前上下文并切到另一个上下文swapcontext(current_ctx, next_ctx);意思是把当前执行状态保存到current_ctx切换到next_ctx以后如果再次切回current_ctx程序会从swapcontext的下一行继续跑。这是实现协程调度最常用的函数。四、一个最小例子下面是一个简单的 C 示例主函数切到协程协程再切回来。#include ucontext.h #include iostream ucontext_t main_ctx; ucontext_t task_ctx; char task_stack[1024 * 64]; void task_function() { std::cout task: start\n; std::cout task: switch back to main\n; swapcontext(task_ctx, main_ctx); std::cout task: resumed\n; std::cout task: end\n; } int main() { getcontext(task_ctx); task_ctx.uc_stack.ss_sp task_stack; task_ctx.uc_stack.ss_size sizeof(task_stack); task_ctx.uc_stack.ss_flags 0; task_ctx.uc_link main_ctx; makecontext(task_ctx, task_function, 0); std::cout main: switch to task\n; swapcontext(main_ctx, task_ctx); std::cout main: back from task\n; std::cout main: switch to task again\n; swapcontext(main_ctx, task_ctx); std::cout main: done\n; }可能输出main: switch to task task: start task: switch back to main main: back from task main: switch to task again task: resumed task: end main: done关键点是这里swapcontext(task_ctx, main_ctx);协程主动让出执行权回到主函数。然后主函数再次swapcontext(main_ctx, task_ctx);协程会从上次暂停的位置继续执行也就是std::cout task: resumed\n;这就是“有栈协程”的感觉。五、uc_link的作用这行很重要task_ctx.uc_link main_ctx;它表示当task_function()执行结束后自动切回main_ctx。如果写成task_ctx.uc_link nullptr;那么task_function()结束后整个线程通常就结束了。举个例子void task_function() { std::cout task end\n; }如果uc_link main_ctx函数结束后回到main。如果uc_link nullptr函数结束后可能直接退出当前线程。六、栈是你自己管理的ucontext是“有栈上下文”。每个通过makecontext创建的上下文通常需要自己的栈char stack[64 * 1024]; ctx.uc_stack.ss_sp stack; ctx.uc_stack.ss_size sizeof(stack);这意味着栈太小会栈溢出栈内存必须在上下文运行期间一直有效不能用已经销毁的局部数组作为栈多个 context 不能随便共享同一块栈比如这样是危险的ucontext_t create_task() { ucontext_t ctx; char stack[64 * 1024]; ctx.uc_stack.ss_sp stack; return ctx; }因为stack是局部变量函数返回后就失效了。更常见的做法是std::vectorchar stack(64 * 1024);或者char* stack new char[64 * 1024];但要注意释放时机。七、用它实现简单协程可以抽象成这样struct Coroutine { ucontext_t ctx; std::vectorchar stack; bool finished false; };主调度器维护多个Coroutine每个协程自己在适当的时候调用yield()void yield() { swapcontext(current-ctx, scheduler_ctx); }调度器再切到另一个协程swapcontext(scheduler_ctx, next-ctx);这种模型叫协作式调度。也就是说切换发生在协程主动让出 CPU 的时候而不是系统强制抢占。类似task A running task A yield scheduler task B running task B yield scheduler task A resume八、 和线程的区别ucontext实现的是用户态上下文切换不是操作系统线程。和std::thread对比项目ucontextstd::thread调度者你自己操作系统是否并行通常不并行可以多核并行切换方式主动yield抢占式调度开销较低较高栈自己管理系统管理安全性坑多更标准可移植性差好很多ucontext更像是“一个线程里模拟多个执行流”。它不能自动利用多核。除非你自己在多个 OS 线程里分别跑多个调度器。九、 和 C20 coroutine 的区别C20 coroutine 是语言级协程但它通常是无栈协程。ucontext是有栈协程。区别很大项目ucontextC20 coroutine类型有栈协程无栈协程暂停位置几乎任意函数层级只能在 coroutine 内co_await/co_yield栈管理手动分配编译器生成 coroutine frame标准性非 C 标准C 标准可移植性较差较好性能模型接近 fiber接近状态机举个直观例子。ucontext可以这样void deep() { yield(); } void middle() { deep(); } void task() { middle(); }只要yield()里切上下文整个调用栈都能暂停。但 C20 coroutine 不是这样。你不能在普通函数深处随便暂停整个调用栈相关函数要参与 coroutine 机制。十、makecontext参数的坑makecontext可以传参数makecontext(ctx, func, 1, value);但这个 API 很老参数传递有历史坑。它原本更适合传int级别的值不保证安全传递指针。比如void func(void* p);你可能想这么写makecontext(ctx, (void (*)())func, 1, ptr);在某些系统上能跑但严格来说可移植性不好尤其是 64 位指针和int参数大小不一致时。很多代码会这么做makecontext(ctx, reinterpret_castvoid (*)()(entry), 1, arg);在 Linux/glibc 某些平台上通常能工作但不要把它当成跨平台保证。更稳一点的设计是入口函数不传复杂参数用全局/静态调度器找到当前协程对象或使用平台已知支持的封装库例如 Boost.Context十一、一个带 yield 的小例子#include ucontext.h #include iostream #include vector ucontext_t main_ctx; ucontext_t coro_ctx; std::vectorchar coro_stack(64 * 1024); void yield_to_main() { swapcontext(coro_ctx, main_ctx); } void coroutine_body() { std::cout coro: 1\n; yield_to_main(); std::cout coro: 2\n; yield_to_main(); std::cout coro: 3\n; } int main() { getcontext(coro_ctx); coro_ctx.uc_stack.ss_sp coro_stack.data(); coro_ctx.uc_stack.ss_size coro_stack.size(); coro_ctx.uc_stack.ss_flags 0; coro_ctx.uc_link main_ctx; makecontext(coro_ctx, coroutine_body, 0); std::cout main: resume coro\n; swapcontext(main_ctx, coro_ctx); std::cout main: resume coro\n; swapcontext(main_ctx, coro_ctx); std::cout main: resume coro\n; swapcontext(main_ctx, coro_ctx); std::cout main: done\n; }输出类似main: resume coro coro: 1 main: resume coro coro: 2 main: resume coro coro: 3 main: done这就已经有点像手写协程了。十二、getcontext的一个经典陷阱看这个代码ucontext_t ctx; int main() { getcontext(ctx); std::cout hello\n; setcontext(ctx); }这会怎样它可能无限输出hello hello hello ...因为setcontext(ctx)恢复到了getcontext(ctx)刚返回的位置。于是继续打印hello再setcontext又回去。所以如果用getcontext/setcontext通常需要一个额外状态变量防止重复int resumed 0; getcontext(ctx); if (!resumed) { resumed 1; setcontext(ctx); }但协程里更常用的是swapcontext逻辑会清楚很多。十三、 生命周期问题这是ucontext最容易出 bug 的地方。比如一个 context 正在使用这块栈std::vectorchar stack;如果你销毁了stack但以后又切回那个 context就炸了。类似Coroutine* c new Coroutine; delete c; swapcontext(main_ctx, c-ctx); // 严重错误要保证context 对象还活着栈内存还活着栈足够大不要切到已经结束的 context不要重复使用已经无效的上下文实际项目里通常要给协程加状态enum class State { Ready, Running, Suspended, Finished };

相关新闻

2026/8/6 17:30:33

Mysql:count(1)和count(*)和count(主键字段)有什么区别?

一、三种写法分别是什么意思1. COUNT(*)SELECT COUNT(*) FROM users;表示:统计结果集中的行数,不管这一行的字段是否为 NULL。这是最标准、最清晰的“统计行数”写法。2. COUNT(1)SELECT COUNT(1) FROM users;1 是一个永远不为 NULL 的常量。对于结果集中…

2026/8/6 17:30:33

VisualCppRedist AIO:终极Windows运行库修复工具完全指南

VisualCppRedist AIO:终极Windows运行库修复工具完全指南 【免费下载链接】vcredist AIO Repack for latest Microsoft Visual C Redistributable Runtimes 项目地址: https://gitcode.com/gh_mirrors/vc/vcredist 你是否曾因"找不到MSVCP140.dll"…

2026/8/6 17:30:33

2026最新!英语老师都在用的1款听说软件推荐

【摘要:】 我深耕英语听说领域教研5年,踩过不少智能工具的坑,今天结合实测数据拆解英语听说训练的核心痛点,分析主流AI工具的技术逻辑,给老师、学生做客观选型参考,无广告全是实操经验。英语听说教学的真实…

2026/8/6 20:40:48

4A统一安全管理平台:构建企业身份与访问管理的核心防线

1. 从“各自为战”到“统一作战”:为什么我们需要4A平台?在网络安全领域干了十几年,我见过太多企业安全建设的“名场面”:运维人员离职,留下一堆没人知道密码的服务器;新员工入职,申请十几个系统…

2026/8/6 20:40:48

LLM工具调用(Function Calling、MCP、Skill、CLI的区别)

什么是Function Calling?原理是什么工具定义使用JSON Schema描述,description字段是LLM判断是否调用的核心依据;运行时是「两轮对话中间执行」的闭环流程;模型通过finish_reason为tool_calls来明确告知需要工具帮助;以…

2026/8/6 20:40:48

MySQL索引失效的7种常见场景与优化方案

1. 索引失效的典型表现与诊断方法当数据库查询性能突然下降时,索引失效往往是首要怀疑对象。一个明显的迹象是原本毫秒级响应的查询突然需要数秒甚至更长时间完成。通过EXPLAIN命令分析执行计划时,如果发现type列显示为"ALL"(全表扫…

2026/8/6 20:40:48

ACDC心脏诊断数据集

摘要:ACDC 数据集包含 150 名患者(五类心脏病理:正常、心梗、扩张型心肌病、肥厚型心肌病、右心室异常),MRI 电影图像(NIfTI 格式,ED/ES 帧),训练集 100 名,测…

2026/8/6 20:35:46

如何快速上手LipNet:从安装到实现唇语识别的完整指南

如何快速上手LipNet:从安装到实现唇语识别的完整指南 【免费下载链接】LipNet Keras implementation of LipNet: End-to-End Sentence-level Lipreading 项目地址: https://gitcode.com/gh_mirrors/lip/LipNet LipNet是一个基于Keras实现的端到端句子级唇语识…

2026/8/5 3:13:11

如何用免费工具突破游戏窗口限制:SRWE完整使用指南

如何用免费工具突破游戏窗口限制:SRWE完整使用指南 【免费下载链接】SRWE Simple Runtime Window Editor 项目地址: https://gitcode.com/gh_mirrors/sr/SRWE 你是否遇到过这样的困扰?想为心爱的游戏截图,却发现游戏不支持自定义分辨率…

2026/8/6 0:04:22

电力系统调度中的源荷不确定性建模与优化实践

1. 电力系统调度中的源荷不确定性挑战现代电力系统正面临前所未有的复杂性,其中源荷不确定性(Source-Load Uncertainty)已成为调度决策中最棘手的难题之一。我在参与某省级电网调度系统升级时,曾遇到风电预测误差导致日内调度计划…

2026/8/6 0:04:22

VGG-T3技术解析:3D重建速度的革命性突破

1. 项目概述:VGG-T3如何重新定义3D重建速度在计算机视觉领域,3D场景重建一直是个计算密集型任务。传统方法重建1000帧图像规模的场景往往需要数小时甚至更长时间,而英伟达最新发布的VGG-T3技术将这个时间压缩到了惊人的54秒。这个突破性进展来…

2026/8/6 0:04:22

深度解析旅游网站建设的意义及其对行业发展的深远影响与核心价值体现

在这个数字化浪潮席卷全球的今天,我们似乎已经忘记了,曾经有一段时间,人们想要去一个陌生的地方,只能靠在书桌前翻阅厚厚的旅游杂志,或者向刚从那里回来的朋友询问那些模糊不清的印象。那时候,“远方”是一个需要精打细算才能抵达的奢侈概念。而现在,只需要一部手机,轻…

2026/8/5 19:21:13

实测才敢推 AI论文网站 2026最新测评与推荐

2026年真正好用的AI论文网站,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。一、综…

2026/8/5 19:21:13

2026必备!AI论文网站测评:最新推荐与深度对比

2026年真正好用的AI论文网站,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。 一、…

2026/8/5 19:21:13

摆脱论文困扰!盘点2026年全网爆红的的AI论文写作工具

一天写完毕业论文在2026年已不再是天方夜谭。2026年最炸裂、实测能大幅提速的AI论文写作工具,覆盖选题构思、文献整理、内容生成、格式排版等核心场景,真正帮你高效搞定论文难题。 一、全流程王者:一站式搞定论文全链路(一天定稿首…