Linux 下 pcie 初始化设备枚举流程代码分析
1、简介以 rk3568 pcie 代码为例简要介绍一下 pcie 初始化设备枚举的过程。比较重要的函数就是pci_scan_child_bus_extend、pci_scan_bridge_extend这两个函数是递归的核心。简要函数调用流程如下2、pci_scan_child_bus_extend该函数入参 bus 为 pci_bus 结构表示一个总线资源。该函数首次被调用时传递下来的是 root bus 结构该函数作用为扫描并创建当前 bus 入参上的所有 pci 设备可能是 endpoint 设备也可能是桥设备当前 bus 扫描结束调用pci_scan_bridge_extend尝试遍历下一级 bus返回值为新的 subordinate number根据不断递归获取到当前 bus 下的最最深一级的 bus 号/** * pci_scan_child_bus_extend() - Scan devices below a bus * bus: Bus to scan for devices * available_buses: Total number of buses available (%0 does not try to * extend beyond the minimal) * * Scans devices below bus including subordinate buses. Returns new * subordinate number including all the found devices. Passing * available_buses causes the remaining bus space to be distributed * equally between hotplug-capable bridges to allow future extension of the * hierarchy. */staticunsignedintpci_scan_child_bus_extend(structpci_bus*bus,unsignedintavailable_buses){....../* Go find them, Rover! *//* 这里的含义是遍历当前 bus 上的所有设备。针对遍历到的设备创建 pci_dev 结构挂载 pci_bus 结构的链表上 */for(devfn0;devfn256;devfn8){/* * 该函数会去调用 pci_scan_single_device创建、初始化 pci 设备包括 endpoint 设备与桥设备 * 包括但不限于 BAR 空间的初始化、中断资源的初始化、设备 capability 使能等 */nr_devspci_scan_slot(bus,devfn);/* * The Jailhouse hypervisor may pass individual functions of a * multi-function device to a guest without passing function 0. * Look for them as well. */if(jailhouse_paravirt()nr_devs0){for(fn1;fn8;fn){devpci_scan_single_device(bus,devfnfn);if(dev)dev-multifunction1;}}}/* Reserve buses for SR-IOV capability *//* 还记得前面 SR-IOV 章节的保留 bus 号么没有阅读的可以先去阅读下 */used_busespci_iov_bus_range(bus);maxused_buses;....../* * Scan bridges that are already configured. We dont touch them * unless they are misconfigured (which will be done in the second * scan below). *//* for 循环这里是遍历当前 bus 上的所有桥设备不包括 endpoint 设备*/for_each_pci_bridge(dev,bus){cmaxmax;/* 这里是为了处理 BIOS/Boot 中已经被配置好的 pci 桥 这个是为了兼容各个架构所做的妥协 */maxpci_scan_bridge_extend(bus,dev,max,0,0);/* * Reserve one bus for each bridge now to avoid extending * hotplug bridges too much during the second scan below. */used_buses;if(cmax-max1)used_busescmax-max-1;}/* Scan bridges that need to be reconfigured *//* for 循环这里是遍历当前 bus 上的所有桥设备不包括 endpoint 设备 */for_each_pci_bridge(dev,bus){unsignedintbuses0;if(!hotplug_bridgesnormal_bridges1){/* * There is only one bridge on the bus (upstream * port) so it gets all available buses which it * can then distribute to the possible hotplug * bridges below. */busesavailable_buses;}elseif(dev-is_hotplug_bridge){/* * Distribute the extra buses between hotplug * bridges if any. */busesavailable_buses/hotplug_bridges;busesmin(buses,available_buses-used_buses1);}cmaxmax;/* 这里才是真的是递归遍历下一级 bus通过 buses 参数传递 bus 号 */maxpci_scan_bridge_extend(bus,dev,cmax,buses,1);/* One bus is already accounted so dont add it again */if(max-cmax1)used_busesmax-cmax-1;}......}3、pci_scan_slotpci_scan_child_bus_extend()以 8 为步长遍历devfn每次调用pci_scan_slot()扫描一个 Device Number。传入的devfn低 3 位必须为 0即扫描总是从 Function 0 开始如果 Function 0 表明该设备支持多个 Function函数还会继续枚举同一 Device 下的其他 Function。扫描到的每个 Function 都由pci_scan_single_device()创建或获取对应的pci_dev并挂入bus-devices链表。因此pci_scan_slot()的职责可以概括为以 Device 为单位确定扫描范围以 Function 为粒度发现并登记设备。其返回值nr表示本次扫描中新发现的 Function 数量而不是该总线上已有设备的总数。/** * pci_scan_slot - Scan a PCI slot on a bus for devices * bus: PCI bus to scan * devfn: slot number to scan (must have zero function) * * Scan a PCI slot on the specified PCI bus for devices, adding * discovered devices to the bus-devices list. New devices * will not have is_added set. * * Returns the number of new devices found. */intpci_scan_slot(structpci_bus*bus,intdevfn){unsignedfn,nr0;structpci_dev*dev;/* PCIe 下行链路通常只允许出现 Device 0无需继续扫描其他 Device */if(only_one_child(bus)(devfn0))return0;/* Already scanned the entire slot *//* 首先扫描 Function 0它决定后续是否需要扫描其他 Function */devpci_scan_single_device(bus,devfn);if(!dev)return0;if(!pci_dev_is_added(dev))nr;/* 枚举传统多功能设备或 ARI 设备中的后续 Function */for(fnnext_fn(bus,dev,0);fn0;fnnext_fn(bus,dev,fn)){devpci_scan_single_device(bus,devfnfn);if(dev){if(!pci_dev_is_added(dev))nr;dev-multifunction1;}}/* Only one slot has PCIe device */if(bus-selfnr)pcie_aspm_init_link_state(bus-self);returnnr;}函数的执行过程可分为四个阶段裁剪 Device 扫描范围。only_one_child()判断当前总线是否位于 PCIe 下行端口之后。若该链路按照 PCIe 拓扑只可能出现 Device 0则后续针对 Device 131 的扫描直接返回。探测 Function 0。pci_scan_single_device()首先访问当前 Device 的 Function 0。若 Function 0 不存在说明该 Device 不存在当前槽位的扫描随即结束。枚举后续 Function。next_fn()决定下一个 Function Number。对于传统多功能设备它依据 Header Type 中的 Multifunction 位扫描 Function 17并允许 Function Number 不连续启用 ARIAlternative Routing-ID Interpretation后则根据 ARI Capability 中的 Next Function Number 继续枚举。完成链路初始化。如果本次发现了新设备且当前总线存在上游桥bus-self则调用pcie_aspm_init_link_state()初始化这条 PCIe 链路的 ASPM 状态。3.1 PCI 核心层的扫描范围裁剪传统 PCI 总线采用共享总线模型同一条总线上可以合法挂接多个 Device因此必须扫描 Device 031。PCIe Root Port 或 Downstream Port 的下游侧则是一条点对点链路链路对端通常只能以 Device 0 出现这个 Device 既可能是 Endpoint也可能是 PCIe Switch 的 Upstream Port。Linux PCI 子系统通过only_one_child()判断当前总线的上游桥是否为面向下游的 PCIe 端口并据此将该总线的扫描范围裁剪到 Device 0。only_one_child()既减少了对不存在 BDF 的无效探测也避免部分控制器因与实际拓扑不符的配置事务进入异常上报或超时处理路径。如果没有该步骤pci_scan_child_bus_extend()仍会依次调用pci_scan_slot()探测 Device 131并最终通过 Host 控制器发起真实的配置空间访问这些 Device 在当前链路上虽然必然不存在但访问产生的结果取决于控制器的具体实现。对于实现完备的控制器不存在的 BDF 应当安全返回全 1 或PCIBIOS_DEVICE_NOT_FOUND此时only_one_child()主要体现为枚举效率的提升。但在部分硬件上无效访问还可能引发 Completion Timeout、系统总线异常甚至因地址译码问题映射到其他设备的配置空间。因此这项裁剪同时具有降低风险的保护作用。这里限制的是Device Number并不意味着链路对端只能包含一个 Function。Device 0 仍然可以是传统多功能设备也可以在启用 ARI 后提供更多 Function这些 Function 由后续的next_fn()继续枚举。对于确实存在特殊拓扑的平台可以设置PCI_SCAN_ALL_PCIE_DEVS要求 PCI 核心扫描全部 Device Number。staticinlineboolpcie_downstream_port(conststructpci_dev*dev){inttypepci_pcie_type(dev);returntypePCI_EXP_TYPE_ROOT_PORT||typePCI_EXP_TYPE_DOWNSTREAM||typePCI_EXP_TYPE_PCIE_BRIDGE;}staticintonly_one_child(structpci_bus*bus){structpci_dev*bridgebus-self;/* 特殊拓扑要求扫描全部 Device Number */if(pci_has_flag(PCI_SCAN_ALL_PCIE_DEVS))return0;/* PCIe 下行端口之后通常只有 Device 0 */if(bridgepci_is_pcie(bridge)pcie_downstream_port(bridge))return1;return0;}3.2 Host 控制器驱动的配置空间访问过滤only_one_child()依赖bus-self判断当前总线的上游桥而 Root Bus 没有上游桥因此其bus-self为NULL。同时PCI_SCAN_ALL_PCIE_DEVS可以要求跳过这项裁剪配置空间访问也可能来自重新扫描、热插拔或其他内核路径。因此Host 控制器驱动必须结合自身的 Root Bus 拓扑和地址译码方式在pci_ops入口独立检查目标 BDF 是否可以安全访问。staticintrockchip_pcie_valid_device(structrockchip_pcie*rockchip,structpci_bus*bus,intdev){/* * Access only one slot on each root port. * Do not read more than one device on the bus directly attached * to RCs downstream side. */if(pci_is_root_bus(bus)||pci_is_root_bus(bus-parent))returndev0;return1;}这项检查不仅用于减少无效访问还直接关系到枚举结果的正确性。Root Bus 上的配置空间访问最终进入rockchip_pcie_rd_own_conf()而该函数只根据寄存器偏移where访问 RC 内 Root Port 的 own configuration space调用链中并没有把devfn传递给它staticintrockchip_pcie_rd_conf(structpci_bus*bus,u32 devfn,intwhere,intsize,u32*val){structrockchip_pcie*rockchipbus-sysdata;if(!rockchip_pcie_valid_device(rockchip,bus,PCI_SLOT(devfn))){*val0xffffffff;returnPCIBIOS_DEVICE_NOT_FOUND;}if(pci_is_root_bus(bus))returnrockchip_pcie_rd_own_conf(rockchip,where,size,val);returnrockchip_pcie_rd_other_conf(rockchip,bus,devfn,where,size,val);}staticintrockchip_pcie_rd_own_conf(structrockchip_pcie*rockchip,intwhere,intsize,u32*val){void__iomem*addr;addrrockchip-apb_basePCIE_RC_CONFIG_NORMAL_BASEwhere;......}这意味着如果没有rockchip_pcie_valid_device()Root Bus 上针对 Device 131 的读操作也会落到同一份 Root Port 配置空间并读出与 Device 0 相同的 Vendor ID、Device ID 等信息。Linux PCI 子系统可能由此把同一个 Root Port 重复识别为多个桥设备进一步引发总线号和资源窗口冲突、重复初始化等问题对非法 Device Number 的写操作则可能直接改写 Root Port 自身的配置寄存器。因此Root Bus 上的过滤属于保证正确性的必要措施而不仅仅是性能优化。该函数在rockchip_pcie_rd_conf()和rockchip_pcie_wr_conf()的入口处被调用访问 Root Bus 时仅允许访问 Device 0即 RC 内 Root Port 的配置空间访问 Root Bus 的直接子总线时同样只允许访问 Device 0即 RC 下行链路的唯一对端从配置空间访问入口阻止与实际拓扑不符的事务对更深层级的总线不作此限制因为 PCIe Switch 下游可能形成包含多个 Device Number 的合法拓扑。如果目标 Device Number 不合法读操作返回0xffffffff同时以PCIBIOS_DEVICE_NOT_FOUND结束不再向硬件发起配置事务。由此可见only_one_child()是 Linux PCI 子系统依据 PCIe 拓扑实施的第一层扫描裁剪rockchip_pcie_valid_device()则是 Host 驱动在硬件访问入口设置的第二道防线。前者避免探测拓扑上确定不存在的 Device后者负责阻止地址混叠及控制器不支持的配置事务两者不能相互替代。4、pci_scan_single_devicepci_scan_single_device函数是初始化 pcie 设备的重中之重。本篇其余部分都是 pcie 设备枚举的过程只有这个函数是配置函数。structpci_dev*pci_scan_single_device(structpci_bus*bus,intdevfn){structpci_dev*dev;devpci_get_slot(bus,devfn);if(dev){pci_dev_put(dev);returndev;}/* * 为当前设备创建 pci_dev 结构 * 同时初始化当前设备的 BAR 资源、中断资源 */devpci_scan_device(bus,devfn);if(!dev)returnNULL;/* 初始化当前设备的 capabilities 功能 */pci_device_add(dev,bus);returndev;}pci_device_add()-pci_init_capabilities()-pci_ea_init -pci_configure_ari -pci_iov_init -......5、pci_scan_bridge_extend该函数入参 bus 为 pci_bus 结构表示一个总线资源。该函数首次被调用时传递下来的是 root bus 结构该函数作用为扫描并创建当前 bus 入参的下级 bus在pci_add_new_bus函数中会去创建新的 bus 结构dev 结构是 pci 设备的 pci_dev 结构。注意这里的 pci 设备只会是 pci 桥设备不会是 endpoint 设备返回值为新的 subordinate number根据不断递归获取到当前 bus 下的最最深一级的 bus 号/* * pci_scan_bridge_extend() - Scan buses behind a bridge * bus: Parent bus the bridge is on * dev: Bridge itself * max: Starting subordinate number of buses behind this bridge * available_buses: Total number of buses available for this bridge and * the devices below. After the minimal bus space has * been allocated the remaining buses will be * distributed equally between hotplug-capable bridges. * pass: Either %0 (scan already configured bridges) or %1 (scan bridges * that need to be reconfigured. * * If its a bridge, configure it and scan the bus behind it. * For CardBus bridges, we dont scan behind as the devices will * be handled by the bridge driver itself. * * We need to process bridges in two passes -- first we scan those * already configured by the BIOS and after we are done with all of * them, we proceed to assigning numbers to the remaining buses in * order to avoid overlaps between old and new bus numbers. * * Return: New subordinate number covering all buses behind this bridge. */staticintpci_scan_bridge_extend(structpci_bus*bus,structpci_dev*dev,intmax,unsignedintavailable_buses,intpass){......pci_read_config_dword(dev,PCI_PRIMARY_BUS,buses);primarybuses0xFF;secondary(buses8)0xFF;subordinate(buses16)0xFF;....../* 这里的 if 分支含义是在 BIOS/Boot 没有配置的情况下当前桥设备 pci_dev 的配置空间读出非 0 */if((secondary||subordinate)!pcibios_assign_all_busses()!is_cardbus!broken){unsignedintcmax;/* * Bus already configured by firmware, process it in the * first pass and just note the configuration. */if(pass)gotoout;/* * The bus might already exist for two reasons: Either we * are rescanning the bus or the bus is reachable through * more than one bridge. The second case can happen with * the i450NX chipset. *//* 这里会去为下一级 bus 创建 pci_bus 结构下一级 bus 的 bus 号为 secondary */childpci_find_bus(pci_domain_nr(bus),secondary);if(!child){childpci_add_new_bus(bus,dev,secondary);if(!child)gotoout;child-primaryprimary;pci_bus_insert_busn_res(child,secondary,subordinate);child-bridge_ctlbctl;}/* 递归入口这里的 child 已经下一级的 bus 了 */cmaxpci_scan_child_bus(child);if(cmaxsubordinate)pci_warn(dev,bridge has subordinate %02x but max busn %02x\n,subordinate,cmax);/* Subordinate should equal child-busn_res.end */if(subordinatemax)maxsubordinate;}else{/* 这里的 else 分支含义是在 BIOS/Boot 已经配置的情况下或者当前桥设备 pci_dev 的配置空间读出为 0 *//* * We need to assign a number to this bus which we always * do in the second pass. */if(!pass){if(pcibios_assign_all_busses()||broken||is_cardbus)/* * Temporarily disable forwarding of the * configuration cycles on all bridges in * this bus segment to avoid possible * conflicts in the second pass between two * bridges programmed with overlapping bus * ranges. *//* * 这里是为了解决 bus 号冲突问题。因为 BIOS 已经配置好桥设备的 bus 资源 * 但因为现在操作系统又在重新配置可能会和 BIOS 原先的配置有冲突 * 所以这里对 bus 资源先进行了一个复位操作全写 0 */pci_write_config_dword(dev,PCI_PRIMARY_BUS,buses~0xffffff);gotoout;}/* Clear errors */pci_write_config_word(dev,PCI_STATUS,0xffff);/* Read bus numbers from EA Capability (if present) */fixed_busespci_ea_fixed_busnrs(dev,fixed_sec,fixed_sub);if(fixed_buses)next_busnrfixed_sec;elsenext_busnrmax1;/* 更新下一级 bus 的 bus 号 *//* * Prevent assigning a bus number that already exists. * This can happen when a bridge is hot-plugged, so in this * case we only re-scan this bus. *//* 这里会去为下一级 bus 创建 pci_bus 结构下一级 bus 的 bus 号为 next_busnr*/childpci_find_bus(pci_domain_nr(bus),next_busnr);if(!child){childpci_add_new_bus(bus,dev,next_busnr);if(!child)gotoout;pci_bus_insert_busn_res(child,next_busnr,bus-busn_res.end);}max;if(available_buses)available_buses--;/* 这里会去更新当前桥设备的 pri、sec、sub 寄存器这里的 sub 还默认是 0xff */buses(buses0xff000000)|((unsignedint)(child-primary)0)|((unsignedint)(child-busn_res.start)8)|((unsignedint)(child-busn_res.end)16);....../* We need to blast all three values with a single write */pci_write_config_dword(dev,PCI_PRIMARY_BUS,buses);if(!is_cardbus){child-bridge_ctlbctl;/* 递归入口这里的 child 已经下一级的 bus 了 */maxpci_scan_child_bus_extend(child,available_buses);}else{......}/* * Set subordinate bus number to its real value. * If fixed subordinate bus number exists from EA * capability then use it. */if(fixed_buses)maxfixed_sub;pci_bus_update_busn_res_end(child,max);/* 递归结束会根据递归得到的 max 值去修改 sub 寄存器的值 */pci_write_config_byte(dev,PCI_SUBORDINATE_BUS,max);}......}5、举例以上面这张图举例Linux 下的 PCIe 设备枚举顺序为调用pci_scan_child_bus_extend创建 bus 2 上的设备创建出 root bus 设备200调用pci_scan_bridge_extend创建下一级 bus 3再次调用pci_scan_child_bus_extend创建 bus 3 上的设备创建出 upstream port300[这里开始第一次递归]再调用pci_scan_bridge_extend创建下一级 bus 4再次调用pci_scan_child_bus_extend创建 bus 4 上的设备创建出 downstream port410、420…4180 [这里开始第二次递归]要注意这里会一次性创建出当前 bus 上所有扫描到的设备。这里已经涉及到了广度优先遍历再调用pci_scan_bridge_extend创建下一级 bus 5再次调用pci_scan_child_bus_extend创建 bus 5 上的设备创建出 endpoint 设备500[这里开始第一次递归返回返回的位置就是 for_each_pci_bridge 这个循环]如果 500是一个 switch 的话当尝试创建 bus 6 时实际上就等同于创建 upstream port 300的过程这时会接着往下深度遍历不会往右遍历。遍历结束根据遍历的返回值也就是递归的返回值原图中的 bus 6这时就有可能变成 bus 8、bus 9…再次调用pci_scan_child_bus_extend创建 bus 6 上的设备创建出 endpoint 设备600…从上面的分析我们不难看出Linux 内核代码中PCIe 设备枚举的过程不仅仅是深度优先遍历中间还夹杂了广度优先遍历这一点不看代码是很难知道的网上能搜到的结论都是清一色的深度优先遍历。纸上得来终觉浅绝知此事要躬行鸭

