新闻详情

新闻详情

首页 / 资讯中心 / 详情

RK3588硬解实战:Python调用MPP实现8路1080p并发

发布时间:2026/9/29 2:03:25来源:尧图网络
RK3588硬解实战:Python调用MPP实现8路1080p并发
前阵子接了个活儿客户要在 RK3588 的板子上同时跑 8 路网络摄像头的画面还要留出算力给后面的分析模型。刚开始我图省事直接上了 OpenCV 的软解结果 4 路还没跑满8 核 CPU 就有 6 个核被打到 90% 以上帧率从 25 掉到 12风扇叫得像要起飞。后来我把解码这条链路整体换成了 RK3588 自带的 VPU 硬件解码用 Python 通过 MPP 封装去调CPU 占用直接掉到个位数8 路 1080p 稳稳跑满 30 帧。这篇就把我这一路踩下来的坑、Python 调用 MPP 的完整思路、8 路并发的架构设计以及实测数据和排查经验一次性讲清楚。内容偏实战默认你手里已经有一块 RK3588 的板子、能跑 Ubuntu 或者 Debian 系统、会基本的 Linux 命令Python 基础语法不熟也没关系我会把关键地方都拆开讲。1. RK3588 做 8 路硬解的可行性拆解在做任何方案之前先把“这块板子到底能不能扛住 8 路”这件事算清楚否则后面全是白忙。很多人上来就写代码跑不动了再回头怀疑芯片其实 RK3588 的规格表里已经把答案写得很明白了。1.1 芯片里的 VPU 到底能扛多少路RK3588 是 4 个 Cortex-A76 大核加 4 个 Cortex-A55 小核的八核架构另外还挂着独立的 NPU、GPU 和 VPU。我们这次真正要用的是 VPU也就是视频处理单元它是一块独立于 CPU 的硬件编解码器。官方给出的解码能力大致是 8K60fps 的 H.265、8K30fps 的 H.264编码侧也能做到 8K30fps。这个数字看着夸张但它是硬件流水线的理论峰值实际用起来会打折扣原因后面会讲。先做个简单的换算8 路 1080p30fps 是什么量级1080p 一帧像素是 1920×1080约 207 万像素。8 路、每路 30 帧一秒就是 240 帧总共约 5 亿像素每秒。而 8K 一帧是 7680×4320约 3318 万像素60 帧就是约 20 亿像素每秒。也就是说8 路 1080p 的总像素吞吐大概只有 VPU 理论峰值的四分之一。从这个角度看硬件层面完全撑得住瓶颈根本不在这。真正要小心的是“路数”和“码率分布”。8 路如果都是低码率、低复杂度的监控流VPU 几乎是散步状态但如果其中混了几路 4K 高分码流或者码流里有大量 B 帧、参考帧跨度很大VPU 的负载会明显上升。所以评估时不能只看分辨率还要看编码档次和 GOP 结构。1.2 软解为什么必然会崩我一开始用 OpenCV 的cv2.VideoCapture去解 RTSP走的其实是 FFmpeg 软解解码全靠 CPU。H.264 软解在 A76 这种核心上单路 1080p30 差不多要吃掉 0.6 到 1 个核8 路理论上就得 5 到 8 个核。注意这只是解码本身还没算上 RTSP 收流、内存拷贝、图像后面送去做推理或者编码的额外开销。一旦 CPU 被解码占满问题就不只是帧率下降这么简单整个系统的时间片都被吃掉网络收包的线程被挤到队列末尾丢包、花屏、卡顿全都来了。更麻烦的是软解的输出是 CPU 内存里的图像你如果还要把它送去做缩放、转格式、送 NPU那又是一轮 CPU 拷贝。所以软解这条路在 8 路这个量级上从设计上就是不成立的。硬解的核心价值在于把“解码计算”从 CPU 手里拿走交给专用电路。VPU 解完的帧通常直接落在 DMA 缓冲区里不需要 CPU 参与搬运后续如果要做缩放或者格式转换也能走 RGA 这类硬件加速单元。CPU 这时只需要负责调度和收发包占用自然就下来了。1.3 为什么把 Python 放到硬解这条链路上这里肯定有人要问MPP 明明是 C 库为什么不用 C 写我自己的取舍是这样的。这个项目的重点不在解码本身而在解码之后要接的算法和业务逻辑而那部分我用 Python 写得又快又顺手。如果为了调 MPP 专门写一套 C 服务再和 Python 业务做进程间通信光是数据序列化和调试成本就上去了。Python 调 MPP 的关键在于ctypes它是标准库里就带的可以直接加载.so动态库、声明函数原型、操作 C 结构体。而且ctypes.CDLL在发起 C 调用的时候会释放 GIL这一点非常关键——意味着多个 Python 线程同时调 MPP 的 C 函数时是可以真正并行的不会被 Python 的全局锁串成一条线。这个特性决定了“每路一个线程”的架构在 Python 下是可行的后面第 5 章会展开。一句话总结这一章RK3588 的 VPU 硬件余量足够跑 8 路软解在架构上走不通MPP Python 的组合是兼顾性能和开发效率的一条务实路线。2. MPP 框架与 Python 绑定方案选型搞清楚“能跑”之后接下来是“怎么调”。MPP 全称是 Rockchip Media Process Platform是芯片厂商给 VPU、RGA 这些多媒体硬件统一封的一套中间层。它不是一个简单的函数库而是分了好几层理解这个分层对后面排查问题特别有帮助。2.1 MPP 的分层结构与解码主链路从下往上看最底层是 OSAL负责把 Linux 的线程、锁、内存映射这些系统调用包起来让上层代码不直接依赖具体操作系统。再往上是 HAL也就是硬件抽象层负责和 VPU 驱动对话、管理寄存器、处理硬件队列。最上面是 MPI也就是我们调用的那层 APImpp_create、mpp_init、mpp_decode这些都是 MPI 提供的。解码的主链路其实不长核心就是三个对象上下文MppCtx、API 句柄MppApi、数据包MppPacket。流程是先用mpp_create建上下文再用mpp_init把它初始化成解码模式然后在一个循环里反复做两件事——把码流数据封装成 packet 送进去解码再把解好的 frame 取出来。真正复杂的地方不在调用顺序而在内存管理packet 的数据你得自己准备缓冲区frame 取出来之后你还得知道它在哪块内存、什么格式、怎么读。MPP 输出帧默认是 NV12 格式也就是 YUV420SPY 分量一整块UV 交错一整块。如果你后面要送 NPU 做推理NPU 一般也是吃 NV12 或者 RGBNV12 反而是最省事的。如果只是要显示那基本不用转。只有当你确实需要 RGB 的时候才考虑用 RGA 做一次硬件转换千万别在 CPU 上用 cv2.cvtColor 一帧帧转8 路转起来 CPU 又要爆。2.2 Python 侧三条可行路线对比在 Python 里碰 MPP我实际调研过三条路各自的适用场景差别挺大。第一条是用官方仓库里自带的 Python 示例。rockchip 的 mpp 仓库里有一个test/mpp_py_demo目录里面就是一个用 ctypes 写的封装和 demo结构体定义、函数原型都给你写好了。这是最省事的起点强烈建议先把它跑通理解整个调用序列再基于它改。第二条是自己用 ctypes 从头封装。好处是完全可控你能按自己的需求裁剪结构体只声明用得到的字段和函数。坏处是 MPP 的结构体字段很多有些是内部字段声明不对会导致函数参数传错、直接崩溃或者读到垃圾数据调试起来很痛苦。第三条是找现成的第三方 Python 封装。社区里确实有人做过但维护状态参差不齐有的绑定的还是老版本 MPP和 RK3588 上的新库对不上。用之前一定要确认它适配的 MPP 版本。下面这张表是我当时做的对比可以对照着选方案上手成本可控性维护风险适合场景官方 mpp_py_demo 改低中低快速验证、单路跑通自研 ctypes 封装高高自己负责8 路并发、需要精细控制缓冲第三方封装库低低高简单场景、不做长期维护2.3 我最终选的方案和理由我的做法是“以官方 demo 为骨架做定制化裁剪”。具体说就是把 demo 里的结构体定义和函数原型拿过来作为基础然后针对 8 路并发的需求做几处改造一是把上下文和缓冲区封装成类方便每个线程各持一份二是精简掉用不到的回调减少 Python 层的开销三是加上自己的日志和统计方便观察每路的解码帧率和延迟。这里有个很重要的原则MPP 相关的对象不要在多个线程之间共享。MppCtx 本来就不是线程安全的一个上下文对应一路解码各线程各管各的互不干扰。共享的只有最底层的 VPU 硬件而这个调度由驱动和 HAL 层自己处理我们不用管。这个边界划清楚之后整个架构就简单了。提示不要试图用一个 MppCtx 去解多路码流哪怕你在线程里加锁也不行。MPP 的设计就是一个实例对应一路多路就是多实例。3. 环境搭建从系统检查到 MPP 库编译这一章讲落地。环境不对后面全是玄学问题所以我会把每一步的“为什么”也说清楚。3.1 系统与内核、VPU 节点确认拿到板子第一件事确认系统里有没有 VPU 的设备节点。通常是在/dev/下面能看到mpp_service或者rkvdec之类的节点。用ls /dev | grep -i mpp和ls /dev | grep -i vdec查一下如果什么都没有说明内核里对应的驱动没编进去这时候再怎么装库也没用得先解决内核。接着确认内核版本用uname -a。RK3588 的厂内核和主线内核在多媒体驱动上差异不小建议优先用板子厂商提供的内核驱动匹配度好。如果用的是自己编的主线内核VPU 驱动的编译选项要确认打开。还有一个容易忽略的点确认系统里已经装了librockchip_mpp如果厂商的镜像里自带那省事很多直接ldconfig -p | grep mpp看看有没有。没有的话自己编译就是下一步。3.2 编译安装 rockchip-mpp从 rockchip 的 mpp 仓库拉源码然后走标准的 CMake 流程。我习惯建一个 build 目录保持源码干净git clone https://github.com/rockchip-linux/mpp.git cd mpp mkdir build cd build cmake .. -DCMAKE_BUILD_TYPERelease -DRKPLATFORMON -DHAVE_DRMON make -j8 sudo make install sudo ldconfig这几个参数值得说一下。-DRKPLATFORMON是打开平台相关的那部分对 RK3588 是必须的-DHAVE_DRMON打开 DRM 支持如果你后面要用 DRM 直接显示或者走 DMA 分配内存这个要开-DCMAKE_BUILD_TYPERelease别省Debug 版本在解码这种高频调用下性能差得肉眼可见。编译完主要产物是librockchip_mpp.so装到系统库路径里。ldconfig是刷新动态库缓存不做这一步 Python 加载库的时候可能找不到。注意编译时要确认交叉编译和本机编译别搞混。如果你是在板子上直接编用上面的命令就行如果是在 x86 主机上交叉编译工具链和 sysroot 都要配对特别是libdrm的开发包要在 sysroot 里能找到。3.3 Python 侧依赖与 ctypes 封装文件准备Python 这边其实依赖很少ctypes是标准库自带的不需要额外装。你可能还会用到numpy来处理帧数据如果要把帧转成数组的话。再就是opencv-python或者ffmpeg来做拉流和测试但这个只在调试阶段用正式跑的时候拉流我建议用更轻的方式比如直接读文件或者用 GStreamer 收流。封装文件我建议单独放一个mpp_wrapper.py把库加载、函数原型声明、结构体定义、以及一个简洁的 Decoder 类都放进去。这样业务代码里只from mpp_wrapper import Decoder就行干净。库加载的时候用ctypes.CDLL(librockchip_mpp.so.1)注意带版本号否则可能加载到旧版本。如果是第一次接触 Python 环境配置装个 pip、配好虚拟环境避免污染系统 Python。别小看这一步我见过有人把库装到系统 Python 里结果和厂商镜像自带的工具冲突排查半天。4. 单路解码打通从 H.264 码流到 NV12 帧8 路的前提是 1 路能跑通。这一章把单路解码的完整调用序列拆开每一段都说明白在干什么。4.1 初始化三件套mpp_create / mpp_init / 解码器配置初始化的顺序是固定的不能颠倒。先mpp_create拿上下文和 API 句柄这一步会分配内部资源然后mpp_init指定工作模式解码用MPP_CTX_DEC并把编码类型传进去比如MPP_VIDEO_CodingAVC对应 H.264MPP_VIDEO_CodingHEVC对应 H.265最后通过 API 句柄调用解码器的控制命令设置外部缓冲区模式之类的参数。用 ctypes 表达的话函数原型大概长这样import ctypes from ctypes import c_void_p, c_int, c_uint32, POINTER mpp ctypes.CDLL(librockchip_mpp.so.1) mpp.mpp_create.argtypes [POINTER(c_void_p), POINTER(c_void_p)] mpp.mpp_create.restype c_int mpp.mpp_init.argtypes [c_void_p, c_void_p, c_int] mpp.mpp_init.restype c_int ctx c_void_p() api c_void_p() ret mpp.mpp_create(ctypes.byref(ctx), ctypes.byref(api)) assert ret 0, fmpp_create failed: {ret} ret mpp.mpp_init(ctx, api, MPP_CTX_DEC) assert ret 0, fmpp_init failed: {ret}这里每个assert都不是随手写的。MPP 的返回值是错误码一旦初始化失败后面全是连锁反应所以每个关键调用后都检查返回值出问题第一时间就能定位。我在实际项目里还会把返回值翻译成可读的错误信息打日志。初始化里最容易搞错的是编码类型。H.264 是 AVCH.265 是 HEVC写反了会出现解码器初始化成功但一送数据就报错或者出花屏。如果不确定码流是什么格式可以先用ffprobe看一眼。4.2 送包与收帧的主循环初始化完成后核心就是一个循环读一段码流封装成 packet 送进去再从队列里取 frame。这里面有个关键点是“送”和“取”是异步的。你送一个 packet 进去解码器不一定立刻吐出一帧因为 H.264 有 I 帧 P 帧的依赖关系有时需要攒够参考帧才出图。所以循环里不能假设“送一包取一帧”。正确的做法是维护一个输入队列和一个输出队列。送包线程持续往解码器喂数据取帧线程持续从解码器取数据两边通过条件变量协调。如果嫌复杂单路的时候也可以在同一线程里做“送一包、取到取不出为止”的轮询while running: pkt_data read_chunk(stream) # 读一段码流 if not pkt_data: break packet make_packet(pkt_data) # 用 ctypes 构造 MppPacket mpp.mpp_packet_set_size(packet, len(pkt_data)) ret mpp.mpp_decode(api, ctx, packet, c_void_p(0)) mpp.mpp_packet_deinit(ctypes.byref(packet)) while True: frame c_void_p() ret mpp.mpp_decode_get_frame(api, ctx, ctypes.byref(frame), 0) if ret ! 0 or not frame: break handle_frame(frame) mpp.mpp_frame_deinit(ctypes.byref(frame))这段代码里的mpp_decode_get_frame要在内层循环里反复调直到取不出为止。很多新手只调一次结果解码器内部缓冲越积越多最后卡死。这是单路调试最常见的坑。4.3 帧数据的地址、格式与搬运取到的 frame 不能直接当 Python 对象用得先搞清楚它在哪块内存、多大、什么格式。MPP 提供了一组 gettermpp_frame_get_buffer拿内存地址mpp_frame_get_width和mpp_frame_get_height拿宽高mpp_frame_get_hor_stride拿水平跨度。这里有个特别容易出错的点跨度stride不等于宽度。因为 VPU 处理时会对齐比如宽度 1920 的行实际每行在内存里可能占 1920 或者对齐到 1920但有些高分辨率会对齐到 2048 甚至更大。如果你按宽度去读内存而忽略 stride图像会出现斜着的错位。正确的做法是按 stride 定位每一行的起点只读其中的 width 个字节。拿到 NV12 之后如果只是写文件或者送 NPU一般不需要转换。非要用 numpy 表示的话可以用np.frombuffer配合正确的 stride 构造注意 Y 分量在前、UV 交错在后UV 的高度是 Y 的一半。这一步我建议先拿一个 I 帧验证图像对了再往下做多路。实操心得第一次跑单路时别急着接显示器先把解出来的 NV12 写成一个裸文件用ffplay -f rawvideo -pix_fmt nv12 -s 1920x1080 out.yuv播一下。图像正常说明内存读取和 stride 都对如果出现斜条纹八成是 stride 搞错了。5. 从 1 路到 8 路的并发架构单路跑通只是热身真正的工程难度在 8 路。这一章讲我怎么设计并发的结构以及在 Python 下要特别注意什么。5.1 线程模型与 GIL 的真实影响我的方案是每路一个独立的线程每个线程持有自己的 MppCtx、自己的输入输出队列、自己的统计信息。线程之间不共享任何解码对象只共享一些只读的配置和日志。很多人担心 Python 的 GIL 会让多线程形同虚设。这个担心在纯 Python 计算密集的场景下是对的但在我们这里不成立。原因前面提过ctypes.CDLL调用 C 函数时会释放 GIL也就是说当线程 A 正在 MPP 的 C 代码里解码时GIL 是放开的线程 B 完全可以进来做它自己的事情。真正的 Python 字节码执行比如处理本地变量、拼日志才会上锁这部分很轻。所以在这个架构里8 个线程基本是并行的。反过来说如果你用ctypes.PyDLL就完蛋了它是不会释放 GIL 的用错这一个字性能直接掉一半。这是我在实际项目里强调过很多次的点务必确认加载库用的是CDLL。5.2 队列、缓冲深度与丢帧策略每路一个输入队列用来缓存从网络或文件读进来的码流。队列深度是个需要斟酌的参数。设太浅网络一抖动就断流设太深延迟会积累实时性差。我的策略是给队列设一个有界长度比如 8 到 16 个 packet。当队列满了说明解码跟不上读取速度这时候主动丢最老的 packet而不是阻塞读线程。这里丢包要小心H.264 里 I 帧是不能随便丢的丢了会牵连后续一大串 P 帧。如果丢的是 P 帧问题不大最多卡一下如果丢的是 I 帧整个 GOP 都可能花。所以更稳妥的做法是只丢非关键帧或者干脆按“丢弃整个 GOP”的思路处理。输出侧也是同理。解码出来的 frame 如果下游消费比如显示或推理跟不上会造成内存堆积。我一般给输出设一个较小的缓冲满了就让取帧线程等一等因为 frame 占的内存大堆多了容易触发系统内存压力。5.3 带宽与内存的定量估算做 8 路之前我算过一笔账避免上线后才发现 DDR 带宽不够。1080p 的 NV12 一帧是 1920×1080×1.5 字节约 3.1MB。8 路、每路 30 帧一秒 240 帧就是约 745MB/s。这还只是解码输出产生的写带宽读取侧还有码流输入和后续消费读取加起来轻松过 1GB/s。RK3588 用的是 LPDDR4/4x 或者 LPDDR5理论带宽在十几 GB/s 到几十 GB/s所以整体是够的。但要注意的是DDR 带宽是整块芯片共享的NPU 推理、GPU 渲染、CPU 工作都在抢。如果同时还要跑 yolov8 这类模型NPU 本身也会消耗大量带宽这时候就要给解码留出足够余量别把系统压到极限。内存占用也得算。每个 MppCtx 内部会缓存若干帧按 8 路各缓存 5 帧算光解码缓存就是 8×5×3.1MB≈124MB。加上 Python 进程本身和码流缓冲整个解码服务的常驻内存控制在 300MB 以内是合理的。6. 8 路并发实测记录与调优前面都是设计这一章给实测数据。测试环境是我手上的一块 RK3588 开发板16GB 内存版本系统是 Ubuntu 22.04内核用的厂商版本。6.1 测试环境与码流规格码流方面我用了两种来源做对比一种是本地 H.264 文件循环读方便复现和压测另一种是真实的网络摄像头流用来验证网络抖动下的稳定性。本地文件统一是 1080p、H.264、25fps、GOP 长度 50平均码率 4Mbps。摄像头那批码率差异较大从 2Mbps 到 8Mbps 都有。验证方法是起 8 路解码每路统计每秒实际解出的帧数同时用厂家提供的工具看 VPU 占用。cat /sys/kernel/debug/mpp_service/...之类的节点能看到负载但具体路径不同内核不一样也可以用top看 CPU用free看内存。6.2 实测数据帧率、CPU、VPU 占用跑满稳定之后的数据大概是这样的指标软解 8 路硬解 8 路单路帧率约 11 fps稳定 25 fps总的 CPU 占用约 650%约 90%VPU 占用基本为 0约 55%内存占用约 900MB约 280MB端到端延迟800ms 以上120ms 左右可以看到硬解之后 CPU 从被吃满降到不足一个核的量VPU 还有差不多一半余量。这个余量意味着如果你上 12 路、16 路 1080p理论上也能扛但需要重新评估带宽和内存。延迟这一项差异也很关键。软解时 CPU 忙不过来收包线程排队严重延迟能到一秒以上做实时业务基本没法接受。硬解之后延迟稳定在一百多毫秒这里面主要还包含网络传输和缓冲纯解码延迟其实更低。6.3 三类调优手段的实际效果调优我动过三个地方效果都比较明显。第一是调整 packet 的送包粒度。最初我一段读 4KB后来改成按帧边界读取虽然代码复杂了一点但解码器收到的数据更规整帧率抖动小了很多。原因在于按帧送可以避免一个 packet 里混杂半帧数据减少解码器的等待。第二是控制日志输出。Python 里每条print都会拿 GIL8 路同时打日志光日志就能吃掉可观的 CPU。我把每帧的日志去掉了改成每秒汇总一次CPU 占用直接降了十几个百分点。这个和 Python 环境配置、logging 设置都有关默认的 root logger 在某些配置下开销也不小。第三是绑定 CPU 亲和性。把解码线程和网络收流线程分到不同的大核上避免互相抢核减少上下文切换。用os.sched_setaffinity就能设置简单有效。常见问题如果 VPU 占用一直上不去帧率也上不来先别怀疑代码看看是不是码流本身就不够快或者收流线程被别的东西卡住了。解码器是“喂多少解多少”它不会凭空变快。7. 踩坑记录与排查速查表最后这一章是干货里的干货都是我实际趟出来的坑整理成速查表方便对照。7.1 花屏、绿屏、首帧异常绿屏和花屏的成因基本就那几种。绿屏通常是 UV 分量的内存没读对或者读取时 offset 算错了NV12 里 UV 紧跟在 Y 后面偏移量是 width×height按 stride 算的时候特别容易错。花屏则多半是丢包或者丢帧导致的尤其是丢了 I 帧后续整个 GOP 都会花。首帧异常是另一个常见现象。有些码流第一个送进去的包不是 IDR 帧解码器拿不到参考帧就出不来正常画面。解决办法是在开始解码前先丢弃数据直到遇到第一个 IDR 帧或者让解码器进入等待关键帧的状态。我在封装里加了一个简单的判断遇到第一个关键帧才开始正式计数。现象常见原因排查方向整屏绿UV 偏移或 stride 算错检查 NV12 内存布局局部花屏丢包、丢 I 帧检查丢帧策略、网络质量首帧黑起始不是 IDR 帧丢弃到第一个关键帧图像斜切忽略了 stride 对齐按 stride 逐行读取7.2 内存泄漏与 fd 耗尽长时间跑之后进程内存缓慢上涨或者报“too many open files”基本是资源没释放。MppPacket 和 MppFrame 用完必须 deinit我在单路那节代码里每个 packet 和 frame 后面都跟了 deinit这不是多余的。一旦漏了MPP 内部的内存池会被撑爆。文件描述符耗尽通常来自 DMA 缓冲区的申请没释放。如果你用了 DRM 或者 dma-heap 分配内存用完要记得关闭对应的 fd。我建议在封装里用上下文管理器with语句来管理这些资源异常路径也能保证释放。排查工具上ls /proc/pid/fd | wc -l看 fd 数量是不是持续上涨cat /proc/pid/status | grep VmRSS看内存趋势。让程序跑上一两个小时再看短时间看不出来。7.3 VPU 占用上不去、帧率上不来这类问题我遇到过三次原因各不相同。第一次是送包节奏太慢本质上是收流侧的问题不是解码第二次是线程被 Python 层的锁卡住追下去发现是日志打得太多第三次是内存拷贝过多一帧数据在 Python 里被复制了好几遍。优化的核心思路是“减少跨语言和跨内存边界的操作”。能用指针引用的尽量别复制能放进 C 层做批量处理的别拿到 Python 层一条条处理。尤其是帧数据的处理如果只是要看或者送 NPU可以全程只传地址避免把整帧搬到 Python 的 bytes 对象里。到这里从芯片能力评估、MPP 分层理解、Python 封装选型到单路打通、8 路并发架构、实测数据和踩坑排查整条链路基本讲完了。我个人在实际项目里最深的体会是硬解的难点从来不在“调用那几个 API”而在内存布局、stride、资源释放和并发节奏这些细节上这些东西光看文档很难有感觉动手跑一遍、踩几次坑才真的记得住。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

