windows 驱动实例分析系列: wintun驱动分析-api篇(三)
Wintun API 模块深度解析文档三会话管理与数据路径一、概述本文档聚焦session.c它实现了 Wintun 数据会话的全部功能包括会话的启动与结束、数据包的接收与发送。数据路径是整个 Wintun 性能的关键所在其设计充分利用了 Windows 内核驱动与用户态通过共享环形缓冲区通信的机制并实现了高效的无锁并发。主要文件session.c以及公开头文件wintun.h中对应的函数声明。二、会话结构TUN_SESSIONtypedefstruct_TUN_SESSION{ULONG Capacity;struct{ULONG Tail;ULONG TailRelease;ULONG PacketsToRelease;CRITICAL_SECTION Lock;}Receive;struct{ULONG Head;ULONG HeadRelease;ULONG PacketsToRelease;CRITICAL_SECTION Lock;}Send;TUN_REGISTER_RINGS Descriptor;HANDLE Handle;}TUN_SESSION;Capacity环形缓冲区容量用户指定的值必须是 2 的幂。Receive与Send分别对应发送方向和接收方向注意命名容易混淆应用程序调用WintunAllocateSendPacket准备发送数据包时实际上是向接收环驱动读出的环写入数据而WintunReceivePacket是从发送环驱动写入的环读取。之所以这样命名是因为从驱动的视角看应用程序“接收”的包来自驱动“发送”的环反之亦然。为清晰起见下文将按功能描述应用程序发送AllocateSendPacket/SendPacket操作的是Receive方向因为驱动会从这个环“接收”应用程序的数据。应用程序接收ReceivePacket/ReleaseReceivePacket操作的是Send方向驱动将数据“发送”到这个环供应用程序读取。每个方向都有自己的环形缓冲区指针Descriptor.Send.Ring/Descriptor.Receive.Ring、尾指针Tail、释放指针TailRelease或HeadRelease、待释放包计数以及一个临界区锁保护内部状态。Descriptor包含了发送环和接收环的描述信息基址、大小、尾移动事件句柄用于传递给驱动。Handle是设备对象句柄用于执行 IOCTL。三、会话启动WintunStartSession3.1 参数校验与内存分配检查Capacity是否在WINTUN_MIN_RING_CAPACITY和WINTUN_MAX_RING_CAPACITY之间且为 2 的幂由调用方保证但未显式校验。计算每个环的大小RingSize TUN_RING_SIZE(Capacity)即sizeof(TUN_RING) Capacity (TUN_MAX_PACKET_SIZE - TUN_ALIGNMENT)。这是为了容纳环形数据、数据包对齐以及最大包大小。使用VirtualAlloc分配两倍RingSize的内存AllocatedRegion原因是驱动要求发送环和接收环在物理上连续实际上代码将这段内存分为两个连续的环发送环位于基址接收环位于基址 RingSize。分配双倍大小是为了确保两个环对齐并且VirtualFree可一次性释放。3.2 事件对象创建为发送环和接收环各创建一个手动重置事件CreateEventW用于通知对方尾指针已移动。这些事件句柄被填入Descriptor.Send.TailMoved和Descriptor.Receive.TailMoved驱动会使用它们来唤醒等待的线程。3.3 打开设备对象调用AdapterOpenDeviceObject获取设备句柄该函数内部通过CreateFileW打开之前获得的设备接口文件。3.4 IOCTL 注册环形缓冲区执行DeviceIoControl控制码TUN_IOCTL_REGISTER_RINGS自定义 CTL_CODE传递TUN_REGISTER_RINGS结构包含两个环的基址、大小和事件句柄。驱动收到此 IOCTL 后将内存映射到内核地址空间并保存事件对象引用。若成功则驱动开始接受数据包。3.5 初始化内部状态设置Capacity。初始化两个临界区Receive.Lock和Send.Lock并指定自旋计数LOCK_SPIN_COUNT0x10000提高多核性能。返回TUN_SESSION指针。四、数据包接收应用程序读取4.1WintunReceivePacket此函数从发送环驱动写入数据的环中取出一个包。流程进入Send.Lock临界区。检查Session-Send.Head是否已到达Capacity若大于等于则驱动可能已终止返回ERROR_HANDLE_EOF。读取驱动更新的Tail指针ReadULongAcquire(Session-Descriptor.Send.Ring-Tail)。该指针由驱动更新指向环尾。如果Head Tail表示无新数据返回ERROR_NO_MORE_ITEMS。计算BuffContent TUN_RING_WRAP(Tail - Head, Capacity)即环中可用数据字节数。从Data[Head]处读取TUN_PACKET头部包含Size字段检查大小是否合法≤ WINTUN_MAX_IP_PACKET_SIZE。计算对齐后的包大小AlignedPacketSize TUN_ALIGN(sizeof(TUN_PACKET) BuffPacket-Size)确保不超过BuffContent。将PacketSize输出返回BuffPacket-Data指针并将Head向前移动AlignedPacketSize按容量回绕。增加PacketsToRelease计数表示有一个包待释放。离开临界区。注意函数返回的指针直接指向共享内存因此应用程序在释放前必须尽快处理或拷贝数据因为后续调用可能会覆盖该区域。4.2WintunReleaseReceivePacket当应用程序处理完包后必须调用此函数释放缓冲区。流程进入Send.Lock。通过指针反算TUN_PACKET首地址BuffPacket (TUN_PACKET *)(Packet - offsetof(TUN_PACKET, Data))。将该包的Size与TUN_PACKET_RELEASE0x80000000进行位或操作标记为“待释放”。这样可以延迟实际更新Head直到有足够的待释放包可以成批更新。然后进入一个循环从当前HeadRelease位置开始检查每个包的释放标志若已释放则累加AlignedPacketSize并减少PacketsToRelease直到遇到未释放的包。将更新后的HeadRelease写入环形区的Head字段WriteULongRelease通知驱动这些位置已被释放。离开临界区。这种批量释放设计减少了频繁写共享内存的开销提高了性能。五、数据包发送应用程序写入5.1WintunAllocateSendPacket为要发送的数据包分配空间从接收环中取空闲区域。流程进入Receive.Lock。检查Session-Receive.Tail是否超限。计算对齐后的包大小AlignedPacketSize。读取驱动更新的Head指针ReadULongAcquire(Session-Descriptor.Receive.Ring-Head)它指示驱动已处理到的位置。计算可用空间BuffSpace TUN_RING_WRAP(Head - Tail - TUN_ALIGNMENT, Capacity)减去一个对齐单位是为了避免Tail与Head完全相等时无法区分空和满。若AlignedPacketSize BuffSpace返回ERROR_BUFFER_OVERFLOW环形缓冲区满。在Tail位置写入TUN_PACKET头部设置Size PacketSize | TUN_PACKET_RELEASE初始标记为待释放。返回Data指针并将Tail前移。增加PacketsToRelease计数。5.2WintunSendPacket将填充好的数据包提交给驱动。流程进入Receive.Lock。通过Packet反算TUN_PACKET清除TUN_PACKET_RELEASE标志表示该包已准备好被驱动读取。进入循环检查当前位置TailRelease处的包是否已准备就绪即没有释放标志若是则累加其大小并前移TailRelease减少PacketsToRelease。完成批量提交后用WriteULongRelease更新环形区的Tail字段。内存屏障MemoryBarrier()确保之前的写入对驱动可见。检查环形区的Alertable字段若为真则触发SetEvent唤醒等待的驱动线程驱动可能通过事件等待新数据。离开临界区。六、环形缓冲区管理细节6.1 对齐与填充TUN_ALIGNMENT为sizeof(ULONG)4 字节保证所有指针自然对齐。每个包包括头部在环中的存储占用的实际大小是TUN_ALIGN(sizeof(TUN_PACKET) PacketSize)确保下一个包从对齐地址开始。环容量Capacity必须是 2 的幂以便使用位与运算实现快速回绕。6.2 原子操作与内存序使用ReadULongAcquire/WriteULongRelease等原语在 Windows 中通常通过Interlocked或volatile加内存屏障实现确保跨线程/跨进程的可见性。共享内存的Head、Tail字段被声明为volatile ULONG但为了确保多核顺序代码中使用了显式的MemoryBarrier()和ReadAcquire/WriteRelease语义这些宏在 WDK 中定义但在用户态需要自行实现这里可能是通过_InterlockedCompareExchange等替代。6.3 事件通知当驱动有数据可读时它会设置Send.Ring-Tail并触发Send.TailMoved事件应用程序可以通过WintunGetReadWaitEvent获取该事件句柄在ReceivePacket返回ERROR_NO_MORE_ITEMS后等待。当应用程序提交新数据时SendPacket会检查Receive.Ring-Alertable并可能触发Receive.TailMoved事件驱动同样可以等待该事件。6.4 线程安全每个方向使用单独的临界区允许同时进行发送和接收互不干扰。所有函数在必要时获取锁确保对内部计数和指针的修改是原子的。七、性能优化特性批量处理释放和提交时将多个包的指针更新合并为一次写操作减少与驱动共享内存的交互次数。无锁头尾指针共享内存的Head/Tail由驱动和用户态分别以顺序一致性语义更新避免锁争用。固定容量环形缓冲区大小在会话创建时确定避免动态扩展带来的开销。直接内存访问数据包指针直接指向共享内存避免内核态与用户态间的数据拷贝仅需一次拷贝从网卡到共享内存或反之。八、与驱动交互的 IOCTL自定义控制码TUN_IOCTL_REGISTER_RINGS的定义为CTL_CODE(51820U, 0x970U, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)其中51820是 Wintun 的专属设备类型码。驱动解析此 IOCTL 后将用户空间的内存地址锁定并映射到内核同时保存事件句柄以便后续信号通知。九、总结session.c实现了 Wintun 最核心的数据传输路径其设计体现了极致的性能追求共享环形缓冲区消除了内核-用户态上下文切换和数据拷贝的开销。批量处理与无锁原子操作充分利用多核 CPU。事件机制允许高效等待避免忙等。正是这一层的高效实现使得 Wintun 能够达到远超传统 TAP 驱动的吞吐量如前文所述实测可达 700 Mbps。下一篇文章将介绍支撑这一切的辅助基础设施日志、命名空间、注册表、资源提取和 WOW64 代理。