相关新闻

期权入门介绍

期权入门介绍

文章目录1.基本概念1.1 两大类型1.2 基本要素1.3 分类1.3.1 按权利类型区分1.3.2 按标的资产区分1.3.3 按行权方式区分1.3.4 按行权价与资产价格关系区分1.4 期权的成本:权利金1.5 期权价值1.5.1 内在价值(Intrinsic Value)1.5.2 时间价值&am…

2026/8/17 16:53:34 阅读更多 →
告别逐个另存为:一条命令完成全站批量下载,省下大半天

告别逐个另存为:一条命令完成全站批量下载,省下大半天

告别逐个另存为:一条命令完成全站批量下载,省下大半天 【免费下载链接】CyberdropBunkrDownloader Simple downloader for Cyberdrop and Bunkrr 项目地址: https://gitcode.com/gh_mirrors/cy/CyberdropBunkrDownloader 凌晨一点十七分&#xff…

2026/8/17 16:52:32 阅读更多 →
AI编程助手如何实现近期工作上下文理解:从概念到工程实践

AI编程助手如何实现近期工作上下文理解:从概念到工程实践

你是否遇到过这样的场景:在IDE里写代码时,需要一个助手帮你补全函数,但每次都要重新解释一遍项目背景和当前文件结构?或者,在使用ChatGPT讨论一个复杂的技术方案时,聊到一半它突然“失忆”,忘记…

