详解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 };

相关新闻

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

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:31 阅读更多 →
VisualCppRedist AIO:终极Windows运行库修复工具完全指南

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:31 阅读更多 →
2026最新!英语老师都在用的1款听说软件推荐

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

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

2026/8/6 17:30:30 阅读更多 →

最新新闻

3步玩转Reloaded-II:零基础开启你的游戏模组之旅

3步玩转Reloaded-II:零基础开启你的游戏模组之旅

3步玩转Reloaded-II:零基础开启你的游戏模组之旅 【免费下载链接】Reloaded-II Universal .NET Core Powered Modding Framework for any Native Game X86, X64. 项目地址: https://gitcode.com/gh_mirrors/re/Reloaded-II 你是否曾经为心爱的游戏寻找新玩法…

2026/8/6 18:28:56 阅读更多 →
崩坏星穹铁道自动化助手:三月七小助手终极使用教程

崩坏星穹铁道自动化助手:三月七小助手终极使用教程

崩坏星穹铁道自动化助手:三月七小助手终极使用教程 【免费下载链接】March7thAssistant 崩坏:星穹铁道全自动 三月七小助手 项目地址: https://gitcode.com/gh_mirrors/ma/March7thAssistant 还在为《崩坏:星穹铁道》中重复的日常任务…

2026/8/6 18:28:56 阅读更多 →
终极Windows激活指南:5分钟免费激活微软全家桶的完整方案

终极Windows激活指南:5分钟免费激活微软全家桶的完整方案

终极Windows激活指南:5分钟免费激活微软全家桶的完整方案 【免费下载链接】Microsoft-Activation-Scripts Open-source Windows and Office activator featuring HWID, Ohook, TSforge, and Online KMS activation methods, along with advanced troubleshooting. …

2026/8/6 18:28:56 阅读更多 →
设置窗口图标

设置窗口图标

import sys from PyQt5.QtWidgets import QApplication, QWidget, QStyle from PyQt5.QtGui import QIconif __name__ "__main__":# 创建应用对象app QApplication(sys.argv)# 创建主窗口window QWidget()window.setWindowTitle("设置窗口图标示例")win…

2026/8/6 18:28:56 阅读更多 →
告别手速焦虑:大麦自动抢票系统5分钟上手指南

告别手速焦虑:大麦自动抢票系统5分钟上手指南

告别手速焦虑:大麦自动抢票系统5分钟上手指南 【免费下载链接】ticket-purchase 大麦自动抢票,支持人员、城市、日期场次、价格选择 项目地址: https://gitcode.com/GitHub_Trending/ti/ticket-purchase 还在为抢不到热门演唱会门票而烦恼吗&…

2026/8/6 18:28:56 阅读更多 →
OSkhQuant:让量化交易像搭积木一样简单

OSkhQuant:让量化交易像搭积木一样简单

OSkhQuant:让量化交易像搭积木一样简单 【免费下载链接】OSkhQuant 看海量化回测系统(看海量化交易系统)开源代码,khQuant框架,实现A股可视化回测,全部开源。 项目地址: https://gitcode.com/gh_mirrors/…

2026/8/6 18:27:56 阅读更多 →

日新闻

深入解析LimboAI C++内核:架构设计与性能优化实战

深入解析LimboAI C++内核:架构设计与性能优化实战

1. 项目概述:为什么我们需要深入LimboAI的C内核?如果你是一名使用Godot引擎的游戏开发者,尤其是对AI行为逻辑有较高要求的项目,那么LimboAI这个名字你大概率不会陌生。它作为Godot 4生态中一个备受瞩目的行为树与状态机插件&#…

2026/8/6 0:00:06 阅读更多 →
Unity 2D游戏敌人AI系统:基于PlayMaker状态机与2D Toolkit的实战开发

Unity 2D游戏敌人AI系统:基于PlayMaker状态机与2D Toolkit的实战开发

1. 项目概述与核心思路大家好,我是老张,一个在游戏开发一线摸爬滚打了十多年的老码农。今天咱们接着聊《空洞骑士》风格2D动作游戏的Demo制作。上一期我们搭好了基础框架,处理了角色移动和碰撞,这一期,我们要让游戏世界…

2026/8/6 0:00:06 阅读更多 →
被动防火门市场前景发展趋势

被动防火门市场前景发展趋势

被动防火门依靠材质结构、密闭构造阻隔烟火蔓延,无需电控启动,是建筑被动消防系统核心构件,行业依托新规管控、城市更新、工业安全升级迎来稳定扩容,整体朝着合规化、专项化、低碳化、智能化方向发展。现阶段 GB12955‑2024 新版国…

2026/8/6 0:00:06 阅读更多 →

周新闻

最大流算法详解:从水管网络到Ford-Fulkerson与Dinic实战

最大流算法详解:从水管网络到Ford-Fulkerson与Dinic实战

1. 从水管网络到最大流:一个核心问题的诞生想象一下,你是一个城市供水系统的总工程师。你的城市有多个水源(水库),需要通过一个复杂的地下管道网络,将水输送到各个居民区。每条管道都有其最大通水能力&…

2026/8/5 15:00:43 阅读更多 →
基于Springboot的企业门户网站(源码+LW+调试文档+讲解)

基于Springboot的企业门户网站(源码+LW+调试文档+讲解)

温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台…

2026/8/5 13:13:56 阅读更多 →
MATLAB xcorr函数详解:从互相关原理到四大实战应用

MATLAB xcorr函数详解:从互相关原理到四大实战应用

1. 从一次信号“找茬”说起:为什么我们需要互相关几年前,我在处理一组声学传感器数据时遇到了一个棘手的问题。我有两个麦克风记录了一段相同的音频信号,理论上它们接收到的声音波形应该非常相似,只是由于麦克风位置不同&#xff…

2026/8/5 10:20:36 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/5 23:28:39 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗?ncmdump解密工具帮你轻松解决这个困…

2026/8/5 21:00:14 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片:为英语学习 App 打造桌面级学习助手适用平台:HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0(API 26 Beta)新增了 AgentCard 智能体卡片能力,这是继 HMAF(鸿蒙智能体框架&#x…

2026/8/5 23:46:51 阅读更多 →