相关新闻

蒸馏争议升温:Kimi K3被点名背后的技术话语权之争

蒸馏争议升温:Kimi K3被点名背后的技术话语权之争

导语:一个反常识的现象正在发生——当美国官员试图用“蒸馏”这个技术词汇给中国AI公司贴上“抄袭”标签时,最先站出来反对的,不是中国企业,而是近200家美国AI企业。他们联名反对切断中国开源模型的访问,理由是&#x…

2026/10/6 9:11:03 阅读更多 →
三星Q2利润创纪录:HBM4如何重塑存储利润分配

三星Q2利润创纪录:HBM4如何重塑存储利润分配

一个"卖内存条"的公司,交出了AI芯片公司级别的成绩单 2026年第二季度,三星电子交出了一份让整个半导体行业侧目的财报:单季营业利润89.49万亿韩元(约合640亿美元),同比增长1814%,创下…

2026/10/16 2:52:51 阅读更多 →
从文档解析到RAG系统:让ChatGPT掌握私有知识的完整指南

从文档解析到RAG系统:让ChatGPT掌握私有知识的完整指南

1. 项目概述:为什么需要将文档喂给ChatGPT?最近身边不少朋友和同事都在问我一个挺实际的问题:“我手里有一堆PDF报告、Word文档,甚至是一大堆会议纪要,怎么才能让ChatGPT帮我分析、总结或者回答里面的问题?…