2026/8/17 16:52:32 阅读更多 →

最新新闻

外贸GEO11|GEO优化第一步:怎么让AI收录你的内容?

外贸GEO11|GEO优化第一步:怎么让AI收录你的内容?

引言:当AI成为你的“第一道门卫” 在传统外贸时代,客户找到你,可能通过展会、黄页,或者一封开发信。但在AI主导的GEO(谷歌引擎优化)时代,情况发生了根本性转变:你的第一个“客户”&a…

2026/8/17 17:34:59 阅读更多 →
从悬浮大屏看皮卡内饰智能化:设计逻辑、技术集成与场景应用

从悬浮大屏看皮卡内饰智能化:设计逻辑、技术集成与场景应用

1. 从一张内饰图,看皮卡市场的“内卷”新战场 最近,一张凯程F70的内饰图在网络上曝光,瞬间在车友圈和行业观察者中激起了不小的水花。原因无他,图中那块醒目的悬浮式中控大屏,成为了绝对的视觉焦点。对于一款皮卡车型来…

2026/8/17 17:34:59 阅读更多 →
XSS攻击原理与CTF实战技巧详解

XSS攻击原理与CTF实战技巧详解

1. XSS攻击的本质与危害解析跨站脚本攻击(Cross-Site Scripting)本质上是一种将恶意脚本注入到可信网站中的攻击手法。与很多人第一印象不同,XSS攻击的核心不在于"跨站",而在于"脚本执行"——攻击者利用网站对…

