OpenWrt 故障处理案例
案例一:网关重启后 Modbus 采集中断
故障现象
生产环境中,OpenWrt 网关每次重启后约 3 分钟内 Modbus 采集程序无法连接到 RS485 设备,3 分钟后自动恢复。
排查过程
bash
# 1. 查看串口设备状态
ls -la /dev/ttyS1
# 设备存在,权限正常
# 2. 查看采集程序日志
logread | grep modbus
# [ERROR] Failed to connect: Resource temporarily unavailable
# 3. 检查串口是否被占用
fuser /dev/ttyS1
# 无输出,未被占用
# 4. 手动测试串口
echo -e '\x01\x03\x00\x00\x00\x01\x84\x0A' > /dev/ttyS1
# 无响应
# 5. 检查串口参数
stty -F /dev/ttyS1 -a
# 发现波特率为 115200,而设备要求 9600根本原因
procd 服务启动顺序问题:采集程序在 network 服务完全就绪前启动,导致串口初始化脚本(依赖网络时间同步)未执行完成。同时,串口参数在每次重启后被重置为默认值。
解决方案
bash
# 1. 创建串口初始化脚本
cat > /etc/init.d/serial-init <<'EOF'
#!/bin/sh /etc/rc.common
USE_PROCD=1
START=60 # 在 network(20) 之后,采集程序(95)之前
start_service() {
# 配置 RS485 串口参数
stty -F /dev/ttyS1 9600 cs8 -cstopb -parenb raw
# 设置低延迟模式
setserial /dev/ttyS1 low_latency
logger -t serial-init "RS485 port initialized"
}
EOF
chmod +x /etc/init.d/serial-init
/etc/init.d/serial-init enable
# 2. 修改采集程序服务,添加依赖
# /etc/init.d/modbus-collector
# 在 start_service 中添加:
procd_set_param respawn 30 5 0 # 失败后 30 秒重试案例二:MQTT 消息丢失,Broker 连接频繁断开
故障现象
网关上的 mosquitto broker 每隔 1-2 小时断开一次,重连后部分历史消息丢失。
排查过程
bash
# 查看 mosquitto 日志
logread | grep mosquitto | tail -50
# 1703123456: Socket error on client gateway-001, disconnecting.
# 1703123460: New connection from 192.168.1.50 on port 1883.
# 检查系统资源
free -m
# total: 128MB, used: 121MB, free: 7MB ← 内存严重不足!
# 查看内存占用最高的进程
top -b -n 1 | head -20
# mosquitto 占用 45MB,node-red 占用 62MB
# 检查 OOM 日志
dmesg | grep -i "out of memory"
# [12345.678] Out of memory: Kill process 1234 (mosquitto) score 892根本原因
设备内存仅 128MB,node-red 内存泄漏导致系统 OOM,内核强制杀死 mosquitto 进程。
解决方案
bash
# 1. 限制 node-red 内存使用
# /etc/init.d/node-red
procd_set_param command node --max-old-space-size=64 /usr/bin/node-red
# 2. 配置 mosquitto 持久化(防止消息丢失)
cat > /etc/mosquitto/mosquitto.conf <<'EOF'
persistence true
persistence_location /overlay/mosquitto/
persistence_file mosquitto.db
autosave_interval 60
# 限制内存使用
max_queued_messages 1000
max_inflight_messages 20
EOF
# 3. 添加内存监控,提前预警
cat > /usr/bin/mem-watchdog.sh <<'EOF'
#!/bin/sh
MEM_FREE=$(grep MemFree /proc/meminfo | awk '{print $2}')
if [ "$MEM_FREE" -lt 20480 ]; then # 低于 20MB 告警
logger -t mem-watchdog "WARNING: Low memory ${MEM_FREE}KB"
mosquitto_pub -h localhost -t "alert/gateway/memory" \
-m "{\"free_kb\":$MEM_FREE,\"level\":\"warning\"}"
fi
EOF
echo "*/5 * * * * /usr/bin/mem-watchdog.sh" >> /etc/crontabs/root案例三:4G 模块掉线后无法自动恢复
故障现象
使用 EC21 4G 模块的网关,在信号弱区域偶发掉线后,即使信号恢复也无法自动重连,需要手动重启。
排查过程
bash
# 检查 modem 状态
mmcli -m 0
# Status: failed
# Reason: sim-missing ← 误报,SIM 卡实际存在
# 检查 USB 设备
lsusb
# Bus 001 Device 003: ID 2c7c:0121 Quectel EC21
# 查看 ModemManager 日志
logread | grep ModemManager | tail -30
# ModemManager: <warn> couldn't load SIM: GDBus.Error: org.freedesktop.ModemManager1.Error.Core.Failed
# 检查 QMI 接口
ls /dev/cdc-wdm*
# /dev/cdc-wdm0 ← 存在
# 手动重置 modem
mmcli -m 0 --reset
# 重置后恢复正常根本原因
ModemManager 在检测到信号丢失后进入错误状态,未实现自动状态机恢复。
解决方案
bash
# 创建 4G 连接看门狗
cat > /usr/bin/4g-watchdog.sh <<'EOF'
#!/bin/sh
INTERFACE="wwan0"
CHECK_HOST="8.8.8.8"
MAX_FAIL=3
FAIL_COUNT_FILE="/tmp/4g_fail_count"
# 读取失败计数
FAIL_COUNT=$(cat "$FAIL_COUNT_FILE" 2>/dev/null || echo 0)
# 检查网络连通性
if ping -c 2 -W 5 -I "$INTERFACE" "$CHECK_HOST" > /dev/null 2>&1; then
echo 0 > "$FAIL_COUNT_FILE"
exit 0
fi
FAIL_COUNT=$((FAIL_COUNT + 1))
echo "$FAIL_COUNT" > "$FAIL_COUNT_FILE"
logger -t 4g-watchdog "Ping failed, count: $FAIL_COUNT"
if [ "$FAIL_COUNT" -ge "$MAX_FAIL" ]; then
logger -t 4g-watchdog "Resetting 4G modem..."
# 方法1:通过 ModemManager 重置
mmcli -m 0 --reset 2>/dev/null
sleep 10
# 方法2:如果 MM 重置失败,重启 USB 设备
if ! ping -c 1 -W 5 -I "$INTERFACE" "$CHECK_HOST" > /dev/null 2>&1; then
logger -t 4g-watchdog "MM reset failed, cycling USB power..."
# 通过 GPIO 控制 USB 电源(硬件相关)
echo 0 > /sys/class/gpio/gpio18/value
sleep 3
echo 1 > /sys/class/gpio/gpio18/value
sleep 15
fi
echo 0 > "$FAIL_COUNT_FILE"
fi
EOF
chmod +x /usr/bin/4g-watchdog.sh
echo "*/2 * * * * /usr/bin/4g-watchdog.sh" >> /etc/crontabs/root
/etc/init.d/cron restart案例四:升级固件后配置丢失
故障现象
执行 sysupgrade -c 升级后,自定义的 /etc/myapp.conf 和 /usr/bin/custom-script.sh 丢失。
原因分析
sysupgrade -c 只保留 /lib/upgrade/keep.d/ 中列出的文件路径,自定义文件需要手动添加。
解决方案
bash
# 方法1:添加到 sysupgrade 保留列表
cat > /lib/upgrade/keep.d/myapp <<'EOF'
/etc/myapp.conf
/etc/myapp/
/usr/bin/custom-script.sh
/root/.ssh/authorized_keys
/etc/crontabs/root
EOF
# 方法2:使用 sysupgrade.conf
cat >> /etc/sysupgrade.conf <<'EOF'
/etc/myapp.conf
/usr/bin/custom-script.sh
EOF
# 验证保留列表
sysupgrade --list-backup | grep myapp案例五:LuCI 无法访问,uhttpd 崩溃
故障现象
Web 管理界面突然无法访问,curl 返回 Connection refused。
排查过程
bash
# 检查 uhttpd 状态
/etc/init.d/uhttpd status
# inactive
# 查看错误日志
logread | grep uhttpd
# uhttpd: Failed to bind socket: Address already in use
# 查找占用 80 端口的进程
netstat -tlnp | grep :80
# tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 1234/python3
# 发现是自定义 Python HTTP 服务占用了 80 端口解决方案
bash
# 修改 uhttpd 端口或停止冲突服务
uci set uhttpd.main.listen_http='0.0.0.0:8080'
uci commit uhttpd
/etc/init.d/uhttpd restart
# 或者修改 Python 服务端口
# 修改对应服务配置文件,使用 8000 等非冲突端口常用诊断命令速查
bash
# 系统状态
ubus call system board # 硬件信息
cat /proc/cpuinfo # CPU 信息
free -m # 内存使用
df -h # 磁盘使用
uptime # 运行时间和负载
# 网络诊断
ip addr show # 接口地址
ip route show # 路由表
ip neigh show # ARP 表
ss -tlnp # 监听端口
tcpdump -i eth1 -n port 502 # 抓取 Modbus 流量
# 日志查看
logread -f # 实时日志
logread | grep ERROR # 过滤错误
dmesg | tail -50 # 内核日志
# 进程管理
ps aux # 所有进程
pgrep -a mosquitto # 查找进程
kill -HUP $(pgrep mosquitto) # 发送 HUP 信号
# UCI 调试
uci show # 显示所有配置
uci changes # 未提交的变更