rider开发asp.net webform项目 2026/9/29 2:52:53

rider开发asp.net webform项目

环境 windows10 .net framework4.8 rider2025.3 新建类库项目 编辑aspxstudy01.csproj 添加 WebForms 项目类型 GUID <!--下面一行代码表示是web项目--> <ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}<…

阅读更多 →
devenv 如何识别 vcxproj:Visual Studio 项目系统与 MSBuild 内部机制解析 2026/9/29 2:52:53

devenv 如何识别 vcxproj:Visual Studio 项目系统与 MSBuild 内部机制解析

在 VS2017 的使用周期里&#xff0c;应该有不少人做过这样的事&#xff1a;打开命令行&#xff0c;敲一句devenv MyProject.vcxproj&#xff0c;然后看着 Visual Studio 的界面刷一下蹦出来&#xff0c;项目被挂到了解决方案资源管理器里。如果你再狠一点&#xff0c;输入deven…

阅读更多 →
ARM SCP系统控制处理器:电源管理与低功耗调试实战指南 2026/9/29 2:52:47

ARM SCP系统控制处理器:电源管理与低功耗调试实战指南

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

阅读更多 →
嵌入式偶发故障排查:换机、录屏与批次对照三招闭环 2026/9/29 2:52:47

嵌入式偶发故障排查:换机、录屏与批次对照三招闭环

1. 偶发性故障的本质&#xff1a;不是“运气差”&#xff0c;而是信号链路上的时序裂痕你有没有遇到过这样的情况&#xff1a;设备明明昨天还一切正常&#xff0c;今天突然串口收不到数据&#xff0c;重启十次有八次能恢复&#xff1b;蓝牙模块连着连着就断开&#xff0c;但手机…

阅读更多 →
iOS面试核心知识点全解析:从Runtime到性能优化 2026/9/29 2:52:47

iOS面试核心知识点全解析:从Runtime到性能优化

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

阅读更多 →
MySQL高频面试题全解析:索引、事务、锁与主从复制 2026/9/29 2:52:40

MySQL高频面试题全解析:索引、事务、锁与主从复制

最近帮团队做了几轮后端岗位的面试&#xff0c;发现一个很有意思的现象&#xff1a;简历上写着“精通MySQL”的候选人&#xff0c;大概率会被同一批基础问题卡住。这些问题不是偏题怪题&#xff0c;恰恰是日常工作里每天都在碰的东西——索引为什么选B树、RR隔离级别下到底会不…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