2026/8/17 17:34:59 阅读更多 →
外贸GEO10|E-E-A-T原则:专业、经验、权威、可信——GEO的核心

外贸GEO10|E-E-A-T原则:专业、经验、权威、可信——GEO的核心

为什么你的外贸官网流量不错,询盘却寥寥无几? 很多外贸老板都有这样的困惑:独立站流量上来了,谷歌排名也不错,但询盘质量就是不高,转化率始终上不去。你投入了大量精力做SEO、投广告,客户点进来…

2026/8/17 17:34:59 阅读更多 →
豪华车市场销量分析:从奥迪2018年成绩单看品牌体系力与增长策略

豪华车市场销量分析:从奥迪2018年成绩单看品牌体系力与增长策略

1. 从一份年度成绩单说起:豪华车市场的“基本盘”之战 又到了各家车企晒成绩单的时候。每年年初,当各大品牌陆续公布上一年度的销量数据时,对于行业内的从业者、投资者乃至普通消费者而言,这都是一次绝佳的“读财报”机会。数字背…

2026/8/17 17:34:59 阅读更多 →
Substance.Data测试指南:用Mocha为图数据应用构建可靠测试套件

Substance.Data测试指南:用Mocha为图数据应用构建可靠测试套件

Substance.Data测试指南:用Mocha为图数据应用构建可靠测试套件 【免费下载链接】data A uniform interface for domain data (deprecated) 项目地址: https://gitcode.com/gh_mirrors/data26/data Substance.Data 是一个基于 JavaScript 的图数据表示库&…

