1. 为什么RV1106的系统构建值得单独拿出来聊
RV1106这颗芯片这两年在一线出现的频率越来越高,尤其是做低功耗视觉终端、电池类IPC、门铃、猫眼、扫码设备这类产品的团队,几乎绕不开它。它本质上是瑞芯微面向轻量级AI视觉场景推的一颗SoC,单核Cortex-A7加上内置的NPU,再配上一颗RISC-V协处理器做低功耗常驻任务,封装小、BOM成本压得下来,这是它被大量选型的直接原因。但真正上手之后你会发现,RV1106的嵌入式Linux系统构建和你在RK3399、RK3568上习惯的那套流程,体感完全不一样——它的存储介质通常是SPI NAND或者小容量eMMC,内存也紧,编译产物动辄几个G,烧录镜像又要求分区对齐,稍不注意就是编译两小时、启动卡在logo。
我前后在RV1106上做过三四个量产项目,从最早的SDK默认配置一路踩到能把冷启动压进两秒以内,中间关于编译优化和启动优化的坑基本都趟过一遍。这篇东西不是官方文档的复述,而是把“从编译到启动”这条链路上真正影响效率的环节拆开讲:SDK怎么裁剪、编译怎么并行、镜像怎么瘦身、启动阶段怎么定位耗时、哪些优化是性价比高的、哪些是看着美好实际收益极低的。适合已经拿到RV1106开发板、跑通了官方demo、但觉得构建慢、启动慢、不知道怎么下手的同学,也适合正在做嵌入式Linux项目实战、需要把系统体积和启动时间当成硬指标的团队参考。
需要先说明一点:下面涉及的具体路径、配置项名称,不同版本的SDK会有差异,我尽量讲清楚“为什么这么改”而不是死记某个文件,这样你换SDK版本也能自己推。所有参数和数值都是我在实际项目里测出来的量级,不是理论值,你的板子和配置不同,绝对值会有出入,但优化方向和收益比例是通用的。
2. 构建流程的整体设计与思路拆解
2.1 RV1106系统构建到底包含哪些阶段
很多人一说“编译系统”就以为是敲一条make然后等,其实RV1106的完整构建链路至少分成五段,每一段的耗时和优化空间都不一样。第一段是工具链准备,RV1106用的是arm-rockchip830-linux-uclibcgnueabihf这类交叉工具链,SDK里通常自带预编译版本,但如果你要自己换glibc或者换版本,这一步会引入兼容性问题。第二段是uboot编译,产出SPL和uboot镜像,负责最底层的DDR初始化和引导。第三段是kernel编译,包括设备树、驱动、内核模块,这是整个构建里最耗时的一块。第四段是rootfs构建,用buildroot或者busybox拼出根文件系统,再往里塞应用和库。第五段是打包与镜像生成,把前面所有产物按分区表拼成一个可烧录的update.img或者分区镜像。
这五段里,kernel和rootfs占了80%以上的时间,uboot通常几十秒就完事。所以优化精力要放在kernel和rootfs上,而不是去纠结uboot那点时间。我见过有同学花半天研究怎么让uboot编译快10秒,结果kernel那边一个配置没关就多花二十分钟,这就是没抓住重点。
2.2 为什么RV1106的构建比大芯片更“敏感”
RV1106的构建敏感,核心原因是它的资源约束和存储介质特性。大芯片的板子往往配大容量eMMC、内存也宽裕,编译产物大一点无所谓,烧录慢一点也能忍。但RV1106常见的搭配是SPI NAND,容量可能就128MB或者256MB,rootfs稍微多塞点东西就爆分区;而且SPI NAND的写入速度和擦除特性决定了镜像不能太大,否则烧录时间成倍增长。另外它的DDR容量通常也小,kernel里如果开了太多调试选项,启动时内存占用高,容易在早期就OOM。
这就决定了RV1106的构建思路必须是**“按需裁剪”而不是“全量编译”**。官方SDK默认配置是为了兼容尽可能多的场景,所以打开了一堆你可能根本用不到的驱动、文件系统、调试功能。你要做的第一件事不是优化编译命令,而是先把这些无关的东西关掉。裁剪带来的收益是双重的:编译时间下降,镜像体积下降,启动时间也跟着下降。这是RV1106上性价比最高的优化,没有之一。
2.3 编译优化和启动优化的关系
这里要纠正一个常见误区:很多人把编译优化和启动优化当成两件独立的事,其实它们在RV1106上是强耦合的。你编译时关掉的每一个驱动、每一个文件系统、每一个调试选项,都会直接反映到启动时间上。比如kernel里关掉一个用不到的USB网卡驱动,编译时省几秒,启动时省的是驱动probe的时间;rootfs里去掉一个用不到的服务,编译时省打包时间,启动时省的是服务拉起的时间。
所以正确的做法是把编译配置当成启动优化的第一手段,而不是等系统跑起来了再去用工具分析启动耗时。我一般的流程是:先做一轮激进的裁剪,把明显用不到的全关掉,编译一版测启动;然后根据启动日志再针对性关第二轮;最后才用工具去抠那些裁剪不掉的耗时点。这样比一上来就上工具分析要高效得多,因为工具分析出来的很多耗时点,其实你直接关掉对应功能就没了,根本不需要优化。
3. 核心细节解析与实操要点
3.1 工具链选择:为什么不要轻易换
RV1106 SDK自带的工具链是经过验证的,和kernel、uboot的版本是配套的。我强烈建议不要轻易换工具链,除非你有明确的理由。换工具链最常见的动机是想用更新的gcc拿到更好的优化,但实际收益往往被兼容性问题吃掉。比如你换了高版本gcc,kernel里某些老代码可能编译报错,你得一个个打patch;换glibc版本可能导致rootfs里的库和工具链不匹配,运行时各种找不到符号。
如果你确实需要换,我的经验是只换工具链的优化等级,不换工具链本身。也就是保持SDK自带的工具链,通过CFLAGS调整优化参数。RV1106的Cortex-A7支持到-mcpu=cortex-a7 -mfpu=neon-vfpv4,确保这两个参数正确,比换工具链带来的收益更实在。另外注意,kernel和uboot的编译优化等级不要盲目上-O3,-O2在嵌入式场景下通常是体积和性能的最佳平衡点,-O3可能让镜像变大反而拖慢启动。
3.2 kernel裁剪:从menuconfig到defconfig的取舍
kernel裁剪是RV1106构建优化的主战场。官方SDK一般会提供一个rv1106_defconfig,这个配置是“大而全”的,你要做的是基于它做减法。具体怎么减,我按优先级列一下。
第一优先级是关掉所有用不到的驱动。RV1106常见的应用场景是视觉终端,那么声卡、HDMI、大部分USB设备类驱动、蓝牙、WiFi(如果你用外挂模组另说)、各种传感器驱动,如果项目用不到就全关。这里有个技巧:不要一个个在menuconfig里找,直接看.config里=y和=m的项,对照你的硬件原理图,用不到的批量改成# ... is not set。
第二优先级是关掉调试和追踪功能。CONFIG_DEBUG_INFO、CONFIG_FTRACE、CONFIG_KPROBES、CONFIG_DEBUG_KERNEL这些,量产版本一律关掉。CONFIG_DEBUG_INFO关掉能显著减小kernel体积,因为它会往vmlinux里塞大量调试符号。我实测过,光关掉DEBUG_INFO,kernel镜像能小20%到30%,编译时间也能省一截。
第三优先级是精简文件系统支持。kernel里支持的文件系统类型,只留你实际用的。RV1106上通常用squashfs做只读根、overlayfs做可写层、tmpfs做临时目录,那么ext4、f2fs、ntfs、vfat这些如果不用就关掉。每关一个文件系统,kernel就少一块代码,启动时也少一次初始化。
第四优先级是调整内核特性。比如CONFIG_PREEMPT,如果你的应用对实时性要求不高,用默认的CONFIG_PREEMPT_NONE或者CONFIG_PREEMPT_VOLUNTARY就行,CONFIG_PREEMPT会引入额外开销。再比如CONFIG_HZ,默认可能是100或者250,对于视觉终端,100通常够用,调高会增加调度开销。
3.3 rootfs构建:buildroot还是busybox自己拼
RV1106的rootfs构建有两条路:用buildroot,或者用busybox自己拼。buildroot的好处是自动化、依赖管理清晰,坏处是它默认会带一堆你可能不需要的包,而且buildroot本身的配置也需要裁剪。busybox自己拼的好处是极致精简,坏处是依赖要自己处理,容易漏库。
我的建议是:项目初期用buildroot快速跑通,量产阶段根据实际情况决定是否换成手工拼。如果buildroot裁剪得当,其实也能做到很小。buildroot裁剪的关键是make menuconfig里的Target packages,把不需要的包全关掉,尤其是那些会自动拉一堆依赖的包,比如python、perl、各种网络工具。另外buildroot的BR2_TARGET_ROOTFS_SQUASHFS要打开,用squashfs做根文件系统,压缩率高,读取快。
如果你决定手工拼,核心是只放运行时真正需要的库和可执行文件。用ldd查每个可执行文件的依赖,把依赖库拷进去,然后反复在板子上跑,缺什么补什么。这个过程比较磨人,但最终能拼出一个几十MB甚至更小的rootfs。注意库的版本要和工具链一致,否则会出现运行时找不到符号的问题。
3.4 并行编译:make -j到底开多少
make -jN的N怎么定,是个老生常谈的问题。理论上N等于CPU核心数,但实际要看内存。RV1106的编译通常在x86主机上做,如果你的主机是8核16G,make -j8通常没问题;如果是8核8G,make -j8可能因为内存不够导致编译进程被OOM killer干掉,反而更慢。我的经验是N取核心数和内存GB数的较小值,比如8核16G取8,8核8G取6,4核8G取4。
另外kernel编译和rootfs编译可以分开并行。比如你先make -j8 kernel,编译的同时在另一个终端make -j4 rootfs,只要内存够,两者互不干扰。但要注意SDK的顶层Makefile可能有依赖关系,直接并行可能出问题,稳妥的做法是分步执行,而不是一条make -j全包。
还有一个容易被忽略的点:ccache。如果你要反复编译kernel,装个ccache能大幅减少重复编译的时间。配置方法是在工具链的gcc前面加ccache,比如export CROSS_COMPILE="ccache arm-rockchip830-linux-uclibcgnueabihf-"。第一次编译会慢一点(因为要填充缓存),后续改代码再编译,命中缓存的部分几乎是瞬间完成。我实测在反复调试驱动阶段,ccache能把编译时间砍掉一半以上。
4. 实操过程与核心环节实现
4.1 环境准备与SDK拉取
先确认主机环境。RV1106的SDK编译对主机有要求,Ubuntu 18.04或者20.04是比较稳的,22.04也能用但可能要装一些老版本的依赖库。必备的包包括build-essential、git、repo、libssl-dev、libncurses-dev、device-tree-compiler、bc、rsync、cpio、python3等。缺哪个装哪个,编译报错第一件事就是看是不是缺依赖。
SDK拉取一般用repo工具,按官方给的manifest来。拉完之后先别急着编译,先看一眼目录结构,确认kernel、uboot、buildroot、device/rockchip这些目录都在。然后执行一次./build.sh lunch选择板级配置,这一步会生成对应的配置文件,后续编译都基于这个配置。
提示:SDK拉取和首次编译建议在SSD上进行,机械硬盘上编译kernel会明显更慢,因为kernel编译涉及大量小文件读写。
4.2 第一轮裁剪:从defconfig开始
拿到SDK后,第一件事是进kernel目录做裁剪。先make ARCH=arm rv1106_defconfig生成.config,然后make ARCH=arm menuconfig进去看。但menuconfig一项项找太慢,我的做法是直接编辑.config,用搜索的方式批量处理。
具体操作:先用grep -n "=y" .config列出所有编译进内核的项,对照硬件原理图,把用不到的驱动标记出来。比如你的板子没有音频codec,就搜SND相关的项,全改成# CONFIG_XXX is not set。没有USB Host,就搜USB相关的项关掉。这个过程要细心,关错了会导致启动失败,所以建议每关一批就编译一次、启动一次,确认没问题再关下一批。
裁剪完之后,把当前.config保存成自己的defconfig,比如make ARCH=arm savedefconfig,然后把生成的defconfig放到arch/arm/configs/下,命名为rv1106_mini_defconfig。以后编译直接用这个,不用每次重新裁。
4.3 kernel编译参数调优
kernel编译时,除了-j,还有几个参数值得调。第一个是LOCALVERSION,如果你不需要在版本号里加后缀,把它清空,能避免一些不必要的重新编译。第二个是INSTALL_MOD_STRIP,编译模块时加上strip,能减小模块体积。第三个是KCFLAGS,可以在这里追加优化参数,比如KCFLAGS="-mcpu=cortex-a7 -mfpu=neon-vfpv4",确保针对RV1106的CPU特性优化。
编译命令我一般这么写:
make ARCH=arm CROSS_COMPILE=arm-rockchip830-linux-uclibcgnueabihf- \ rv1106_mini_defconfig make ARCH=arm CROSS_COMPILE=arm-rockchip830-linux-uclibcgnueabihf- \ KCFLAGS="-mcpu=cortex-a7 -mfpu=neon-vfpv4" \ -j8 zImage modules dtbs注意zImage、modules、dtbs分开写,这样如果只是改了设备树,可以只编dtbs,不用全量重编。实测只编dtbs几秒钟就完事,全量编kernel要几分钟到十几分钟不等。
4.4 rootfs精简与打包
rootfs这块,如果用buildroot,先make menuconfig进Target packages,把不需要的包关掉。重点检查这几类:网络工具(curl、wget、ssh如果不用就关)、脚本语言(python、perl、lua如果不用就关)、图形库(如果不用GUI就关)、数据库(不用就关)。每关一个包,buildroot会重新计算依赖,可能连带关掉一批,所以关完要重新make看有没有报错。
打包时用squashfs,压缩算法选xz或者gzip。xz压缩率高但压缩慢,gzip快但体积大。对于RV1106这种存储紧张的板子,我倾向用xz,虽然编译时多花点时间,但镜像小,烧录和启动都受益。buildroot里配置BR2_TARGET_ROOTFS_SQUASHFS和BR2_TARGET_ROOTFS_SQUASHFS4_XZ即可。
打包命令buildroot会自动执行,产物在output/images/下。拿到rootfs.squashfs后,用mksquashfs可以手动再压一次,确认体积。如果体积还是大,用unsquashfs解开看哪个目录占空间,针对性删。
4.5 镜像打包与分区对齐
RV1106的镜像打包用SDK里的build.sh或者mkimage工具。分区表通常在device/rockchip/rv1106/下的配置文件中定义。这里有个关键点:分区起始地址要对齐到擦除块大小。SPI NAND的擦除块通常是128KB或者256KB,如果分区起始地址没对齐,烧录后可能出现读写异常。SDK默认的分区表一般是对齐的,但如果你自己调整了分区大小,一定要检查对齐。
打包命令大致是:
./build.sh firmware产物是update.img,用瑞芯微的烧录工具烧到板子上。烧录前确认板子进了maskrom或者loader模式,烧录工具能识别到设备。
注意:SPI NAND烧录比eMMC慢很多,一个100MB的镜像可能要烧几分钟。所以镜像瘦身的收益在烧录环节也很明显,别只盯着编译时间。
5. 启动优化:从冷启动到应用拉起的全链路
5.1 启动阶段划分与耗时定位
RV1106的启动链路大致是:上电 -> BootROM -> SPL -> uboot -> kernel -> init -> 应用。要优化启动,先得知道每个阶段花了多久。最直接的办法是打开uboot和kernel的启动时间戳。uboot里可以打开CONFIG_BOOTSTAGE,kernel里打开CONFIG_PRINTK_TIME,这样串口日志每行都带时间戳,一眼就能看出哪个阶段慢。
我一般会记录几个关键时间点:uboot开始执行的时间、kernel开始解压的时间、kernel initcall完成的时间、init进程启动的时间、应用起来的时间。这几个点一拉出来,瓶颈在哪就清楚了。实测RV1106上,如果没优化,uboot可能占1秒多,kernel占2到3秒,init和应用占1到2秒,总共5到6秒很常见。优化目标通常是压到2秒以内。
5.2 uboot阶段的优化
uboot阶段能优化的点不多,但有几个值得做。第一是关掉uboot里的调试输出,CONFIG_DEBUG_UART相关的日志如果不需要就关掉,串口打印本身也耗时。第二是精简uboot的命令集,用不到的cmd全关掉,能减小uboot体积,加载更快。第三是调整DDR初始化参数,这个要参考瑞芯微给的DDR配置工具,参数不对会导致DDR跑在低频,拖慢整个启动。第四是跳过不必要的硬件自检,比如内存自检,量产版本可以关掉。
uboot阶段还有一个大杀器是SPL直接引导kernel,跳过uboot的完整初始化。但这个改动比较大,需要SPL里就完成DDR初始化和kernel加载,适合对启动时间极度敏感的场景。一般项目做到关日志、精简命令集就够了。
5.3 kernel阶段的优化
kernel阶段的优化分两块:减少initcall数量和并行化initcall。减少initcall靠的就是前面说的裁剪,每关一个驱动就少一个initcall。并行化initcall靠CONFIG_INITCALL_PARALLEL,但这个选项在ARM上支持有限,收益不稳定,我实测下来提升不明显,不建议花太多精力。
另一个有效的手段是延迟加载非关键驱动。把一些不急着用的驱动从=y改成=m,让它们在init之后再加载,这样kernel启动阶段就少了这些驱动的probe时间。比如文件系统相关的驱动、网络驱动、一些传感器驱动,都可以改成模块,需要时再insmod。
还有压缩方式的选择。kernel镜像可以用gzip、xz、lz4等压缩。lz4解压最快但压缩率低,xz压缩率高但解压慢。RV1106的CPU是Cortex-A7,解压性能有限,我实测lz4的解压速度比gzip快不少,虽然镜像大一点,但启动时间反而更短。这个要实测,不同板子结果可能不同。
5.4 init和rootfs阶段的优化
init阶段是启动优化的另一个大头。如果你用busybox init,检查/etc/inittab和/etc/init.d/rcS,把不需要的服务全删掉。很多SDK默认会拉起一堆服务,比如telnetd、ftpd、各种守护进程,量产版本一律不要。每删一个服务,就少一次fork和exec,启动时间就少一点。
如果你用systemd,那优化空间更大但也更复杂。systemd本身启动就慢,RV1106这种小芯片上我一般不建议用systemd,busybox init足够。如果非要用,把不需要的target和service全disable掉,用systemctl mask彻底屏蔽。
rootfs阶段还有一个技巧是预链接和预加载。把常用的库预链接,减少运行时符号解析时间;把应用需要的库用LD_PRELOAD预加载,减少首次加载的延迟。但这些收益相对小,优先级排在裁剪之后。
5.5 应用启动的优化
应用本身的启动时间也占一部分。如果你的应用是C/C++写的,检查有没有在启动时做耗时操作,比如加载大配置文件、初始化大内存池、扫描目录等。这些能延后的就延后,能异步的就异步。如果是脚本语言写的,考虑换成编译型语言,或者把脚本预编译成字节码。
还有一个实用技巧是把应用和依赖库放到tmpfs里。squashfs是只读的,读取虽然不慢,但tmpfs在内存里,读取更快。启动时把应用目录拷到tmpfs再执行,能省一点时间。但要注意内存占用,RV1106内存紧张,别把内存撑爆了。
6. 常见问题与排查技巧实录
6.1 编译报错速查
RV1106编译报错,按频率排,最常见的是这几类。第一类是缺依赖,报错信息里通常有No such file or directory或者command not found,装对应的包就行。第二类是工具链不匹配,报错里有unrecognized command line option或者cannot find -lxxx,检查CROSS_COMPILE和工具链路径。第三类是kernel配置冲突,报错里有undefined reference或者conflicting types,通常是裁剪时关错了依赖项,把相关的项一起关或者一起开。
第四类是内存不足,编译进程被kill,报错里有Killed或者virtual memory exhausted,减小-j的值。第五类是权限问题,报错里有Permission denied,检查文件权限或者用sudo。第六类是磁盘满,报错里有No space left on device,清理编译产物或者换大磁盘。
| 报错关键词 | 可能原因 | 解决方法 |
|---|---|---|
| No such file or directory | 缺依赖包 | 安装对应dev包 |
| unrecognized command line option | 工具链不匹配 | 检查CROSS_COMPILE |
| undefined reference | 配置冲突 | 检查kernel配置依赖 |
| Killed | 内存不足 | 减小-j值 |
| Permission denied | 权限问题 | 检查文件权限 |
| No space left on device | 磁盘满 | 清理或换盘 |
6.2 启动卡死排查
启动卡死是RV1106上最常见的问题,排查思路是看串口日志卡在哪一行。如果卡在uboot阶段,通常是DDR参数不对或者存储介质识别失败。如果卡在kernel解压,可能是镜像损坏或者压缩方式不匹配。如果卡在kernel initcall,看最后一行日志是哪个驱动,大概率是那个驱动的问题,把它关掉或者改成模块。
如果卡在init阶段,通常是rootfs有问题,比如init程序找不到、库缺失、挂载失败。这时候可以用init=/bin/sh启动到shell,手动检查。如果连shell都进不去,可能是rootfs镜像本身有问题,重新打包。
还有一个隐蔽的坑是分区表不对。如果分区起始地址和镜像大小不匹配,kernel可能加载到错误的位置,表现为启动到一半就重启或者花屏。这时候用烧录工具读回分区表,和配置文件对比,确认一致。
6.3 镜像烧录失败
烧录失败常见原因有几个。第一是板子没进对模式,RV1106要进maskrom或者loader模式,按键时机不对就识别不到。第二是USB线或者口的问题,换线换口试试。第三是烧录工具版本不对,用瑞芯微官方最新版的烧录工具。第四是镜像本身有问题,重新打包一次。第五是SPI NAND坏块,这个比较麻烦,可能需要换板子。
烧录时如果进度条卡住不动,先别急着拔线,等几分钟看看。SPI NAND烧录本来就慢,尤其是大镜像。如果超过十分钟还不动,再考虑重来。
6.4 启动时间反复优化不下来的情况
有时候你裁了又裁,启动时间就是下不来。这时候要怀疑几个点。第一是DDR频率没跑满,用瑞芯微的工具读一下DDR实际频率,如果低于预期,检查DDR配置。第二是CPU频率没跑满,kernel里检查cpufreq配置,确保启动时CPU跑在高频。第三是存储读取速度慢,SPI NAND的读取速度本来就有限,如果镜像没压缩或者压缩率低,读取时间就长。第四是串口日志本身耗时,把kernel的loglevel调低,减少打印。
还有一个容易被忽略的点是电源管理。如果板子的电源设计有问题,上电后电压爬升慢,也会拖慢启动。这个要用示波器看,一般项目碰不到,但如果是自己画的板子,值得查一下。
6.5 独家避坑经验
分享几个我在RV1106上踩过的坑。第一个是不要用太新的gcc,我试过用gcc 11编kernel,结果一堆warning和error,换回SDK自带的gcc 8就没事。第二个是squashfs的块大小,默认可能是128KB,改成64KB或者256KB对读取速度有影响,要实测。第三个是uboot的bootdelay,默认可能是1秒或者2秒,量产版本改成0,省这一两秒很可观。第四个是kernel的console,如果不需要串口控制台,把console=参数去掉,能省一点初始化时间。
第五个是rootfs里的/etc/init.d顺序,服务启动顺序不对可能导致某个服务等另一个服务,白白浪费时间。检查依赖关系,能并行的并行。第六个是应用别用动态链接,如果应用不大,静态链接能省去加载动态库的时间,虽然体积大一点,但启动快。第七个是别在启动时做OTA检查,这个操作涉及网络,会阻塞启动,放到后台异步做。
7. 优化收益的量化与取舍
7.1 各阶段优化的实际收益
我把实际项目里的优化收益列一下,给你个参考。kernel裁剪(关掉DEBUG_INFO和无关驱动)通常能省30%到50%的编译时间,镜像体积减20%到40%,启动时间减0.5到1秒。rootfs裁剪(buildroot关包)能省20%到30%的编译时间,镜像体积减30%到50%,启动时间减0.3到0.8秒。uboot优化(关日志、精简命令)能省0.3到0.5秒启动时间。init优化(删服务)能省0.2到0.5秒。应用优化(异步化、静态链接)能省0.2到0.5秒。
这些加起来,从默认配置的5到6秒启动,压到2秒以内是可行的。但要注意,收益是递减的,越往后越难抠。前期的裁剪收益最大,后期的微调收益小但耗时多。所以要根据项目的时间预算决定优化到什么程度。
7.2 什么时候该停止优化
优化不是无止境的。我的经验是,当启动时间已经满足产品需求,就停止。比如产品要求3秒内出图,你压到2.5秒就够了,没必要为了再省0.2秒花一周时间。另外要考虑维护成本,过度裁剪的配置可能在新版本SDK上跑不起来,每次升级都要重新适配,这个成本要算进去。
还有一个判断标准是优化是否影响功能稳定性。如果某个优化导致偶发启动失败或者功能异常,那这个优化就不值得。稳定性永远优先于启动时间,尤其是量产产品。
7.3 不同场景的优化侧重
不同产品对启动优化的侧重不一样。电池类设备更看重低功耗,那么启动优化要配合电源管理,比如启动时不要拉高不必要的模块。视觉终端更看重出图速度,那么优化重点在sensor驱动和图像pipeline的初始化。带网络功能的产品更看重网络就绪时间,那么WiFi或者以太网的初始化要优先。扫码设备更看重扫码响应,那么应用层的优化比系统层更重要。
所以别照搬别人的优化清单,先想清楚你的产品最在意什么,把精力放在那个环节上。
8. 一些实操中的个人体会
RV1106这个平台,我最大的体会是**“少即是多”。你关掉的东西越多,系统跑得越快、越稳。很多同学舍不得关,觉得万一以后要用呢,结果就是系统臃肿、启动慢、还容易出问题。我的做法是从最小配置开始往上加**,而不是从默认配置往下减。先编一个只有串口和存储驱动的最小系统,能启动到shell,然后根据需求一个个加驱动和服务,每加一个测一次。这样你对系统里有什么、每个东西干什么,心里一清二楚,出了问题也好定位。
另一个体会是编译环境要固定。SDK版本、工具链版本、主机系统版本,一旦确定就不要轻易动。我见过团队里有人升级了主机系统,结果编译报错,查了半天是glibc版本变了。所以建议用Docker把编译环境固化下来,或者至少记录清楚版本号,换机器时照着装。
最后说个小的:串口日志是你的好朋友。RV1106没有屏幕,出问题全靠串口。把串口日志完整保存下来,出问题时对比正常和异常的日志,差异点往往就是问题所在。我习惯每次启动都把日志存一份,时间长了就是一个日志库,排查问题时翻历史日志特别有用。
这个平台后续还可以往安全启动和OTA升级方向扩展,这两个话题和系统构建也强相关,尤其是分区设计和镜像签名,和前面讲的打包环节是连着的。如果后面有机会再单独聊。