从MDIO子设备到MFD多功能设备:Linux设备模型驱动设计实战
做嵌入式Linux驱动这么久和DSA交换机驱动打交道也有几年了一直觉得这套框架设计得挺精巧。但最近在适配某厂商的新款SoC平台时碰到了一个很典型的设备模型归属问题折腾了好几天最后被内核社区一位维护者一句话点醒思路直接从“怎么把MDIO子设备做成独立设备”转变成了“改成MFD多功能设备”。这个转变背后涉及的对Linux设备模型的理解我觉得很值得单独拿出来写一篇总结。文章会围绕我这次具体遇到的情况展开问题是什么、为什么原方案走不通、MFD方案到底改了什么、踩了哪些坑以及最后验证的结果。里面涉及大量设备模型、DSA框架、MDIO总线的细节我也会尽量用大白话把原理讲清楚方便做嵌入式驱动和BSP开发的朋友参考。无论是刚接触DSA不久的人还是被类似“设备注册不上”问题困扰过的老手这篇文章应该都能帮上忙。1. 问题背景与表象V3版驱动里的MDIO子设备“独立”失败1.1 我遇到的具体场景这套DSA交换方案已经迭代到V3了。前两版用的是比较传统的做法交换芯片内部集成的PHY和外部PHY都挂在MDIO总线上通过DSA框架统一管理平时跑得还算稳定。V3版本在硬件上做了一次重构芯片内部多了一个独立的功能模块这个模块虽然物理上是挂在同一个MDIO物理总线上但它不仅仅是PHY还集成了信号检测、状态上报等功能硬件设计上给它分配了独立的时钟和电源域擦写它的寄存器还需要先过一道状态机的锁。按前两版的习惯新模块无非就是MDIO总线上多一个设备驱动里多一个phy_driver注册或者直接在DSA驱动内部通过mdio_device_register把它挂上去。结果实测下来问题很明显这个设备要么探测不到要么探测到了但驱动加载的时机完全不对经常出现访问寄存器时时钟还没开、电源域还没就绪的情况导致整个交换芯片的初始化流程被它拖垮异常报错一大堆。1.2 表象背后的三个核心矛盾先说结论这个设备根本不适合作为MDIO总线上一个普通的phy_device独立存在。原因有三个第一个矛盾在设备模型的父子关系上。MDIO总线上的phy_device它的生命周期管理是以“挂在MDIO总线上”为前提的。这个新模块自带独立电源域和时钟域它本质上是一个多功能复合设备不能单纯依赖MDIO驱动框架去管它的电源和时钟。电源、时钟这类资源通常属于父设备也就是那个MFD设备由父设备统一管理子设备通过运行时PM去引用。如果强行把它注册成独立的phy_device就等于让它脱离了父设备的资源管理链。第二个矛盾是MDIO总线的扫描机制。MDIO总线驱动初始化后会主动扫描总线地址通过读取PHY ID寄存器来识别设备。问题在于这个V3版芯片的MDIO总线在系统刚上电的时候新模块的时钟还在关断状态这时候去读它的PHY ID返回的全是垃圾值或者0xffffffff总线扫描结果自然就是不识别。后面就算把时钟打开了MDIO总线也不会重新扫描一次除非手动再触发这在正常启动流程里很难插一脚。第三个矛盾是模块的使用方式。它需要和其他子系统打交道比如需要支持用户态通过某个字符设备接口控制它而不仅仅是作为一个网络PHY被内核网络栈使用。把它硬塞进phy_driver模型里等于让网络子系统去管一个根本不是网络设备的东西长远看只会越改越乱。1.3 为什么“独立注册”最初看起来可行说来也惭愧这个方案是我最开始提交的。当时觉得很简单平台手册里写得清清楚楚“该模块挂载于MDIO总线地址0x1E”那我写一个mdio_driverprobe回调里读写寄存器不就行了但实测下来我发现Linux设备模型不是“能读到寄存器”就完事它讲究的是设备、总线、驱动三者如何匹配以及资源由谁分配、何时释放。MDIO总线只是提供了一种访问物理寄存器的途径不等于适合承载所有挂在它上面的设备。我后来复盘把“尝试独立注册”的过程理了一下本质上是三个步骤全都踩了雷想让这个模块被MDIO总线扫描到失败因为扫描时时钟没开。想通过设备树添加匹配节点失败因为MDIO子节点的匹配逻辑依赖总线扫描结果。想让驱动在需要的时机再加载失败因为phy_device的注册时机由总线扫描决定不受我控制。这三点踩完基本就验证了一个道理MDIO物理总线上的设备不一定非要做成MDIO设备。这个“物理挂在谁下面”和“逻辑上归属哪个驱动框架”其实是两回事当时没想通这一点走了不少弯路。2. 方案对比MDIO直挂、平台设备、MFD到底差在哪2.1 我最初尝试的三种方案在听维护者建议之前我自己尝试过三种在MDIO总线模型下“曲线救国”的办法每个都有明显的短板。第一种是直接给MDIO总线驱动的扫描函数打补丁在扫描完0x00到0x1F地址之后自动开启目标模块的时钟再补扫一次。这个方案的问题在于完全绕过了设备模型的资源管理相当于在总线上写死了一段专用逻辑。今天为A模块打补丁明天B模块也要打后天换个平台这个补丁就完全不适用把通用代码搞得乱七八糟。很典型的“治标不治本”。第二种是在DSA驱动内部单独开一个线程每隔几百毫秒去轮询一次总线看那个地址能不能访问。这个方案更不靠谱轮询本身就是妥协什么时候能访问完全看运气而且如果模块处于不可预测状态轮询还可能把总线锁死影响交换芯片本身的功能。这种方案别说是产品上线了自己测试阶段都觉得心虚。第三种是把模块的兼容字符串做成一个platform_driver让它作为一个普通平台设备走device_probe。这个方案在思路上已经比前两个接近正解了但它有一个致命伤这个模块的寄存器访问是必须依赖MDIO总线的做成platform_driver之后它得自己去拿MDIO总线句柄自己管理总线访问的互斥和时序。这等于把总线驱动的一部分职责强行搬到了平台驱动里绕了一圈还是绕不开对MDIO的直接依赖。所以这三条路全走不通之后我才把问题抛到内核社区也才有了后面的MFD方案。2.2 内核维护者的思路转换那位维护者回复得很简短大意是说“你有没有考虑过它本身就是一个MFD设备你把它拆成两个子设备一个处理MDIO/PHY功能另一个处理控制功能让MFD去统一管理资源和生命周期。”当时我看到这条回复第一反应是“对啊”第二个反应是“怎么会这么简单”。回头再看这个模块它确实具备MFD设备的典型特征单一物理硬件上集成多个功能不同功能需要暴露给不同的子系统内部有独立的资源域各功能模块之间共享物理访问通道。这些东西不正是MFD框架设计的初衷吗Linux内核里已经有无数的类似先例。比如某款音频芯片内部集成了codec、电源管理和GPIO扩展对外通过I2C访问它就是一个MFD设备通过mf_cell把codec做成一个子设备、电源管理做成另一个子设备。我们遇到的情况和这个几乎一模一样只是物理通道从I2C换成了MDIO而已。之前一直盯着“MDIO总线上的PHY”这个角度看把问题越想越窄而MFD的视角是从设备功能出发物理通道只是一个访问手段思路一下就开阔了。2.3 MFD方案的结构差异传统方案和MFD方案之间的差异可以通过一个简单的表格来看清楚对比维度传统MDIO子设备方案MFD多功能设备方案设备注册依据MDIO总线扫描结果设备树节点match电源/时钟归属难以管理只能被动等待MFD父设备统一管理驱动匹配方式phy_driver / mdio_drivermfd_cell platform_driver子功能扩展每加一个功能总线上多一个设备MFD内部多一个cell访问通道直接绑定MDIO总线由平台驱动持有总线访问句柄系统休眠无法协调各功能块的休眠次序通过MFD统一协调这个表格基本上把两种模型的核心差异讲清楚了。MFD方案之所以能解决问题核心在于它对“设备”的定义更贴合硬件实际不是总线上的一个地址而是物理芯片上的一个复合功能体。3. 改造实操从MDIO子设备到MFD cell的完整过程3.1 设备树节点的重构改造的第一步是重新设计设备树。原本打算用传统的MDIO节点方式描述这个设备现在要把它提升为一个独立的多功能设备节点。具体做法是在根节点或者合适的总线节点下新增一个平台设备节点并给它一个独特的compatible属性然后在这个节点下挂若干子节点每个子节点对应一个函数功能。我最终采用的设备树结构大概是这样的mdio { status okay; ... }; soc { switch_combo0 { compatible vendor,switch-combo; reg 0x0; mdio-bus mdio; mdio-addr 0x1e; clocks switch_clk; power-domains switch_pd; #address-cells 1; #size-cells 0; switch_phy: switch-phy0 { reg 0; }; switch_ctrl: switch-ctrl1 { reg 1; interrupt-parent gpio; interrupts 17 IRQ_TYPE_LEVEL_LOW; }; }; };这里有几个关键点需要解释一下。mdio-bus和mdio-addr这两个属性被我放在父节点里而不是放在子设备节点里。原因是访问通道MDIO总线属于父设备共享的资源MFD父设备先拿好这个通道子设备需要访问时向父设备申请即可。如果每个子设备都直接引用MDIO总线那总线访问的互斥和并发控制就成了大问题。clocks和power-domains这两个属性也放在父节点。父设备在probe时会按顺序完成时钟准备、电源域开启、MDIO总线访问准备然后才注册子设备。这保证了子设备的probe回调执行的时候底层资源已经全部就绪不用再去猜时序。3.2 MFD驱动代码的组织方式设备树确定之后就要写MFD驱动本身了。这个驱动要做的事有五个缺一个都会出问题解析设备树中父节点的资源时钟、电源、MDIO总线句柄、MDIO地址。开启时钟和电源域。初始化MDIO总线访问通道确认目标模块可访问。定义并注册各个子设备的mf_cell结构体。调用devm_mfd_add_devices注册子设备把资源统一暴露给子设备。父设备驱动的核心部分看起来大致是这样static const struct mfd_cell switch_combo_devs[] { { .name switch-phy, .of_compatible vendor,switch-phy, .id MFD_ID_OF_PLATFORM, .platform_data phy_platform_data, .pdata_size sizeof(phy_platform_data), }, { .name switch-ctrl, .of_compatible vendor,switch-ctrl, .id MFD_ID_OF_PLATFORM, .platform_data ctrl_platform_data, .pdata_size sizeof(ctrl_platform_data), }, }; static int switch_combo_probe(struct platform_device *pdev) { struct device *dev pdev-dev; struct switch_combo_data *data; struct device_node *np dev-of_node; struct mii_bus *bus; int ret; data devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); >static int switch_phy_probe(struct platform_device *pdev) { struct switch_combo_data *parent dev_get_drvdata(pdev-dev.parent); u32 val; dev_set_drvdata(pdev-dev, parent); /* 此时直接使用父设备提供的通道 */ mutex_lock(parent-mii_mutex); val mdiobus_read(parent-mdio_bus_handle, parent-mdio_addr, 0x10); mutex_unlock(parent-mii_mutex); dev_info(pdev-dev, PHY module ready, status 0x%x\n, val); return 0; }这里有个细节子设备的平台数据通过pdev-dev.platform_data传下来但父设备的操作句柄一般是通过pdev-dev.parent去拿的。在MFD框架里父设备的私有数据就是通过这个机制传递的。这要求父设备在probe里一定要调用platform_set_drvdata把自己的数据指针保存好。3.4 注册顺序的保证MFD框架和MDIO方案最大的区别就是把“设备注册”从总线扫描这一被动行为变成了由父设备驱动的主动行为。父设备在确认时钟、电源、总线都就绪后才调用devm_mfd_add_devices此时子设备的probe才会触发。这个顺序从架构上得到了保证不再依赖时序上的碰运气。这套机制让我在测试中彻底摆脱了之前“时钟没开但设备先注册成功”的竞态问题。以前每次开机看日志都要反复确认访问寄存器时模块是不是已经苏醒现在好了父设备不准备完子设备根本不会被注册逻辑绝对可靠。4. 改造过程中踩过的坑与排查记录4.1 踩坑记录一设备树里缺少依赖关系导致EPROBE_DEFER死循环第一次把代码写完编译烧录系统启动后马上开始刷屏报错一个关键子设备的驱动反复延迟探测那叫一个执着。看日志发现of_mdio_find_bus返回空指针MDIO总线还没有被其驱动注册成功。排查思路是这样的MDIO总线本身是平台外设它有自己的probe节奏而MFD父设备在设备树里是挂在soc总线节点下的理论上MDIO驱动的probe时间比它早。但实际设备树里我的交换组合节点没有显式声明对MDIO总线节点的依赖导致内核探测顺序并不按我预期执行。解决办法是在父节点的设备树属性里补充依赖关系加了一个depends-on属性指向MDIO总线节点内核在注册平台设备时会根据这个依赖关系调整probe顺序问题解决。这个属性在旧版本内核里可能不支持新版内核对这个做了解析实际使用中要注意内核版本。4.2 踩坑记录二父设备probe被调用两次这是我遇到过最诡异的一个现象。同一个父设备节点probe函数被调用了两次第一次成功注册了子设备第二次再次注册时子设备名冲突直接报错。深入追踪之后发现问题是设备树里除了原本的节点还有一个历史遗留的节点在另一处也指向了相同的compatible字符串。两棵树合并时没有做去重处理导致两个独立的platform_device被创建出来各自都匹配上了同一个驱动。解决办法很直接清理掉冗余节点。这也提醒了一个原则设备树里的节点description必须和驱动保持一一对应不要图省事复制粘贴。4.3 踩坑记录三MDIO总线访问的并发安全改造之前MDIO总线的访问都集中在DSA驱动内部由DSA自己的锁机制保护。改成MFD之后子设备的时钟检测逻辑和DSA框架同时访问MDIO总线的可能性大大增加并发问题立刻暴露出来。主要体现在某个子设备占用总线期间DSA访问交换芯片的PHY寄存器返回了错误值间接导致链路状态误判。解决办法是在父设备初始化时为MDIO访问创建一个专用互斥锁MDIO的访问本来就是慢速操作增加一把锁的开销完全在可接受范围内。同时也重新审视了子设备里所有对MDIO总线的访问点统一收口到父设备提供的带锁访问函数里。4.4 踩坑记录四时钟和电源域在系统休眠时挂掉系统休眠唤醒之后模块出现无法访问的问题。根因是父设备驱动的suspend回调里时钟和电源管理时序处理得不对子设备还没有来得及停止访问父设备就把时钟关了导致访问还挂在半路上系统休眠流程直接卡死。解决思路是调整suspend的流程先让所有子设备各自处理好自己的状态再关父设备的资源和时钟。具体做法是利用devm_mfd_add_devices注册的device在内核系统suspend流程中子设备会先于父设备收到suspend通知天然好使。我只需要在子设备suspend回调里做必要的状态保存父设备suspend回调只做资源关闭即可。5. 最终验证和性能对比5.1 长时间稳定性验证改造完成后我在测试环境上跑了整整一周的稳定性测试。测试内容包括1000次系统重启、100次suspend/resume往返、500次热插拔模拟、以及长时间网络吞吐压测。所有测试全部通过没有出现一次模块访问超时或者总线异常。这个结果在意料之中。MFD方案从根本上解决了资源管理归属的问题所有访问通道都有明确的锁保护所有时钟电源都有父设备统一把关竞态条件从架构上被消灭了剩下的就只是常规的功能验证。5.2 与旧方案的对比结果我把V2版本老驱动和新的MFD方案做了一组对比测试主要看三个指标启动时间、代码行数、问题率。启动时间是新的方案稍微快一点主要是省去了总线轮询和多轮探测的开销代码行数新方案比旧的精简了大约三成因为子设备不需要重复实现总线访问的获取、互斥和资源管理问题率旧方案在弱电场景下偶尔有偶发访问超时新方案直接清零了。这组数据放在了内部技术评审的文档里最终顺利通过了评审。5.3 这套方案的扩展空间MFD方案解决了当前问题之外还给了后续很大的扩展空间。以后如果芯片再集成新的功能模块比如温度传感器、光模块监控、电压监测只需要在MFD的cells数组里多加一个设备条目再单独写一个子设备驱动完全不影响已有功能的稳定性。如果后续还要做用户态控制接口比如ioctl或者sysfs节点也只需要把控制功能做进switch-ctrl子设备里不需要改动父框架。这个“扩展成本”比MDIO方案低了一个数量级。写在最后的经验总结回到最开始的那个问题为什么这么简单的思路转变我却花了几天时间才找到我自己复盘得出的结论是长期在某个框架内工作很容易把物理通道和逻辑角色绑定在一起思考认定“MDIO总线上的东西就应该是MDIO设备”反而忽略了更高层的设备模型设计。Linux内核在设备模型上的设计本就层次分明物理通道只是传递数据的一种方式而设备的角色和功能则决定它应该挂在哪套框架下。在实际开发中遇到设备“注册不了”“驱动加载顺序不对”这类问题我建议先停下来想一想这个设备到底在读内核的哪个注册流程它承载的功能逻辑应该在哪一层被触发。很多时候问题并不在于代码写得不对而是从第一步的设备模型设计就已经走错了方向。这次的经验对我来说不仅是一次技术方案的改变也是一次对Linux设备模型理解上的升华。以后再遇到类似的“物理通道和逻辑角色不匹配”的尴尬局面我再也不会盯着总线那一层抠代码了先想清楚设备该以什么身份接入内核一定是最省时间的路径。

相关新闻

嵌入式必会总线:CAN协议原理与STM32调试避坑指南

嵌入式必会总线:CAN协议原理与STM32调试避坑指南

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

2026/10/11 1:10:18 阅读更多 →
AUTOSAR以太网协议栈仿真套件:在PC上跑通SOME/IP与DoIP

AUTOSAR以太网协议栈仿真套件:在PC上跑通SOME/IP与DoIP

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

2026/10/11 1:09:18 阅读更多 →
PointNet与PointNet++点云分类分割实战:PyTorch实现与避坑指南

PointNet与PointNet++点云分类分割实战:PyTorch实现与避坑指南

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

2026/10/11 1:09:18 阅读更多 →

最新新闻

WPF贝塞尔曲线绘制平滑折线图实战指南

WPF贝塞尔曲线绘制平滑折线图实战指南

简介:本资源是一个基于WPF与C#实现的贝塞尔曲线动态折线图可视化项目,面向.NET桌面开发初学者及图形学实践者,解决传统折线图缺乏平滑过渡与动态量程适配的问题。项目完整封装为RAR压缩包(65KB),共36个文件…

2026/10/11 1:51:41 阅读更多 →
视黄酸、FIV与脑膜屏障——猫原代脑膜细胞如何解码神经发育与神经免疫的交叉调控密码

视黄酸、FIV与脑膜屏障——猫原代脑膜细胞如何解码神经发育与神经免疫的交叉调控密码

在神经科学研究领域,脑膜长期以来被视为静态包裹大脑的“惰性保护层”。然而,近十年的研究正在从根本上改写这一认知——脑膜不仅是中枢神经系统的物理屏障,更是一个高度分区化、功能特化的动态微环境调控系统。脑膜成纤维细胞主动表达与血脑…

2026/10/11 1:51:41 阅读更多 →
功能红利退潮之后:C端产品设计差异化的4条走心路径

功能红利退潮之后:C端产品设计差异化的4条走心路径

【摘要】功能差异的保质期已缩短至6个月,参数升级的用户感知趋近于零,C端差异化竞争正从功能层转向情感层。文章以波特竞争战略、KANO模型、峰终定律为锚点,用走心力4因子公式拆解4个落地切口,配套6步执行流程、3类典型误区与3条量…

2026/10/11 1:51:41 阅读更多 →
免疫组库基础分析14:基于NAIR包的TCR/BCR免疫组库公共簇分析

免疫组库基础分析14:基于NAIR包的TCR/BCR免疫组库公共簇分析

摘要 适应性免疫受体库测序(AIRR-Seq)是解析免疫应答、疾病标志物筛选的核心技术,TCR/BCR簇的跨样本共享性与表型关联性是关键研究切入点。NAIR(Network Analysis of Immune Repertoire)是基于R语言的免疫组库网络分析…

2026/10/11 1:51:41 阅读更多 →
Node-Exporter 详解:服务器监控神器,从零部署实战教程

Node-Exporter 详解:服务器监控神器,从零部署实战教程

文章目录Node-Exporter 详解:服务器监控神器,从零部署实战教程一、什么是 Node-Exporter?核心监控范围二、为什么要用 Node-Exporter?三、Node-Exporter 部署实战(两种方式)方式一:二进制部署&a…

2026/10/11 1:51:41 阅读更多 →
主动悬架真正难的并不是算法

主动悬架真正难的并不是算法

前言 做主动悬架时间久了,有一个很深的感受: 主动悬架真正难的,往往不是算法。 刚开始接触这个领域时,很容易把注意力集中在控制算法上。Skyhook、LQR、H∞、MPC,甚至更复杂的预测控制和整车协同控制,看起来…

2026/10/11 1:50:41 阅读更多 →

日新闻

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

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

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

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

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

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

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

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

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

2026/10/11 0:00:27 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/11 0:00:27 阅读更多 →

月新闻

我发现了一个新思路:用 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/10 5:23:50 阅读更多 →
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/9 21:32:20 阅读更多 →
黑夜航拍船只数据集训练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/10 10:38:42 阅读更多 →