1. RK3588设备在边缘现场"死机"的三种典型场景
做边缘AI部署做到第三个年头,我越来越确信一件事:真正让RK3588这类设备在7×24运行中倒下的,往往不是某个不可描述的软硬件Bug,而是三个看起来不起眼的工程问题。这三个问题如果没人管,设备就会在某个凌晨悄无声息地掉线,而现场的人只能坐两个小时车去手动断电重启。
先看第一个,也是最容易被低估的一个——热崩溃。RK3588是一颗8核SoC,4个Cortex-A76大核加4个Cortex-A55小核,还集成了一颗6 TOPS算力的NPU。CPU和NPU同时满载的时候,整板功耗可以拉到15W甚至更高。边缘设备的外壳多半是金属密封或者半密封的,装在室外配电柜、路侧机箱、车顶盒子里,夏天的环境温度能到45℃以上。散热设计稍微不到位,SoC结温就会顶到85℃、90℃,这时候内核的 thermal 框架开始限频,CPU从2.4GHz一路降到1.2GHz。降频之后推理帧率断崖式下跌,业务侧任务开始堆积,负载反而更高,温度继续往上顶,最终触发硬件热保护直接断电。整个过程像多米诺骨牌,第一步往往是风扇策略不给力,最后一步则是完全掉电。
第二个场景是资源渐进式泄漏。做过RK3588上YOLOv8部署的同学应该有体会:一个C++推理服务,如果图像缓冲、tensor、epoll句柄这些资源有任何一环没有释放干净,跑三到五天内存就会悄悄涨出一两百MB。视频类业务更明显,RTSP拉流、硬解码、推理、结果上报,这个链路如果没做超时保护,网络一抖动就会卡在某个阻塞调用里,然后句柄越积越多。再有就是日志。系统日志和应用日志如果不控制体积,/var/log满了之后,很多服务会因为写不了日志而直接罢工,表现就是"设备假死"。这类问题因为不是瞬间发生的,所以最恶心,往往在交付验收完之后的第三周才冒出来。
第三个场景是单点故障无法自愈。应用进程崩了没人拉起,业务就断了;内核panic了但系统没有配置自动重启,机器就一直卡死在串口输出画面;某个线程死锁导致整个系统响应不了,但没有外部看门狗把系统打断。说白了,很多设备的"死机"本质上只是"坏了没人管"。我在项目交付中见过太多现场,设备Console口插上,屏幕上一堆调用栈,系统是彻底凉了的,但没有任何机制能让它自己爬起来。
这三个场景摆在一起,结论其实很清晰:想让RK3588边缘AI设备做到7×24不死机,不能只靠某个单一的看门狗或者某个优秀的电控设计,而是需要一套从底层硬件到业务进程的完整守护机制。我习惯把这套机制称作 Guardian——它不是一个开源软件,也不是某个商业产品的名字,而是我在多个RK3588边缘智能项目里沉淀下来的一套工程方法论,包含散热闭环、内核参数、看门狗联动、进程守护、存储保护等一整套能落地、能复制、能验证的实践。
2. Guardian守护体系的整体设计:三层防线怎么分工
Guardian这套体系的思路,可以用一句工程上行话概括:不要假设系统不会挂,要设计系统挂了之后能自己爬起来。业界做高可用设备,原则是相同的:快速失败、自动恢复。边缘AI设备和数据中心服务器最大的区别在于,它没有人值守,断了网也没有运维人员能第一时间介入,所以一切恢复动作都必须是自动的、本地的、不依赖外部网络的。
按照这个原则,我把守护体系分成三层。
第一层是硬件级防线。内核panic、软锁死、硬锁死、任务卡死这些情况,操作系统自身已经失去了正常恢复的能力,必须依靠看门狗在毫秒到秒级的时间内把整个SoC复位掉。RK3588内部有WDT外设,板级dts里一般默认使能,Linux内核的watchdog驱动也会把它注册成/dev/watchdog节点。这一层要做的核心事情,是配置好内核"出问题就panic、panic之后就重启"的策略,并在用户空间安排一个喂狗进程,让看门狗既能监视系统是否卡死,又不会误触发复位。
第二层是系统软件层。它负责处理那些硬件看门狗管不了的问题——比如内存泄漏、文件系统写满、日志无限增长、某些服务假死。这一层的工具主要是systemd、cgroup、logrotate、overlayfs,以及一组精心调过的内核sysctl参数。打个比方,硬件看门狗是"整机心脏骤停时的电击器",系统软件层的职责则更像日常体检,在病情恶化之前就把它拦下来。
第三层是业务应用层。AI推理进程、RTSP拉流进程、外设通信进程,这些才是设备存在的意义。这一层要解决的是"某个业务进程挂了但系统其他部分还活着"的情况,核心手段是健康检查加自动重启。进程不是拉起一次就完了,而是要形成一个闭环:定期检查心跳,心跳异常就杀掉重启,重启次数过多就告警并降级运行。
这三层防线缺一不可。只做硬件看门狗,设备的业务逻辑烂成一锅粥也没人管;只做进程守护,内核一旦panic就只能等人工到场;只做监控和参数调优而不做硬件兜底,整个系统还是裸奔。我自己踩过不少坑之后才总结出这个三层架构,后面几章我会把每一层的具体做法和参数细节都讲清楚,照着做,同类设备至少不会再因为"基础功课没做"而掉线。
这里还要特别强调一个设计原则:守护进程本身也必须被守护。温控脚本、喂狗进程、健康检查服务,如果它们自己崩了,就失去意义了。所以这些脚本和进程都应该注册成systemd服务,并纳入WatchdogSec的监督范围。这一条看着简单,实操中很多人会忽略,结果就是守护机制成了纸糊的盾牌,平时看着在跑,关键时刻它先躺下了。
3. 散热闭环控制:温度、风扇转速与PWM调参的完整链路
散热是RK3588边缘设备7×24稳定性的命门,尤其那些主动风冷方案的板卡。我见过太多设备死机是因为风扇策略写得太粗糙——要么温度到了80℃才开始转,要么风扇永远低速转,导致积灰卡死。这一章我把散热闭环需要做的事拆开讲,每一步都有实测依据。
3.1 找到正确的温度数据源
做温控的第一步,是找到CPU/SoC温度到底在哪个sysfs节点。Rockchip平台在标准内核thermal框架下会注册多个thermal zone,每个zone有type和temp两个重要属性。我用的排查命令是:
for z in /sys/class/thermal/thermal_zone*/; do echo -n "$z type="; cat "$z/type" echo -n " temp="; cat "$z/temp" done实测RK3588的Debian 11系统上,soc_thermal通常对应thermal_zone0,读取出来的temp单位是毫摄氏度,比如85000代表85℃。某些板卡会在hwmon节点上也暴露温度,比如/sys/class/hwmon/hwmon1/temp1_input,两者数值应该一致,但以thermal zone为准最靠谱,因为内核调频调压用的就是thermal框架的数据。
需要留意的是,RK3588内部不止一颗温度传感器,CPU、GPU、NPU各有各的传感器节点。如果只盯着CPU温度做风扇控制,NPU跑推理时温度可能已经爆了而风扇还在低速转。稳妥做法是读完所有zone,取最大值作为温控依据。我一般写一个get_temp()函数,遍历所有zone取最大值,超时重试三次,保证读取链路本身可靠。
3.2 风扇转速读取:PWM Capture与FG脉冲换算
"rk3588 读取风扇转速"这个诉求,很多开发板玩家都在问。风扇转速信号来自风扇内部的FG引脚(Frequence Generator),它每转一圈会输出若干个脉冲。RK3588本身没有专用的RPM计数器,但它的PWM外设支持capture模式,正好可以测量FG引脚上的脉冲频率,再换算成转速,这就是很多人提到的"rk3588 pwm capture"用法。
先看板级dts里PWM capture通道怎么配:
pwm_capture: pwm-capture { compatible = "rockchip,rk3588-pwm-capture"; pwm-names = "fan-tach"; pwms = <&pwm2 0 1000000 0>; status = "okay"; };实际引脚和通道以板卡原理图为准,不同开发板差别很大。配置好后,用户在/sys/class/pwm/pwmchipN/下面export对应通道,读取capture文件:
echo 0 > /sys/class/pwm/pwmchip2/export cat /sys/class/pwm/pwmchip2/pwm0/capture输出类似period=8333333 duty_cycle=4166666,period是脉冲周期(纳秒)。频率等于1秒除以周期,然后乘上换算系数得到转速。常规三线PWM风扇FG信号每转输出2个脉冲,所以:
RPM = (1 / period_seconds) × 60 / 2举个例子,如果读到period=8333333ns,也就是频率120Hz,RPM = 120 × 60 / 2 = 3600。不过不同厂家风扇的FG脉冲数可能不一样,有的是每转4脉冲,我建议拿到风扇先实测标定一次——把风扇额定转速和频率对应起来,确定好系数再写进代码。
3.3 温控策略与PWM曲线设计
PWM风扇调速有两种常见实现方式。一种是走内核thermal框架,在dts里配pwm-fan节点和cooling-map,让内核根据温度自动调风扇:
fan: pwm-fan { compatible = "pwm-fan"; #cooling-cells = <2>; pwms = <&pwm1 0 40000 0>; cooling-levels = <0 64 128 192 255>; status = "okay"; };这种方式省心,内核自动处理thermal throttling和风扇联动的逻辑。但缺点是调参不直观,而且不好加"风扇卡死检测"的逻辑。我实际项目里更多是用用户空间的守护脚本来直接控制PWM:
#!/bin/bash # guardian-fan.sh - 温度采集 + 风扇转速读取 + PWM 控制 FAN_PWM="/sys/class/pwm/pwmchip1/pwm0" TEMP_ZONES="/sys/class/thermal" CAPTURE="/sys/class/pwm/pwmchip2/pwm0" get_temp() { local max=0 for z in "$TEMP_ZONES"/thermal_zone*/; do local t=$(cat "$z/temp" 2>/dev/null || echo 0) [ "$t" -gt "$max" ] && max=$t done echo "$max" } get_rpm() { local cap=$(cat "$CAPTURE/capture" 2>/dev/null) local period=$(echo "$cap" | sed -n 's/period=\([0-9]*\).*/\1/p') [ -z "$period" ] && { echo 0; return; } echo $(( 1000000000 / period * 60 / 2 / 1000 )) } while true; do temp=$(get_temp) # 毫摄氏度 temp_deg=$((temp / 1000)) rpm=$(get_rpm) duty=0 if [ "$temp_deg" -ge 70 ]; then duty=255 elif [ "$temp_deg" -ge 45 ]; then # 45~70度线性映射 duty=$(( (temp_deg - 45) * 255 / 25 )) fi echo "$duty" > "$FAN_PWM/duty_cycle" # 日志记录,方便事后分析 logger -t guardian "temp=${temp_deg}C rpm=${rpm} duty=${duty}" sleep 2 done我推荐的分段线性温控策略:45℃以下风扇低速(或停转),45到70℃按线性比例加速,70℃以上直接全速。这个策略比"温度高了就全速、温度低了就停转"的死板模式更平顺,能避免风扇频繁启停带来的噪声和磨损。如果对噪声敏感,可以把风扇启动阈值调到50℃,但上限一定要留够余量——我一般在75℃就强制全速,不给热量累积的机会。
3.4 风扇异常降级保护
温控逻辑跑通之后,最容易被忽略的是风扇故障检测。风扇属于机电产品,有寿命,会卡灰,会缺油,会断线。不要等到SoC热保护关机了才反应过来。在上面的脚本基础上,我加了一段异常逻辑:当PWM占空比已经超过一定阈值,但读取到的转速低于200RPM,且持续30秒,就判定风扇故障,强制PWM全速输出,同时把故障状态写到监控数据库并触发本地告警。
转速为0还有一种可能,就是capture通道本身配置错了。所以我在项目里特意做了"PWM开度与转速联动检查":风扇开度从0调到255,如果在设定时间内转速没有按预期上升,就认为测速链路有问题,同样要告警。这个检查在设备刚启动的时候跑一次就够了,不用总是做。
风扇异常时最怕的是业务还在继续跑,热量还在继续累积。所以降级方案不只是全速转风扇,还要考虑主动降载:检测到风扇故障后,询问业务管理系统是否可以降低AI推理频率或关闭一路视频分析,优先保系统和最核心的业务。这个策略因项目而异,但思路是通用的——散热异常时,先限制发热源,再等待运维介入。
4. 系统级"不死"保障:内核参数、硬件看门狗与systemd联动
散热保证的是硬件不死,系统级保障要解决的是操作系统本身"假死"和"真死"的问题。很多RK3588设备跑着跑着,SSH连不上,串口也没反应,但电源灯还亮着,这种状态最让人头疼。Guardian在这层的目标是:能恢复的自动恢复,不能恢复的快速复位,尽量缩短设备不可用的时间窗口。
4.1 内核关键参数与触发条件
在Debian 11或Ubuntu系统上,我建议在/etc/sysctl.d/99-guardian.conf里写入以下参数:
kernel.panic = 5 kernel.panic_on_oops = 1 kernel.softlockup_panic = 1 kernel.hardlockup_panic = 1 kernel.hung_task_timeout_secs = 60 kernel.hung_task_panic = 1 kernel.nmi_watchdog = 1 fs.file-max = 65536 fs.inotify.max_user_watches = 262144逐个说下我为什么这么设。
kernel.panic=5是内核panic后延迟5秒自动重启。延迟是为了让panic信息有时间刷到console,方便排查。如果设为0,系统卡在panic界面,等人工到场就失去了自愈能力。
kernel.panic_on_oops=1让内核在处理Oops(非致命的内核错误)时升级为panic,从而触发重启。为什么要这么做?因为Oops说明内核已经处于不可靠状态,继续跑下去可能产生更严重的损坏,不如直接重启。
kernel.softlockup_panic=1和kernel.hardlockup_panic=1让CPU软锁死和硬锁死也能触发panic。软锁死通常是中断上下文里死循环,硬锁死可能和硬件异常有关。这两个参数在内核里默认可能是0,不加的话,CPU锁死就永远锁死了。
kernel.hung_task_timeout_secs=60和kernel.hung_task_panic=1用于检测用户空间任务长时间处于D状态(不可中断睡眠)。D状态任务卡住直接影响业务,同时很可能是IO子系统出了问题的信号。设成60秒,超过就panic重启。这个时间不能太短,否则在高负载IO场景会误触发;我试过30秒在日志量大的设备上出现过误报,调到60秒就稳定了。
这些参数在边缘AI设备的套路基本是固定的。大家在做rk3588系统定制的时候,拿这套配置基本不会出大问题。
4.2 硬件看门狗的接入与喂狗策略
RK3588的dts里如果有watchdog节点,Linux内核会注册出/dev/watchdog。我一般先看一眼节点是否存在:
ls -l /dev/watchdog* cat /dev/watchdog 2>&1 | head -1喂狗的方式很简单,往设备写任意数据就会触发一次喂狗操作。系统里常见的喂狗工具是wd_keepalive,也可以自己写个循环脚本:
#!/bin/bash while true; do echo -n "K" > /dev/watchdog sleep 5 done关键在喂狗间隔和WDT超时时间的匹配。如果看门狗超时是30秒,喂狗周期建议控制在5到10秒,也就是超时时间的六分之一到三分之一。喂太频繁,系统卡死几秒就喂不上狗,看门狗也能容忍,但一卡就超过30秒才能复位,业务中断时间太长。喂太慢,稍微调度抖动就会误复位。我在实际项目中习惯设WDT超时30秒,喂狗周期5秒,留足余量。
如果板子上没有内置WDT,或者对可靠性格外敏感,可以考虑外接独立看门狗芯片(比如STWD100),通过GPIO做喂狗。外置看门狗的好处是,即使SoC内部时钟或总线出了问题,它依然能可靠复位。普通项目用RK3588内置WDT就够,但如果设备部署在无人区,我建议上外置方案,多出来的成本比起一次人工出差的差旅费,划算得多。
4.3 systemd WatchdogSec:业务级心跳与硬件复位联动
系统启动后,systemd作为第一个用户空间进程,它的健康状态也应该被监督。在/etc/systemd/system.conf里有一个关键参数:
RuntimeWatchdogSec=30设置这个参数后,systemd主进程会周期性地喂养内核的/dev/watchdog。这样即使systemd它自己卡死了,硬件看门狗也能在30秒内复位整个系统。配合前面的kernel.panic=5,一条完整的复位链就通了:业务进程卡死 → systemd杀掉重启;系统内核卡死 → 内核panic → 自动重启;systemd卡死 → 硬件看门狗复位。
在具体服务单元里,还可以给关键业务进程单独配WatchdogSec:
[Service] ExecStart=/usr/bin/guardian-ai --config /etc/guardian/ai.conf Restart=always RestartSec=3 WatchdogSec=30 MemoryMax=2G MemoryHigh=1500M TasksMax=256配合WatchdogSec,应用进程需要定期调用sd_notify(0, "WATCHDOG=1")通知systemd自己还活着。C代码里是这样的:
#include <systemd/sd-daemon.h> while (1) { /* 业务逻辑 */ run_inference(); sd_notify(0, "WATCHDOG=1"); sleep(5); }一个容易犯的错误是把喂狗放在独立线程里,这样主业务线程卡死时,systemd仍然能收到心跳,看门狗根本发现不了问题。我要求喂狗必须和业务心跳绑在同一个执行路径上——每次跑完核心推理循环就喂一次,这样systemd心跳实际上代表的是业务侧的健康状态,而不是某个空转线程的状态。
4.4 应急恢复兜底:recovery和双系统方案
软件再强,也总有把自己搞坏的一天,比如误刷了错误的固件。所以Guardian的最底层兜底是硬件恢复通路。瑞芯微平台的recovery机制大家应该都熟:按住recovery/maskrom键,用USB Type-C数据线连到电脑,上电,然后工具就能识别到设备进入升级模式。这套机制建议在项目交付文档里写清楚,现场维护的人必须掌握。
比recovery更进一步的是A/B双系统分区。系统A启动失败时,引导程序自动切到系统B;系统B再失败,就从recovery引导。这个方案需要存储空间翻倍,但对7×24设备来说值得考虑。我做一个户外采集项目时跑过这套方案,升级固件再也不用担心写一半断电变砖,稳定性提升非常明显。
5. AI推理场景的进程守护:OOM隔离、心跳上报与自动重启
前两章保证了系统和硬件层面不会死透,但7×24场景里最频繁出问题的,其实是AI推理和视频流这些业务进程。RK3588上部署YOLOv8这类模型,推理进程一般是用C++写的常驻服务,一次模型加载就要几秒钟,模型文件转成RKNN格式后跑在NPU上。这类服务一旦挂掉,设备看起来还开着机,但业务已经完全停了。
5.1 用cgroup做内存隔离
AI推理服务是内存消耗大户,模型权重、中间张量、图像缓冲叠在一起,轻松吃掉1~2GB内存。如果系统内存被某个业务进程吃满,内核OOM killer可能把SSH服务或者其他核心进程杀掉,设备直接失联。解决办法是用systemd的MemoryMax参数给每个服务设置内存上限。
[Service] MemoryMax=2G MemoryHigh=1500M OOMScoreAdjust=500MemoryHigh是软限制,超过之后内核会回收这个进程的可回收内存;MemoryMax是硬限制,一旦超过,内核就对这个cgroup触发OOM。配合OOMScoreAdjust=500,让OOM killer优先杀这个业务进程,而不是杀掉systemd或sshd。这样内存异常时,牺牲的只是一个可以自动重启的推理进程,整个设备仍然可访问、可运维。
要验证上限是否生效,可以手动往进程里注入一段不断申请内存的测试代码,观察dmesg里OOM kill的记录。这一步必须做,不然上线后内存泄漏爆发时,系统可能直接卡死。
5.2 AI推理进程的心跳与自动重启
进程守护需要"业务级心跳",不是简单的进程存活。比如推理服务进程还活着,但内部某个线程死锁了,外表看着一切正常,实际已经停止处理新任务。我的做法是让服务暴露一个本地HTTP健康检查接口,比如/healthz,返回最近一次推理处理的帧数和耗时。守护脚本定期请求这个接口,同时验证两个条件:一是有响应,二是最近一次推理耗时没有超过正常值的3倍。
#!/bin/bash # guardian-health-check.sh curl --max-time 5 -s http://127.0.0.1:8090/healthz || { logger -t guardian "health check failed, restarting service" systemctl restart guardian-ai }把这个健康检查脚本放到systemd timer里每30秒跑一次,或者直接用systemd服务自带的WatchdogSec机制来实现,效果类似。关键点在于:心跳要和业务卡死联动,而不是空转。比如推理服务内部单独开了个线程每5秒调用sd_notify,主线程却堵在某个图像处理函数里出不来,这种心跳就没有意义。我一般把心跳调用放在主推理循环的每个帧处理之后,而不是放在独立线程里,这样只要心跳还在跳动,就说明核心业务还在跑。
5.3 视频流与模型加载的稳定性细节
RK3588边缘AI设备大量用于视频监控场景,RTSP拉流 + 硬解码 + 推理 + 结果上报是标准链路。这个链路里最容易被忽视的是网络抖动。RTSP拉流线程如果阻塞在socket读取上,一旦上游摄像头断流,这个线程可能一直卡着不返回。处理办法是给所有socket调用加超时,同时在守护脚本里加"新帧看门狗":实时视频流如果超过N秒没有新帧到达,判定拉流异常,重启整个视频处理进程。
模型加载方面,RKNN推理服务启动时要把模型加载到NPU,这个过程在冷启动时可能需要几秒钟到十几秒。systemd服务默认的启动超时是90秒,正常情况下足够用。但要注意一个问题:如果系统刚启动时NPU驱动还没初始化完成,推理服务启动就会失败。我在服务单元里加了After=rockchip-npu.service和Restart=always,保证NPU就绪后再启动推理服务,启动失败也能自动重试。
内存泄漏是AI服务的另一大隐患。我建议对每个推理服务做一次72小时的压测,每隔1小时记录一次RSS内存,如果RSS单调上涨超过10%,基本可以断定有泄漏,上线前就要修。实在赶到项目交付节点来不及修,也可以用systemd的MemoryMax做一个"定时重启"的折中方案——比如每天凌晨业务空闲时自动重启一次推理服务,把所有泄漏的内存吐回去。这个方案治标不治本,但确实能让设备先跑起来。
6. 存储与日志的可靠性:eMMC寿命、只读根文件系统与日志轮转
边缘AI设备绝大多数用eMMC做存储。eMMC虽然比SD卡可靠得多,但也不是无限写寿命的。而且系统日志、AI推理日志、视频片段如果随意落盘,要么把存储写穿,要么把分区写满,两者都会导致设备无法正常工作。这一章讲存储和日志的守护手段。
6.1 根文件系统只读化
把根文件系统设成只读,是嵌入式设备提高可靠性的经典做法。好处显而易见:意外断电不会破坏系统盘,日志写满也不会影响系统目录,恶意或异常写入更不会污染二进制文件。
在Rockchip平台的Debian/Ubuntu上,通常做法是用overlayfs把根文件系统合并成"只读底层+内存上层"。具体实现可以用Linux内核boot参数配合initramfs脚本,或者安装overlayroot后配置:
overlayroot="tmpfs"配置之后重启,根文件系统就是tmpfs叠加在只读的eMMC分区上了,系统运行时对/目录的写入都落在内存,重启后自动恢复到出厂状态。业务数据和日志则写入单独的可写分区,比如/data和/var/log。这样既保留了系统的灵活性,又把系统盘写损坏的概率降到了最低。
需要注意,只读根文件系统会让某些需要写/var的包安装失败,需要提前规划好哪些路径走tmpfs、哪些路径走独立可写分区。我通常的做法是:/etc里需要动态修改的文件用bind mount映射到/data分区,/var/log直接挂到/data/log,/tmp和/run本来就是tmpfs不用管。
6.2 日志轮转与落盘策略
日志写满分区是边缘设备最常见的一种"慢死"。AI推理服务如果每个检测帧都打一条日志,一天能写好几个GB,eMMC很快扛不住。我的标准策略是:应用日志统一写到/data/logs,配合logrotate做轮转和压缩。
以/etc/logrotate.d/guardian-ai为例:
/data/logs/ai_service.log { daily rotate 7 compress delaycompress maxsize 50M missingok notifempty copytruncate }copytruncate这个选项很重要,它确保日志文件还在被进程打开写入时也能安全轮转。系统日志方面,journald默认会把所有日志写到/var/log/journal,如果根文件系统只读化了,就要把日志转发到内存或者只保留systemd内存日志,必要时再手动导出。我一般设置:
SystemMaxUse=200M MaxRetentionSec=3dayjournald自己控制体积,日志太多时自动丢弃老日志,不会把磁盘撑爆。
6.3 eMMC磨损保护
除了控制写入量,eMMC本身也要做一些保养。最关键的是定期执行TRIM,让eMMC内部GC有合理的时机整理废弃块:
fstrim -v /data可以放到cron或systemd timer里每周执行一次。另外,尽量避免在业务代码里做高频率小文件写入,比如每秒钟写一个状态文件。日志先写入内存缓冲,攒够一定量再批量落盘,这样既能减少写放大,又能避免每次断电都丢关键日志。
如果对存储可靠性要求更高,可以启用eMMC的硬件写保护(通过MMC工具设置永久写保护区域),或者干脆把系统盘做成只读eMMC,业务数据全部走外部USB SSD或者网络存储。不同方案的取舍取决于项目预算和数据重要性,但思路都是一样的:保护存储就是保护系统本身的生存能力。
7. 一次完整排查实录:散热失控导致的风扇卡滞与系统关机
前面几章讲的是理论和方法,这一章我拿一个实际案例把整个排查链路走一遍。这个案例来自一个室外配电柜里的RK3588边缘AI盒子,跑的正是YOLOv8人员识别加RTSP视频流,设备已经稳定运行了两周,突然在一个高温午后自动关机。
7.1 现象与最初的怀疑方向
用户反馈设备掉线,Console口接上没有输出,ping不通,电源指示灯不亮。第一反应是断电了?后来确认供电正常。怀疑软件死锁,但设备是完全断电状态,不是假死。把设备取回实验室,上电串口看日志,发现最后几行是thermal zone的临界温度告警,然后就是硬件热保护断电。所以问题不是软件,是设备烧过了。
我把设备装上温度探头重新跑压测,同时让日志系统记录温度、风扇PWM、转速三个数据。到这里,第一个教训就出现了:如果没有提前做监控和日志,这种故障排查会像无头苍蝇一样毫无头绪。所以我在所有RK3588项目里都强制要求记录温度轨迹和风扇转速,就是为了这种时候能回放现场。
7.2 逐层排查风扇转速异常的过程
模拟现场高负载跑了大概两天,数据库里的记录显示:关机前几分钟,温度从75℃一路涨到95℃,而PWM duty已经是255(全速)。按理说全速运转下温度不应该升这么快,唯一的解释是风扇没有在转。
我调出转速记录,果然,PWM全速之前的几十分钟,转速已经从3000RPM快速下降到几百RPM,最后稳定在0附近。风扇故障几乎可以确定了。但为什么会突然转速下降?打开盒子检查,发现风扇叶片上积了一层灰,轴承发涩,用手拨动叶片有明显阻力。长期运行加灰尘卡滞,导致风扇轴心阻力变大,最终停转。
为了确认测速链路没问题而不是误报,我用示波器直接测风扇FG引脚,叶片停转后确实没有任何脉冲输出。到这里问题定位完成:不是PWM capture配置错,不是内核读转速读错,就是纯粹的风扇机电故障。
这个案例里的温控策略也有问题:为了静音,风扇在50℃以下长期低速运行,低速工况下灰尘更容易附着在叶片和轴承上,日积月累就卡死了。这是产品设计层面的隐患,不只是风扇质量问题。
7.3 根因确认与修复方案
最后我给这个项目做了三处改动,也推荐给所有类似场景的RK3588设备。
第一,温控策略从"低速常转"改成"按需调速",每周还加了一次30秒的全速自清洁周期,利用离心力把叶片上的灰尘甩掉一部分。第二,增加风扇故障检测逻辑,PWM开度超过一定阈值而转速异常偏低时,立即触发告警,并主动限制AI推理负载,降低发热。第三,也是最关键的,给设备加上了硬件看门狗和内核panic自动重启。这样即使故障真正再次发生导致重启,也只需要45秒左右就能恢复业务,而不是等人工到场。
这次排查给我最大的触动是:散热问题的根源往往不在"散热"本身,而在监测缺失。如果一开始就盯着风扇转速和温度趋势,故障是可以提前发现的。后来我在每个项目的设备验收清单里都加上了一条:必须验证看门狗能复位系统,必须验证风扇在失速时能告警,必须验证日志能在故障后留痕。
做过这套Guardian体系之后,我对RK3588边缘AI设备的7×24稳定性有了新的理解。所谓"不死机",不是追求设备永远不坏,而是要让每次故障都能在最短时间内自愈,把设备的不可用时间压缩到分钟级以内。硬件坏了有看门狗兜底,业务挂了有systemd拉起,内存泄漏了有cgroup隔离,日志满了有轮转清理。把这些环节都打通,再回头看那些"死机"问题,绝大多数都只是守护体系里的一个待完善节点。但也不要过度迷信守护机制本身——守护本质上是在为系统的脆弱兜底,更好的方向永远是在设计阶段就把散热、电源、存储这些基础功课做扎实,让Guardian从"救命稻草"变成"保险措施"。