2026/8/17 17:33:58 阅读更多 →

日新闻

LabVIEW异步调用实战:从原理到生产者消费者模式,解决界面卡顿与并行处理难题

LabVIEW异步调用实战:从原理到生产者消费者模式,解决界面卡顿与并行处理难题

1. 项目概述:为什么异步调用是LabVIEW进阶的必修课? 如果你用LabVIEW做过稍微复杂点的项目,尤其是涉及界面响应、多任务并行或者硬件IO等待的场景,大概率遇到过这样的窘境:前面板点个按钮,整个程序就“卡死…

2026/8/17 0:00:08 阅读更多 →
LabVIEW异步调用实战:解决界面卡顿与并行处理难题

LabVIEW异步调用实战:解决界面卡顿与并行处理难题

1. 项目概述:为什么异步调用是LabVIEW进阶的必经之路如果你在LabVIEW里写过稍微复杂点的程序,尤其是涉及到界面响应、多任务并行或者硬件IO等待,大概率会遇到一个头疼的问题:程序“卡”住了。前面板点不动,进度条不更新…

2026/8/17 0:00:08 阅读更多 →
飞书局域网文件传输实战:3种方案实现高速点对点传输

飞书局域网文件传输实战:3种方案实现高速点对点传输

1. 项目概述:为什么要在局域网内用飞书传文件? 飞书作为一款主流的协同办公套件,其核心功能是围绕云端协作设计的。无论是文档、表格还是文件,通常的分享逻辑都是“上传到云端 -> 生成链接 -> 分享给同事”。这个流程在互联…