2026/10/7 20:31:56 阅读更多 →

最新新闻

airi 项目中的 VueUse useWebWorker 实战指南:Web Worker 注册、通信与前端 AI 推理卸载

airi 项目中的 VueUse useWebWorker 实战指南:Web Worker 注册、通信与前端 AI 推理卸载

人工智能AI AgentMCP 服务工具调用GUI 自动化语音数字人交互助手 【免费下载链接】airi 💖🧸 自托管、归你拥有的 Grok 风格 AI 伴侣与 waifu / 赛博生命灵魂容器,目标是接近 Neuro-sama 的高度;支持实时语音聊天、Minecraft 和 F…

2026/10/16 5:33:37 阅读更多 →
WinPython Zero 发行版 3.7.4.1(32bit)变更日志解读:从 History 文件看 Python 包的升级脉络

WinPython Zero 发行版 3.7.4.1(32bit)变更日志解读:从 History 文件看 Python 包的升级脉络

开发工具构建工具CLI包管理器 【免费下载链接】winpython A free Python-distribution for Windows platform, including prebuilt packages for Scientific Python. 项目地址: https://gitcode.com/gh_mirrors/wi/winpython 点击查看 免费下载 本文以 WinPython 仓…

2026/10/16 5:33:37 阅读更多 →
asc-devkit Tiling 调测实战:ContextBuilder::NodeIoNum 声明算子输入输出个数与内部实现解析

