新闻详情

新闻详情

首页 / 资讯中心 / 详情

Perfetto Android 10+ 抓 trace 与 SQL 分析实战

发布时间:2026/10/1 16:07:28来源:尧图网络
Perfetto Android 10+ 抓 trace 与 SQL 分析实战
从 Android 10 开始设备内部多了一套叫 Perfetto 的性能追踪框架那个曾经被大家敲了无数遍的systrace.py悄悄退到了幕后而perfetto这个命令行工具成了抓系统级 trace 的主要入口。如果你还停留在systrace 抓一切的思路里会发现在新设备上要么命令跑不通要么抓出来的 trace 缺胳膊少腿——不是命令写错了而是底层的数据采集服务换了实现。这篇内容就是围绕 Perfetto 命令行工具在 Android 10 及更高版本上的实际用法展开的重点讲清楚它和旧工具的区别、三种工作模式怎么选、数据源和缓冲区怎么配、抓完之后怎么用 SQL 把 trace 里的问题挖出来。它适合几类人做 App 卡顿和启动耗时优化的同学、做系统层性能分析的工程师、以及需要给测试团队交付一套可复现抓取脚本的人。不需要你有 framework 源码的阅读经验但至少得会用 adb知道什么是线程、什么是调度。文中大部分操作我都在真机上跑过踩过的坑会单独拎出来说命令和配置可以直接抄。1. 为什么 Android 10 之后抓 trace 要换成 Perfetto1.1 systrace 的退场与 Perfetto 的上位逻辑systrace 本质上是一个 Python 脚本加上一套 ftrace 的封装它做的事情是在设备上打开若干 ftrace 事件和 atrace 分类把 kernel 和 framework 埋点写进 ftrace 的 ring buffer最后把原始文本拉回主机用 HTML 前端解析渲染。这个链路在 Android 9 及之前基本够用但有两个绕不过去的硬伤。第一个硬伤是数据源单一。systrace 只能吃 ftrace 这一路的输出进程内存、CPU 频率电压、系统属性、logcat、native 堆采样这些信息它压根不采集。你排查一个卡顿往往需要在 systrace、logcat、dumpsys 之间来回切时间戳还对不齐。第二个硬伤是缓冲和传输机制老旧。ftrace 的 ring buffer 一旦写满就开始覆盖systrace 又没有可靠的流式传输长一点的追踪经常出现前后数据接不上的情况。Perfetto 换了思路设备端跑一个常驻的追踪服务traced配合采集代理traced_probes通过 protobuf 定义的数据源data source来统一收数据。ftrace 只是众多数据源里的一个进程统计、系统统计、日志、堆采样都能挂进来所有数据走同一套时钟、同一份 trace 文件。命令行工具perfetto就是这套体系对外的客户端负责下发配置、接收数据、落地文件。理解这一点很关键Perfetto 不是 systrace 的升级版而是一个采集框架systrace 只是它的一个使用场景。1.2 三种工作模式别一上来就写配置文件perfetto命令有三种调用方式复杂度递增很多人一上来就去啃 protobuf 配置结果被劝退。实际按需选就行。第一种是轻量模式也叫命令行开关模式。所有参数直接写在命令行上适合我现在就要抓 15 秒的调度和图形事件这类临时需求。它内部会自动帮你拼一份最小配置你只需要给出 categories。第二种是配置文件模式用-c指定一份 protobuf 配置文本或二进制把缓冲区大小、数据源、采样周期、文件上限全部写死在文件里。适合团队协作、CI 里跑固定场景、或者需要同时采集多路数据源的复杂场景。第三种是纯开关模式的遗留兼容写法用来替代老的systrace参数比如--categories、--app。现在新写脚本不建议再用了因为它能表达的配置非常有限。模式触发方式适合场景主要限制轻量模式直接跟 categories临时抓取、快速定位数据源固定细粒度参数调不了配置文件模式-c file.pbtx--txt团队脚本、多数据源、长时追踪需要理解 protobuf 字段含义遗留开关模式--categories等迁移老的 systrace 脚本逐步废弃能力受限我的建议是先用轻量模式抓三五次摸清楚 categories 的粒度再过渡到配置文件模式。顺序反过来会浪费很多时间。2. 环境准备与最小可用链路2.1 设备端要看的两件事Android 10 及以上设备上Perfetto 的服务和二进制是系统内置的不需要你安装任何东西。但有两点必须先确认否则后面所有命令都会失败。第一确认追踪服务在跑。执行adb shell getprop init.svc.traced正常返回应该是running。如果返回空或者stopped说明这台设备上追踪服务被关掉了可能是定制 ROM 的裁剪也可能是厂商出于功耗考虑关停。这种情况下getprop persist.traced.enable一般也是 0普通用户没有权限改这个属性。第二确认perfetto二进制存在。执行adb shell which perfetto正常路径是/system/bin/perfetto。Android 10 之前的设备上这个二进制要么不存在要么是很早期的残缺版本。所以标题里强调Android 10 及更高版本不是随口一说它是硬性门槛。顺带解释一下设备端的几个角色。traced是调度中心负责解析配置、分发任务、汇总数据traced_probes是干活的真正去读/sys/kernel/tracing、/proc这些地方perfetto命令行本身则是一个消费者它连上traced把配置递过去再把流回来的数据写进文件。你敲完命令之后回车实际上是命令行进程在等traced通知它数据采完了。链路里任何一环断了现象都是命令挂住然后超时。2.2 主机端的三个工具主机这边需要准备的东西很少但缺一个都会卡住。adb是必须的而且建议用较新的版本。原因是 trace 文件是二进制 protobuf从设备往主机拉的时候必须用adb exec-out而不是adb shell后者会对输出做换行符转换二进制文件一旦被转换就直接报废。这个坑我在早期踩过表现是 trace 文件大小正常但用 UI 打开报解析失败查了半天才想起来是传输方式的问题。第二个工具是Perfetto UI用来可视化。浏览器打开后把 trace 文件拖进去就能看时间轴、线程轨道、CPU 频率曲线还有一个 Query 面板可以直接跑 SQL。它完全在本地解析文件trace 数据不会离开你的机器这点对处理公司内部设备的 trace 比较友好。第三个工具是trace_processor_shell命令行版的 trace 解析器。如果你要批量处理 trace、或者想在服务器上跑自动化分析这个比 UI 更合适。它的用法和 SQLite 命令行很像支持交互式和非交互式两种模式后面第五节会细讲。2.3 三条命令跑通第一个 trace先来一个最小可用的例子把整条链路验证通过。第一步抓取adb shell perfetto \ -o /data/misc/perfetto-traces/trace.perfetto-trace \ -t 16s \ -b 32768 \ sched freq idle am wm gfx view binder_driver hal dalvik camera input res逐项拆解一下。-o是输出路径Android 上 shell 用户可写的目录主要就是/data/misc/perfetto-traces别往/sdcard写SELinux 上下文不对会直接报权限错误。-t 16s是时长支持s、m、h后缀不写这个参数命令会一直跑下去很多新手的第一反应是命令卡死了其实就是忘了加时长。-b 32768是每个 CPU 的缓冲区大小单位 KB这里给了 32MB。后面的sched freq idle am wm gfx view binder_driver hal dalvik camera input res就是 atrace 的 category 列表轻量模式下直接跟选项后面不需要任何前缀。第二步把文件拉回主机adb exec-out cat /data/misc/perfetto-traces/trace.perfetto-trace trace.perfetto-trace第三步打开 Perfetto UI把文件拖进去。如果时间轴上能看到各个 CPU 的调度块、SurfaceFlinger 的帧轨道、目标 App 的主线程说明链路是通的。提示/data/misc/perfetto-traces目录里的文件不会自动清理长时间反复抓取会占掉不少空间建议养成本地分析完就adb shell rm的习惯。3. 内置数据源与抓取参数怎么选3.1 linux.ftrace最常用也最容易配错的一路linux.ftrace是使用频率最高的数据源它负责两件事订阅内核 ftrace 事件以及订阅 framework 侧的 atrace 分类。内核事件用ftrace_events字段指定常见的有sched/sched_switch线程切换、sched/sched_wakeup唤醒、sched/sched_waking、power/cpu_frequencyCPU 频率变化、power/cpu_idle空闲状态进出、irq/*中断、binder/*binder 事务。这些事件是分析 CPU 侧瓶颈的基础尤其是sched_switch和cpu_frequency两个几乎每份有价值的 trace 里都该有。atrace 分类用atrace_categories指定也就是轻量模式里直接跟在后面的那些名字。常用的几个含义是gfx抓图形渲染管线view抓 View 系统的 measure/layout/drawwm抓窗口管理am抓 Activity 生命周期binder_driver抓 binder 驱动层事务hal抓硬件抽象层调用dalvik抓虚拟机相关input抓输入事件分发res抓资源加载。这些分类不是都开着就好每开一个都会增加写入量缓冲区消耗速度会明显变快。atrace 还有一个容易忽略的参数atrace_apps。默认情况下只有系统进程和部分预装应用的 atrace 埋点会被采集你自己的 App 的Trace.beginSection打点是不出现的。要抓到它必须把包名加进atrace_apps列表或者用atrace_apps: *抓所有已安装应用。后者在真机上有副作用应用多了以后数据量会失控我一般只填目标包名。ftrace_config { ftrace_events: sched/sched_switch ftrace_events: sched/sched_wakeup ftrace_events: power/cpu_frequency atrace_categories: gfx atrace_categories: view atrace_categories: binder_driver atrace_apps: com.example.myapp buffer_size_kb: 16384 drain_period_ms: 250 }3.2 另外几路值得开的数据源只抓 ftrace 是过去 systrace 的思路Perfetto 的价值恰恰在于可以同时挂好几路。下面这几个我在实际分析里几乎每次都会带上一两个。linux.process_stats采集进程和线程的元数据包括进程名、线程名、oom_score_adj。别小看它没有这路数据源你的 trace 里线程轨道只会显示一堆 PID 数字根本认不出哪个是渲染线程。而且它开销极小建议无脑开。linux.sys_stats采集系统级统计比如内存使用、CPU 频率统计、进程数。它对排查内存抖动引发 GC 卡顿这类问题特别有用。里面的meminfo_period_ms和vmstat_period_ms控制采样周期默认 1000ms想看得更细可以调到 250ms代价是数据量上升。android.log把 logcat 内容打进 trace好处是日志和性能事件共用一套时间轴你在时间轴上点一个卡顿区间旁边就能看到同一时刻 App 打了什么日志定位因果关系的效率提升很明显。配置里用log_ids指定要抓的日志缓冲区一般是main、system、events。track_event是 Perfetto SDK 的数据源让应用进程通过 SDK 主动上报自定义事件。如果你的 App 接了 Perfetto SDK这路数据源能把业务埋点和系统事件对齐比单纯用Trace.beginSection灵活得多。android.heapprofd是 native 堆采样用来排查 native 内存泄漏。它对目标进程有要求必须是可调试应用且采样本身有性能开销不要在生产环境常态化开启。数据源采什么开销建议linux.ftrace内核事件 atrace中到高取决于事件数按需开分类别全开linux.process_stats进程线程元数据低建议常开linux.sys_stats内存、CPU 统计低到中排查内存问题时开android.loglogcat中需要对齐日志时开track_eventSDK 自定义事件低接了 SDK 就开android.heapprofdnative 堆采样高只在专项排查时开3.3 缓冲区到底该给多大这是最常被问的问题也是 trace 丢数据的主要原因。先讲原理每个 CPU 有一个独立的 ring bufferftrace 往里面写Perfetto 从里面读走并压缩。如果写入速度持续高于读取速度ring buffer 写满之后就开始按fill_policy处理——RING_BUFFER是覆盖最老的数据DISCARD是丢弃最新的数据。覆盖和丢弃哪个更合适我的经验是排查偶发问题用 RING_BUFFER抓完整启动流程用 DISCARD。偶发卡顿你不知道什么时候发生保留最近的现场更实用启动流程有明确的起止点丢弃末尾数据至少能保证开头是完整的。至于给多大可以粗略估算。一台 8 核设备在中高负载下全量订阅sched_switch加sched_wakeup加cpu_frequency每秒写入量能到几 MB 级别。默认的 ring buffer 在部分设备上只有 4MB 左右也就是一两秒就被填满。所以你只要看到 trace 开头正常、后面突然变稀疏基本可以确定是缓冲区太小。我的常用配置是短时抓取30 秒内给-b 32768也就是每 CPU 32MB长时追踪几分钟以上给 64MB同时把fill_policy设成RING_BUFFER并且把不必要的事件去掉。还有一种做法是用--size限制整个 trace 文件的上限比如--size 128mb到量自动停止防止长跑把设备存储写满。注意缓冲区不是越大越好。它占用的是内核内存给得过大在低内存设备上可能触发分配失败命令直接报错退出。64MB 是我在主流设备上验证过的安全上限。4. 配置文件模式实战4.1 一份可复用的文本配置逐行拆解配置文件模式的核心是把所有参数写进一份文本 protobuf用--txt -c指定。文本格式的好处是可读性高能直接进 Git 做版本管理。下面这份是我做应用启动分析时用的模板。buffers { size_kb: 65536 fill_policy: RING_BUFFER } data_sources { config { name: linux.ftrace ftrace_config { ftrace_events: sched/sched_switch ftrace_events: sched/sched_wakeup ftrace_events: power/cpu_frequency atrace_categories: am atrace_categories: wm atrace_categories: gfx atrace_categories: view atrace_categories: binder_driver atrace_apps: com.example.myapp } } } data_sources { config { name: linux.process_stats process_stats_config { scan_all_processes_on_start: true } } } data_sources { config { name: android.log android_log_config { log_ids: main log_ids: system } } } duration_ms: 20000分段解释。buffers段定义缓冲区这里只写了一个 64MB 的 ring buffer。如果想区分不同数据源用不同缓冲区可以定义多个 buffers然后在 data_sources 里用target_buffer索引引用这是进阶玩法多数据源混合时能避免日志把 ftrace 的空间挤掉。每个data_sources段是一路数据源。config.name必须是系统已注册的数据源名写错了不会报错只是那一路静默不采集这个坑很隐蔽建议第一次配完先抓一小段用 UI 确认每一路都有数据。duration_ms是总时长单位毫秒。它和命令行-t是等价的如果两边都写了以命令行为准。下发这份配置有三种方式。最直接的是推送到设备再指定路径adb push trace_config.pbtx /data/misc/perfetto-configs/ adb shell perfetto --txt -c /data/misc/perfetto-configs/trace_config.pbtx \ -o /data/misc/perfetto-traces/startup.perfetto-trace第二种是走标准输入配置文件不用落地cat trace_config.pbtx | adb shell perfetto --txt -c - -o /data/misc/perfetto-traces/startup.perfetto-trace第三种最省事把输出直接怼到主机文件连 pull 都省了adb exec-out perfetto --txt -c - -o - trace_config.pbtx startup.perfetto-trace第三种写法我用了很久但要注意-o -表示输出到标准输出此时必须用adb exec-out用adb shell会因为换行转换破坏二进制内容这个坑重复出现率极高值得单独记一笔。4.2 分级抓取让数据量回归可控配置写顺手之后最大的诱惑就是全都开结果抓出来的文件几百兆打开卡半天真正有用的信息淹没在噪声里。解决思路是分级。第一级是always-on层包括linux.process_stats和少量 sched 事件这些开销低、价值高任何场景都可以带。第二级是场景相关层比如排查滑动卡顿就加gfx、view、input排查启动耗时就加am、wm、binder_driver。第三级是专项层比如 heapprofd、linux.perf只在明确怀疑某一类问题时才开而且只开一小段。atrace_apps 的控制也是分级的一部分。不要写*把目标包名明确列出来这样其他应用的埋点不会来抢缓冲区。如果一次要分析多个应用可以用多次atrace_apps字段逐个添加而不是用通配符。还有一个实战技巧先用短时长、小 buffer 抓一次探路 trace看看数据源的产出速率再决定正式抓取时的 buffer 大小。比如先抓 5 秒看 ring buffer 是否被打满UI 里能看到 buffer 的使用曲线再按比例放大。比拍脑袋定 64MB 靠谱得多。4.3 长时追踪与后台模式抓偶发的卡顿问题在于你不知道它什么时候出现总不能盯着屏幕手动按。Perfetto 提供了两种应对方式。一种是加长时长配合大 ring buffer比如duration_ms: 3000005 分钟配 64MB ring buffer让旧数据自动被覆盖最后看到的是最近几分钟的现场。这种方式不需要额外权限普通 shell 就能用。另一种是 detached 模式也就是-s或--detach加上一个 keyadb shell perfetto -s mytrace -o /data/misc/perfetto-traces/long.perfetto-trace \ -t 5m -b 65536 sched freq gfx view加了-s之后命令行会立刻返回采集在设备后台继续。这个 key 就是这次会话的标识后续可以用带同样 key 的命令去做状态查询。这个模式对脚本化特别友好你可以在自动化测试开始前启动采样测试结束时再来收文件。需要提醒的是长时间追踪必须关注存储空间和功耗。64MB 的 buffer 在持续高负载下可能被写满并被RING_BUFFER策略持续覆盖最终文件大小取决于实际写回的数据量但设备侧的内存占用是实打实的。我的习惯是控制在 5 分钟以内超过这个时长的分析需求改用分段抓取、事后拼接或按关键事件触发的思路。5. trace_processor 命令行分析5.1 trace_processor_shell 的两种用法拿到 trace 文件之后UI 适合看趋势和调用链但如果要回答主线程在启动过程中最长的 20 个区间是哪些这种具体问题SQL 比鼠标点选高效得多。trace_processor_shell就是干这个的。基础用法是直接跟文件路径进入交互模式trace_processor_shell trace.perfetto-trace进去之后就是一个类似 SQLite 的提示符可以直接敲 SQL。想退出敲.quit。非交互式用法更适合脚本两种写法trace_processor_shell -q query.sql trace.perfetto-traceecho select count(*) from slice | trace_processor_shell trace.perfetto-trace非交互模式下结果默认以表格形式打到标准输出可以配合-q加输出格式参数转成 CSV方便后续用脚本处理。批量分析几十份 trace 的时候这个组合特别顺手。提醒trace_processor 的表结构在不同版本之间有过调整比如帧时间线相关的表在不同版本里字段有增删。跑查询前先执行select name from sqlite_master where typetable看一眼实际有哪些表比直接抄网上的 SQL 稳。5.2 三条能立刻用上的查询第一条找出主线程耗时最长的区间。这是最通用的入口卡顿分析基本都从这里开始。select s.name, s.ts, s.dur / 1e6 as dur_ms from slice s join thread_track tt on s.track_id tt.id join thread t on tt.utid t.utid where t.name main order by s.dur desc limit 20;slice表是核心表代表时间轴上的每一个区间thread_track把区间关联到线程轨道thread表再给出线程名。dur的单位是纳秒除以1e6换算成毫秒看着更直观。跑完这一条如果发现排在前面的全是某个自定义的打点名基本就锁定了怀疑对象。第二条统计每个线程占用的 CPU 时间。select t.name, sum(s.dur) / 1e6 as total_ms, count(*) as cnt from sched_slice s join thread t using(utid) group by t.name order by total_ms desc limit 20;sched_slice表是把内核的调度事件整理成某个线程在某段时间内占用 CPU的结构。这条查询能快速看出到底是谁在后台偷跑 CPU经常能抓到一些意料之外的线程比如某个第三方 SDK 的轮询线程。count(*)是切换次数次数多但总时长短说明线程频繁被唤醒这本身也是一个耗电问题。第三条看线程被阻塞的情况。select t.name, ts.state, sum(ts.dur) / 1e6 as total_ms from thread_state ts join thread t using(utid) where t.name main group by t.name, ts.state order by total_ms desc;thread_state表描述线程的状态区间state字段的值包括Running、R可运行但没拿到 CPU、S睡眠、D不可中断睡眠等。这条查询的价值在于区分两类问题如果R状态时间很长说明线程想跑但 CPU 被别的任务占着是调度层面的问题如果是D或者S很长说明在等 IO 或等锁要往下查具体等谁。这个区分直接决定你下一步往哪个方向查比盲猜有效得多。5.3 SQL 和 UI 配合的正确姿势SQL 和 UI 不是二选一配合起来效率最高。我的流程一般是先跑 SQL 找出可疑的时间点ts字段就是时间戳记下来然后到 UI 里跳到那个时间点看同一时刻其他轨道上发生了什么——是不是刚好有个 GC、是不是有个大文件读写、是不是 CPU 频率被压到了最低。SQL 负责定位UI 负责解释两边来回切几次问题的轮廓就出来了。还有一个小技巧在 UI 的 Query 面板里可以跑一模一样的 SQL结果可以直接在界面上高亮到时间轴。这比在命令行里查到时间戳再手动找要快。所以我通常先用命令行快速扫几轮确定方向之后转到 UI 做精查。6. 常见问题与排查技巧实录6.1 命令层面的坑报perfetto: not found。三种可能设备低于 Android 10这台设备是定制 ROM 裁剪了 Perfettoadb 连的其实是模拟器且镜像版本不对。先adb shell getprop ro.build.version.sdk看 API 级别低于 29 就别折腾了老老实实用老工具。命令敲下去没反应也不退出。九成是没写时长或者写的时长格式不对。检查-t参数注意单位后缀不能省-t 10会被当成长度单位不明。另外配置模式下要确认duration_ms存在。报缓冲区分配失败。给的 buffer 太大设备内存扛不住。降一半再试或者把fill_policy改成RING_BUFFER减少内存压力。低端设备上 16MB 可能就已经是上限。trace 文件拉回主机打不开。优先怀疑传输方式。二进制文件必须用adb exec-out cat或者adb exec-out perfetto -o -用adb shell会做换行符转换。判断方法很简单把文件拉到 Linux 上跑file命令正常的 trace 文件会被识别为 protobuf 相关格式如果被识别成文本那基本就是被转换过了。6.2 数据层面的坑trace 打开后线程轨道全是数字。缺linux.process_stats数据源。这路数据源负责提供进程线程的名字映射不开的话只能用 PID 去猜。轻量模式下已经默认包含配置文件模式下要手动加上。抓不到自己 App 的埋点。atrace_apps没配。这个字段在轻量模式下对应--app参数。另外确认一下 App 里用的是Trace.beginSectionframework 层还是 Perfetto SDK 的track_event前者走 atrace 分类后者要单独开track_event数据源。前后数据接不上、中间有空洞。缓冲区被写满并按策略处理过。要么加大 buffer要么减少事件数量要么把长追踪拆成多段。可以先看 UI 里缓冲区使用情况的曲线来确认。文件特别大但有效信息很少。数据源开太多。回到分级抓取的思路把 always-on 层之外的都关掉只保留当前问题相关的。6.3 一份速查表现象最可能原因处理方式perfetto: not found设备版本低于 10 或 ROM 裁剪确认 API 级别换设备命令挂住不退出未指定时长补-t或duration_ms报 buffer 分配失败buffer 超设备上限降低-b或 buffer 段大小文件解析失败传输方式破坏了二进制改用adb exec-out线程轨道无名字缺 process_stats补上该数据源无应用埋点atrace_apps未配添加目标包名数据中间断档ring buffer 溢出加大 buffer 或减少事件文件过大数据源开太多回归分级抓取还有一条经验值得单独说每次抓取都记下设备型号、系统版本和配置内容。同一份卡顿问题在不同芯片平台上的成因可能完全不同性能分析的结论强依赖设备上下文。我见过太多只有一份 trace 文件、没有任何设备信息的求助最后只能靠猜。养成把配置文件和 trace 一起归档的习惯过几周回头看还能复现当时的分析路径。另外提一句时间戳对齐的事。Perfetto 默认用 BOOTTIME 时钟域和内核 ftrace、调度事件是同一套时钟所以 trace 内部的时间对齐天然没问题。但如果你要把它和外部日志比如另一台机器上抓的 log对照就需要额外做时钟快照配置里加上clock_snapshot相关的数据源用两个时钟域的对应关系做换算。这一步在有跨设备联合分析的场景里是必须的日常单设备分析用不上。我个人在真机上反复抓取的体会是Perfetto 命令行最大的价值不在于它能抓多少种数据而在于它把抓取这件事变成了一份可以进版本库的配置。你调好一份针对自己 App 启动流程的配置文件交给测试同学他们用同样的命令在同样的设备上抓出来的 trace和你手里的样本是可以直接对比的。这种可复现性是过去靠命令行参数临时拼凑的 systrace 时代很难做到的。至于缓冲区给多大、分类开几个没有标准答案抓三到五次、看一眼 buffer 曲线数字自己就出来了。
网站建设高端定制企业官网
RELATED

相关资讯

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

较早相关资讯

最新相关资讯

万仙山旅游管理系统实战:Spring Boot+MyBatis+Redis完整设计 2026/10/1 16:55:59

万仙山旅游管理系统实战:Spring Boot+MyBatis+Redis完整设计

先说结论:如果你是准备拿它当毕业设计,或者想找一个能完整跑通“前后端分离 业务系统设计”的练手项目,这个题目很适合。万仙山是河南新乡辉县著名的太行山水景区,名字听着像旅游网站,实际要做的是一套典型的“后台管…

阅读更多 →
嵌入式内存管理实战:从malloc到栈溢出的避坑指南 2026/10/1 16:55:53

嵌入式内存管理实战:从malloc到栈溢出的避坑指南

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

阅读更多 →
腾讯WeKnora本机部署实战:RAG知识库与Agent沙箱解析 2026/10/1 16:55:52

腾讯WeKnora本机部署实战:RAG知识库与Agent沙箱解析

1. 为什么我要在本机折腾 WeKnora第一次看到 WeKnora 这个名字,是在一个做企业知识管理的群里。有人甩了张截图,说腾讯微信团队开源了一个 RAG 知识库项目,能直接吃文档、建索引、跑问答,还带 Agent 和沙箱能力。我当时的第一反应…

阅读更多 →
MiniMax M3接入指南:GroupID鉴权与401报错排查 2026/10/1 16:55:52

MiniMax M3接入指南:GroupID鉴权与401报错排查

最近后台私信被 MiniMax M3 接入的问题刷屏了,清一色是同一个场景:照着官方文档把代码复制下来,第一个请求就被甩一脸unexpected status 401 unauthorized: incorrect api key provided: sk-svcac****。很多人第一反应是密钥复制错了&#xf…

阅读更多 →
Linux目录结构深度解析:FHS、/bin、/etc与挂载点 2026/10/1 16:55:46

Linux目录结构深度解析:FHS、/bin、/etc与挂载点

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

阅读更多 →
前后端分离Long型精度丢失:根因、前端处理与后端序列化方案全解 2026/10/1 16:55:45

前后端分离Long型精度丢失:根因、前端处理与后端序列化方案全解

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

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

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

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