2026/8/17 0:00:08 阅读更多 →

周新闻

基于阿里云与通义千问(Qwen)构建AI应用:从模型调用到生产部署的完整实践指南

基于阿里云与通义千问(Qwen)构建AI应用:从模型调用到生产部署的完整实践指南

如果你是一名开发者,最近可能已经感受到了AI大模型正在从“玩具”变成“生产力工具”的强烈信号。从代码补全到智能Agent,从本地部署到云端API,我们正处在一个技术栈快速重构的节点。然而,面对层出不穷的模型、框架和工具&#xf…

2026/8/17 2:58:27 阅读更多 →
工业通信系统底层逻辑:04 反射——高频能量撞墙之后会发生什么?

工业通信系统底层逻辑:04 反射——高频能量撞墙之后会发生什么?

第四篇:反射——高频能量撞墙之后会发生什么? —— 你以为信号已经过去了,其实它正在回来打你 老Q的现场笔记 第五季,我们正式进入工业神经系统层。这里不再是单个设备的战斗,而是整个工厂“经脉”层面的秩序之战。从这一篇开始,你将第一次看清:看似简单的信号传播,背…

2026/8/17 2:58:30 阅读更多 →
【文章复现】非线性值迭代自适应动态规划(ADP):离散时间非线性系统的策略迭代自适应动态规划算法研究附Matlab代码

【文章复现】非线性值迭代自适应动态规划(ADP):离散时间非线性系统的策略迭代自适应动态规划算法研究附Matlab代码

✅作者简介:热爱科研的Matlab仿真开发者,擅长毕业设计辅导、数学建模、数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。🍎 往期回顾关注个人主页:Matlab科研工作室👇 关注我领取海量matlab电子书和…

2026/8/17 2:58:32 阅读更多 →

月新闻

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

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

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

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

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

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

2026/8/16 6:00:24 阅读更多 →
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/16 6:00:27 阅读更多 →