树莓派断电时修改文本文件:Node.js监听SIGPWR信号失效问题
树莓派断电时Node.js未捕获SIGPWR并写入日志的问题分析与解决
问题根源
你遇到的核心问题在于树莓派断电时系统的瞬间崩溃特性,导致两个关键环节失效:
- 信号传递不及时:当树莓派直接拔掉电源,硬件层面的电源丢失会让系统瞬间停止运行,内核可能来不及将
SIGPWR信号发送给Node.js进程,进程根本没机会执行信号回调。 - 文件写入未持久化:即使进程收到了信号,
fs.appendFileSync虽然是同步写入,但操作系统通常会对磁盘写操作做缓存优化——数据先存在内存缓存里,再批量刷到硬盘。断电瞬间缓存里的数据还没来得及写入物理磁盘,日志自然留不下。
可行的解决办法
1. 强制刷写文件缓存到磁盘
修改你的信号回调,在写入日志后立即调用fs.fsyncSync强制将缓存数据刷到硬盘,确保写入操作真正完成:
var fs = require('fs'); var path = '/home/pi/Documents/Code/surveillanceCam/log/logfile.txt'; const logFileHandle = fs.openSync(path, 'a'); // 打开持久化文件句柄 fs.writeFileSync(path, new Date().toString() + ': Start\n'); fs.fsyncSync(logFileHandle); // 启动日志也强制刷写 process.on('exit', function () { fs.appendFileSync(path, new Date().toString() + ': exit\n'); fs.fsyncSync(logFileHandle); }); process.on('SIGPWR', function () { fs.appendFileSync(path, new Date().toString() + ': SIGPWR\n'); fs.fsyncSync(logFileHandle); process.exit(0); // 主动触发exit事件,确保后续清理逻辑执行 }); process.on('SIGINT', function () { fs.appendFileSync(path, new Date().toString() + ': SIGINT (Ctrl+C)\n'); fs.fsyncSync(logFileHandle); process.exit(0); }); process.stdin.resume();
2. 配置系统层面的断电信号触发
有些树莓派发行版默认没有启用SIGPWR的触发机制,你可以检查并配置:
- 确认系统是否加载了电源检测相关的内核模块,比如
rpi_power_monitor - 编辑
/etc/systemd/system.conf,将DefaultTimeoutStopSec设置为10s左右,给进程足够的处理时间
3. 硬件层面的兜底方案(最可靠)
依赖系统信号本质上受限于系统的存活时间,最稳妥的方式是通过GPIO检测电源状态:
- 给树莓派外接一个简单的电压检测模块,监控电源输入电压
- 当检测到电压低于阈值(比如4.8V)时,立即触发日志写入和刷写操作
- 这种方式不依赖系统内核,能在系统崩溃前更早地捕获断电事件
4. 搭配小型UPS(不间断电源)
如果条件允许,给树莓派配一个迷你UPS(比如基于18650电池的模块),断电后能提供3-5秒的供电时间,足够系统完成信号传递、日志写入和刷写的全流程。
针对你的后续需求的优化
对于启动时检查非正常关机的逻辑,可以在服务启动时读取日志文件的最后几行:
function checkUncleanShutdown() { try { const logContent = fs.readFileSync(path, 'utf8'); const lines = logContent.trim().split('\n'); const lastLine = lines[lines.length - 1]; // 判断最后一条记录是否是启动记录,且没有后续的结束标记 if (lastLine.includes(': Start') && lines.length === 1) { console.error('检测到非正常关机!'); // 这里可以添加报错逻辑,比如发送通知、记录异常日志等 } } catch (err) { // 日志文件不存在,视为首次启动 console.log('首次启动,创建日志文件'); fs.writeFileSync(path, new Date().toString() + ': Start\n'); fs.fsyncSync(logFileHandle); } } // 启动时先执行检查逻辑 checkUncleanShutdown();
内容的提问来源于stack exchange,提问作者Addeuz
相关产品推荐
相关产品推荐

