NDIS小端口驱动开发实战:从Miniport注册到NBL收发路径详解
简介这是一份面向Windows内核网络驱动开发者的NDIS 6.0小端口驱动实例源码针对Realtek 8111/8168/8169/8110等PCI千兆以太网控制器编写。由于DDK中可参考的miniport示例仅有E100BEX学习NDIS 6.0网卡驱动的资料相当稀缺本资源正好填补这一空白适合具备一定驱动基础、希望深入理解NDIS小端口驱动架构的中高级开发者。压缩包共69个文件约615KB以gif、png图片和js脚本、htm页面为主另含css样式、jpg图片及zip压缩包主要构成配套说明页面与演示素材便于对照阅读驱动实现思路。资源还涉及LSO、jumbo frame、电源管理等进阶特性的支持版本可帮助读者理解千兆网卡驱动的完整功能扩展路径。目前已有1240人学习下载适合作为NDIS 6.0 miniport驱动入门与进阶的参考实例。1. 网卡驱动为什么绕不开 NDIS从一块以太网卡说起很多人第一次写网卡驱动都会有个错觉以为只要把数据从硬件 FIFO 里搬进内存再交给协议栈就完事了。真动手才发现一块以太网卡要能被系统识别、能收发帧、能被抓包工具看到、能参与绑定与卸载中间隔着一整套框架。Windows 上这套框架就是 NDIS而以太网卡最典型的落点就是 NDIS 小端口驱动miniport driver。反直觉的地方在于小端口驱动并不是「驱动一块网卡」那么简单它其实是把自己注册进 NDIS 这个中间层让 NDIS 替你管理协议绑定、电源状态、即插即用、统计计数和收发路径。你写的代码只是 NDIS 的一个「被回调方」。搞懂这层关系才能理解为什么网卡驱动里到处是 MiniportXxx 回调、为什么初始化要分好几个阶段、为什么收包路径会牵扯到 NBL 和 NBL 链。这份资源围绕的正是这条主线从驱动入口到收发数据路径把 NDIS 小端口驱动的底层机制讲透。2. 驱动对象与 Miniport 注册为什么入口不是 DriverEntry 就完事2.1 DriverEntry 里到底注册了什么小端口驱动的入口仍然是DriverEntry但它做的事情和普通 WDM 驱动不一样。它不直接创建设备对象而是填充一个NDIS_MINIPORT_DRIVER_CHARACTERISTICS结构然后调用NdisMRegisterMiniportDriver把自己交给 NDIS。这个结构里塞满了回调函数指针NDIS 后续在合适的时机回调它们。NDIS_STATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) { NDIS_MINIPORT_DRIVER_CHARACTERISTICS chars {0}; NDIS_HANDLE wrapperHandle; chars.Header.Type NDIS_OBJECT_TYPE_MINIPORT_DRIVER_CHARACTERISTICS; chars.Header.Size sizeof(NDIS_MINIPORT_DRIVER_CHARACTERISTICS); chars.Header.Revision NDIS_MINIPORT_DRIVER_CHARACTERISTICS_REVISION_2; chars.MajorNdisVersion 6; chars.MinorNdisVersion 80; // 关键回调初始化、暂停、重启、收发、OID 查询 chars.InitializeHandlerEx MiniportInitializeEx; chars.HaltHandlerEx MiniportHaltEx; chars.PauseHandler MiniportPause; chars.RestartHandler MiniportRestart; chars.SendNetBufferListsHandler MiniportSendNetBufferLists; chars.ReturnNetBufferListsHandler MiniportReturnNetBufferLists; chars.OidRequestHandler MiniportOidRequest; return NdisMRegisterMiniportDriver(DriverObject, RegistryPath, NULL, chars, wrapperHandle); }这里几个参数值得说清楚。MajorNdisVersion和MinorNdisVersion决定了 NDIS 用哪套语义回调你6.80 对应的是 NBLNET_BUFFER_LIST收发模型而不是老的 NDIS_PACKET。InitializeHandlerEx带 Ex 后缀意味着它支持扩展的即插即用语义能拿到更完整的资源信息。SendNetBufferListsHandler一次能收到一串 NBL而不是单个包这是 NDIS 6.x 之后吞吐提升的关键设计。常见的实现思路是DriverEntry只做注册不做任何硬件访问。因为此时网卡可能还没上电甚至还没被枚举出来。真正的硬件初始化要等到MiniportInitializeEx被调用。2.2 为什么初始化要分阶段MiniportInitializeEx被调用时NDIS 已经完成了设备枚举把硬件资源内存映射、中断向量通过NDIS_RESOURCE_LIST传进来。驱动在这里要做的事包括读取配置、映射寄存器、申请 DMA 资源、注册中断、创建收发队列、最后调用NdisMSetMiniportAttributes声明自己的能力和 MAC 地址。NDIS_STATUS MiniportInitializeEx(NDIS_HANDLE MiniportAdapterHandle, NDIS_HANDLE MiniportDriverContext, PNDIS_MINIPORT_INIT_PARAMETERS InitParameters) { PMP_ADAPTER adapter allocateAdapter(); adapter-MiniportHandle MiniportAdapterHandle; // 1. 映射 BAR 空间拿到寄存器基址 status NdisMGetBusData(MiniportAdapterHandle, PCI_WHICHSPACE_MEMORY, 0, barInfo, sizeof(barInfo)); adapter-RegBase MmMapIoSpace(barInfo.BaseAddress, barInfo.Length, MmNonCached); // 2. 注册中断NDIS 会帮你处理中断共享和亲和性 status NdisMRegisterInterruptEx(MiniportAdapterHandle, adapter, interruptChars, adapter-InterruptHandle); // 3. 声明 MAC 类型、媒体状态、最大帧长 NDIS_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES genAttrs {0}; genAttrs.MacType NDIS_MEDIUM_802_3; genAttrs.MaximumFrameSize 1514; genAttrs.CurrentLookaheadSize 1514; NdisMSetMiniportAttributes(MiniportAdapterHandle, genAttrs); return NDIS_STATUS_SUCCESS; }分阶段的原因在于NDIS 需要先知道你的能力才能决定怎么绑定协议、怎么分配缓冲。如果你在NdisMSetMiniportAttributes之前就试图收包NDIS 会直接拒绝。另一个细节是NdisMRegisterInterruptEx把中断处理也纳入了 NDIS 管理驱动不需要自己写 ISR 的框架代码只需要提供MiniportInterrupt和MiniportInterruptDpc两个回调。3. 收发数据路径NBL 链是怎么把帧送进协议栈的3.1 发送路径从协议栈到硬件发送路径的入口是MiniportSendNetBufferLists。协议栈把要发的数据封装成 NBL 链每个 NBL 上挂一个 NET_BUFFERNET_BUFFER 里才是真正的 MDL 数据。驱动要做的是遍历这条链把数据通过 DMA 映射到硬件可读的物理地址然后写发送描述符最后敲寄存器通知网卡。VOID MiniportSendNetBufferLists(NDIS_HANDLE MiniportAdapterHandle, PNET_BUFFER_LIST NetBufferLists, NDIS_PORT_NUMBER PortNumber, ULONG SendFlags) { PMP_ADAPTER adapter getAdapter(MiniportAdapterHandle); PNET_BUFFER_LIST nbl NetBufferLists; while (nbl ! NULL) { PNET_BUFFER nb NET_BUFFER_LIST_FIRST_NB(nbl); while (nb ! NULL) { // 把 MDL 映射成硬件可访问的地址 PHYSICAL_ADDRESS pa NdisGetPhysicalAddress(nb); writeTxDescriptor(adapter, pa, NET_BUFFER_DATA_LENGTH(nb)); nb NET_BUFFER_NEXT_NB(nb); } nbl NET_BUFFER_LIST_NEXT_NBL(nbl); } // 敲寄存器触发发送 WRITE_REGISTER_ULONG(adapter-RegBase TX_TRIGGER, 1); }这里有个容易翻车的点NBL 的所有权。驱动在发送完成后必须调用NdisMSendNetBufferListsComplete把 NBL 还给协议栈否则协议栈的发送队列会耗尽表现为网络卡死但硬件没报错。发送完成通常发生在中断 DPC 里驱动遍历已完成的描述符逐个归还对应的 NBL。3.2 接收路径中断、DPC 与 NBL 分配接收路径比发送复杂因为它涉及中断上下文和 DPC 的配合。网卡收到帧后触发中断MiniportInterrupt在中断上下文里只做最少的事屏蔽中断、记录状态然后返回 TRUE 让 NDIS 调度 DPC。真正的收包在MiniportInterruptDpc里完成。VOID MiniportInterruptDpc(PNDIS_HANDLE MiniportInterruptContext, PBOOLEAN QueueDpcWorkItem) { PMP_ADAPTER adapter (PMP_ADAPTER)MiniportInterruptContext; // 遍历接收描述符环找到已完成的帧 while (hasCompletedRxDescriptor(adapter)) { PNET_BUFFER_LIST nbl NdisAllocateNetBufferList(adapter-NblPool, 0, 0); PNET_BUFFER nb NET_BUFFER_LIST_FIRST_NB(nbl); // 把硬件缓冲里的数据拷进 NBL 的 MDL copyRxDataToNbl(adapter, nb); // 交给 NDISNDIS 再分发给绑定的协议 NdisMIndicateReceiveNetBufferLists(adapter-MiniportHandle, nbl, NDIS_DEFAULT_PORT_NUMBER, 1, NDIS_RECEIVE_FLAGS_RESOURCES); } // 重新使能中断 enableInterrupts(adapter); }NdisMIndicateReceiveNetBufferLists是收包的关键调用。它把 NBL 交给 NDISNDIS 根据绑定关系分发给上层协议。最后一个参数NDIS_RECEIVE_FLAGS_RESOURCES表示驱动在调用返回后立即回收 NBL适合驱动自己管理缓冲池的场景。如果不加这个标志NDIS 会在协议处理完后回调MiniportReturnNetBufferLists归还 NBL驱动需要在那里释放资源。3.3 OID 请求网卡配置的隐藏通道除了收发数据NDIS 还通过 OID 请求来查询和设置网卡参数。比如查询 MAC 地址、设置多播列表、调整 MTU、读取统计计数都走MiniportOidRequest。这个回调看起来不起眼但它是驱动和上层管理工具之间的唯一配置通道。NDIS_STATUS MiniportOidRequest(NDIS_HANDLE MiniportAdapterHandle, PNDIS_OID_REQUEST OidRequest) { PMP_ADAPTER adapter getAdapter(MiniportAdapterHandle); switch (OidRequest-Oid) { case OID_802_3_CURRENT_ADDRESS: // 返回当前 MAC 地址 moveMemory(OidRequest-DATA.QUERY_INFORMATION.InformationBuffer, adapter-CurrentMac, 6); OidRequest-DATA.QUERY_INFORMATION.BytesWritten 6; return NDIS_STATUS_SUCCESS; case OID_GEN_STATISTICS: // 返回收发统计 fillStatistics(adapter, OidRequest-DATA.QUERY_INFORMATION.InformationBuffer); return NDIS_STATUS_SUCCESS; default: return NDIS_STATUS_NOT_SUPPORTED; } }OID 请求可以是同步完成的也可以是异步的。如果驱动需要访问硬件才能拿到结果通常返回NDIS_STATUS_PENDING然后在 DPC 里调用NdisMOidRequestComplete完成。同步返回NDIS_STATUS_SUCCESS只适合那些不需要硬件交互的查询。4. 避坑与排查小端口驱动最容易翻车的几个点4.1 初始化失败但没有任何日志现象驱动加载后设备管理器显示黄色感叹号但系统日志里只有一句「设备启动失败」没有更具体的信息。原因MiniportInitializeEx返回了非NDIS_STATUS_SUCCESS但驱动没有在返回前记录具体是哪一步失败。NDIS 只会把最终状态码上报不会替你保留中间过程。解决在MiniportInitializeEx的每个关键步骤后加DEBUGP或DbgPrint把NdisMGetBusData、NdisMRegisterInterruptEx、NdisMSetMiniportAttributes的返回值都打出来。如果用了 WPP记得在MiniportInitializeEx入口和出口各加一个 trace 点。4.2 收包时蓝屏错误码指向 NBL 池现象驱动跑一段时间后蓝屏!analyze -v显示NdisAllocateNetBufferList返回的 NBL 被重复释放。原因在MiniportInterruptDpc里用了NDIS_RECEIVE_FLAGS_RESOURCES意味着 NBL 在NdisMIndicateReceiveNetBufferLists返回后就可以回收。但驱动可能在协议还没处理完时就释放了 NBL或者释放了两次。解决如果驱动自己管理 NBL 池确保每个 NBL 只释放一次。更稳妥的做法是不加NDIS_RECEIVE_FLAGS_RESOURCES让 NDIS 通过MiniportReturnNetBufferLists归还驱动在那里统一释放。这样虽然多一次回调但所有权清晰。4.3 发送队列卡死硬件寄存器显示空闲现象网络突然不通抓包看不到任何发出的帧但网卡寄存器显示发送队列为空。原因驱动在发送完成后没有调用NdisMSendNetBufferListsComplete协议栈的待发送 NBL 计数一直不归零最终耗尽。解决检查发送完成中断的处理逻辑。每次处理完一个发送描述符必须找到对应的 NBL 并调用NdisMSendNetBufferListsComplete。如果一次中断完成了多个描述符要把它们串成链一次性归还减少调用次数。4.4 OID 查询返回成功但数据是空的现象上层工具查询 MAC 地址返回成功但缓冲区全是 0。原因MiniportOidRequest里填了InformationBuffer但忘了设置BytesWritten或者BytesWritten设成了 0。解决每个查询类 OID 都必须设置BytesWritten为实际写入的字节数。对于OID_802_3_CURRENT_ADDRESS这个值是 6对于OID_GEN_STATISTICS是sizeof(NDIS_STATISTICS_INFO)。设置错误会导致 NDIS 认为没有数据返回。4.5 中断注册成功但收不到包现象NdisMRegisterInterruptEx返回成功但网卡收到帧后没有任何反应。原因中断注册成功只代表 NDIS 接受了你的中断对象不代表硬件的中断使能位已经打开。很多网卡需要驱动显式写寄存器打开中断掩码。解决在MiniportInitializeEx的最后写网卡的中断使能寄存器打开接收和发送中断。同时检查MiniportInterrupt的返回值如果返回 FALSENDIS 会认为这个中断不是你的不会调度 DPC。5. 从 NBL 到 DMA设计取舍背后的性能账小端口驱动的设计里最值得琢磨的是 NBL 和 DMA 的配合方式。NDIS 6.x 引入 NBL 链本质上是为了减少每次收发的函数调用次数。一个 NBL 可以挂多个 NET_BUFFER一个 NET_BUFFER 可以挂多个 MDL这样一次中断就能提交一批帧而不是一帧一次调用。这个设计在万兆网卡上尤其明显如果每帧都走一次NdisMIndicateReceiveNetBufferListsCPU 光在函数调用和锁竞争上就耗光了。但批量提交也带来了新的取舍。驱动需要自己管理 NBL 池的大小池太小会在高负载时分配失败池太大又浪费非分页内存。常见的做法是按队列深度动态调整初始分配 64 个 NBL如果连续多次分配失败就翻倍扩容但设一个上限比如 1024。这个策略在多数场景下够用但在突发流量下仍可能丢包因为扩容本身需要时间。另一个取舍是 DMA 映射的时机。发送路径上驱动可以在MiniportSendNetBufferLists里立即映射 MDL也可以延迟到写描述符时再映射。立即映射的优点是逻辑简单缺点是如果硬件队列满映射了却发不出去还得回滚。延迟映射的优点是只在真正发送时才占用 DMA 资源缺点是代码复杂度高需要处理映射失败的情况。我一般会这么理解如果网卡支持分散聚集scatter-gather延迟映射更划算如果不支持立即映射反而少一次拷贝。验证这些取舍是否合理最直接的方法是看统计计数。NDIS 提供了OID_GEN_STATISTICS驱动可以在这里暴露ifOutDiscards、ifInDiscards、ifOutErrors等计数。如果ifOutDiscards持续增长说明发送队列深度不够或者 NBL 池太小如果ifInDiscards增长说明接收路径处理不过来可能需要增大 DPC 的批量处理数量。这些计数不是摆设它们是调优的唯一依据。从那以后我每次写小端口驱动都会先把统计计数接好再跑一遍压力测试看丢包到底发生在哪一层。这个习惯帮我省掉了大量靠猜来定位问题的时间。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

档案管理信息系统核心设计:PostgreSQL+Django构建可审计闭环

档案管理信息系统核心设计:PostgreSQL+Django构建可审计闭环

简介:本资源是一套基于Delphi 6与SQL Server的档案管理信息系统(AMIS)完整开发源码包,面向高校计算机专业学生、桌面应用初学者及中小型单位信息化建设人员,解决传统档案手工管理效率低、检索难、权限模糊、版本混乱等…

2026/10/15 20:55:56 阅读更多 →
openai模型性能对比:用TaoToken统一Key跑通多模型基准测试

openai模型性能对比:用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/15 20:55:56 阅读更多 →
C++算法实现工程手册:从数据结构选型到性能验证的避坑指南

C++算法实现工程手册:从数据结构选型到性能验证的避坑指南

简介:《Implementing Useful Algorithms in C》由Dmytro Kedyk撰写,面向具备一定C基础、希望深入掌握算法设计与工程实现的开发者与计算机专业学生。全书从算法正确性证明、渐进记法、机器模型等基础出发,延伸至随机化算法、数据类型选择、内…

2026/10/15 20:55:56 阅读更多 →

最新新闻

2026前端AI编程工具实测:从Figma2Code到Agent闭环,TaoToken统一Key怎么配

2026前端AI编程工具实测:从Figma2Code到Agent闭环,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/15 21:39:55 阅读更多 →
给 Coding Agent 装上 LSP:别再让它靠 grep 猜代码,TaoToken 统一 Key 打通工具链

给 Coding Agent 装上 LSP:别再让它靠 grep 猜代码,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/15 21:39:55 阅读更多 →
蓝桥杯CT107D实战:DS18B20单总线驱动与避坑指南

蓝桥杯CT107D实战:DS18B20单总线驱动与避坑指南

1. 为什么CT107D上的DS18B20值得单独拿出来讲蓝桥杯单片机组的板子——CT107D,几乎是每个参赛选手绕不开的硬件平台。这块板子上集成了不少外设,数码管、独立按键、矩阵键盘、蜂鸣器、继电器、DS1302、AT24C02、DS18B20、超声波、红外接收等等&#xff0…

2026/10/15 21:39:55 阅读更多 →
Java爬虫实战:用Jsoup高效解析网页表格并导出CSV

Java爬虫实战:用Jsoup高效解析网页表格并导出CSV

简介:这是一个基于Java与Jsoup库抓取网页表格数据的入门实例,适用于在MyEclipse环境中学习网络爬虫原理的开发者,目标是掌握从HTML页面提取结构化表格信息的完整方法。资源包为zip格式,共24个文件,其中包含7个Java源码…

2026/10/15 21:39:55 阅读更多 →
树莓派4 UART多串口配置与通信实战指南

树莓派4 UART多串口配置与通信实战指南

1. 为什么树莓派 4 的 UART 值得单独拿出来聊 树莓派 4 这块板子,但凡你玩过一阵子,大概率都会碰到一个绕不开的需求:接一个串口设备。可能是 GPS 模块、可能是某款工业传感器、可能是和另一块单片机做点对点通信,也可能是调试一个…

2026/10/15 21:39:55 阅读更多 →
大模型指令微调实战:从LoRA选型到数据配比与避坑指南

大模型指令微调实战:从LoRA选型到数据配比与避坑指南

简介:这份PPT面向自然语言处理与大模型方向的学习者与开发者,系统梳理大模型指令微调(Instruction Tuning)的核心概念与整体脉络。内容从NLP四范式切入,依次讲解传统机器学习、深度学习、预训练加微调以及预训练加Prom…

2026/10/15 21:38:54 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战: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/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练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 阅读更多 →