asc-devkit Tiling 调测实战:ContextBuilder::NodeIoNum 声明算子输入输出个数与内部实现解析

人工智能深度学习算子库CANNAscend 【免费下载链接】asc-devkit 本项目是CANN 推出的昇腾AI处理器专用的算子程序开发语言,原生支持C和C标准规范,主要由类库和语言扩展层构成,提供多层级API,满足多维场景算子开发诉求。 项目地址&…

2026/10/16 5:33:37 阅读更多 →
信号与频谱入门:从时域波形到傅里叶变换的底层逻辑

信号与频谱入门:从时域波形到傅里叶变换的底层逻辑

/* 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 5:33:37 阅读更多 →
数据仓库分层实战:ODS、DW、DM三层架构设计与避坑指南

数据仓库分层实战:ODS、DW、DM三层架构设计与避坑指南

/* 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 5:33:37 阅读更多 →
零安装体验memory_graph:Web Debugger与Jupyter/Colab/Marimo集成的完整指南

零安装体验memory_graph:Web Debugger与Jupyter/Colab/Marimo集成的完整指南

【免费下载链接】memory_graph Teaching tool and debugging aid in context of references, mutable data types, and shallow and deep copy. 项目地址: https://gitcode.com/gh_mirrors/me/memory_graph 点击查看 免费下载 想用 memory_graph 看懂 Python 中的引…

2026/10/16 5:32:37 阅读更多 →

日新闻

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

/* 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 0:00:05 阅读更多 →
信号与系统频域分析作业解题指南

信号与系统频域分析作业解题指南

1. 作业背景与核心要求解析信号与系统作为电子信息类专业的核心基础课,其作业设计往往直接关联课程的核心知识框架。2026年春季第六次作业的布置,通常对应着课程进展到频域分析的关键阶段。根据常规教学进度推测,本次作业可能涉及以下核心内容…

2026/10/16 0:00:05 阅读更多 →
Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

1. 从一个"四不像"的练手项目说起如果你已经看完了Django的官方教程前几章,跟着敲完了投票应用,大概会有一种"我好像会了,又好像什么都不会"的割裂感。教程里每个知识点都是拆开的——这一章讲模型,下一章讲视…

2026/10/16 0:00:05 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/15 16:18:54 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/15 19:41:51 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/15 13:48:15 阅读更多 →

月新闻

我发现了一个新思路:用 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/15 0:14:36 阅读更多 →
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/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练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/15 9:49:14 阅读更多 →