把一台正在同步的比特币核心节点停下来,看起来只是”关个程序”,实际上Linux给这条路径准备了不止一个门。哪些门是体面的告别,哪些门是直接从窗户翻出去,差别会在下次开机时显形。本文按比特币核心v31.0的源码,把信号这层接口逐个讲清楚,并给出一份停机动作清单。
节点其实装了一个”告别前台”
在类Unix系统上,进程对外来的停止请求不是自动配合的,而是要自己登记处理函数。比特币核心在初始化阶段(源码 src/init.cpp 中注册信号处理函数处)做了四件事:登记SIGTERM和SIGINT为触发优雅关停;登记SIGHUP为”重开调试日志”;把SIGPIPE设为忽略。信号处理函数本身刻意做得极薄——它只把一个关停请求标志碰一下,真正清场的是主循环里的关停流程:停RPC、断网络连接、把内存池和费用估算落盘、刷新链状态、释放数据库,最后你才能在日志里看到那行Shutdown done。
理解”信号只是按铃,打扫是另一段代码”这一点,后面所有现象都好解释了。

SIGTERM与SIGINT:两条门铃线,接的是同一个铃
对守护进程发 kill(默认信号就是SIGTERM),或者在前台运行时按Ctrl+C(产生SIGINT),在v31.0源码里这两个信号被登记到同一个处理函数——也就是说,无论哪种方式,节点走的都是同一条优雅关停路径:先停止接收新任务,再按顺序收尾,等待写盘完成。这条路径的另一个入口是 bitcoin-cli stop,它从RPC内部发起同样的请求,所以日常运维里它是首选:有明确回包、能告诉你节点是否接受了指令。
顺带一提,这个”体面退出”的历史很长:v31.0源码树里保留的早期发布说明显示,早在0.3.13这一代就有了SIGTERM干净关停的记录,0.7.0又补上了SIGHUP重开日志的能力。停机接口是比特币核心最早就稳定下来的少数东西之一。
kill -9 跳过的是整段善后
kill -9 发出的是SIGKILL。它不是”更用力的停止”,而是一种根本无法被捕获的信号——内核直接终止进程,节点登记的处理函数连被调用的机会都没有。于是上面那段收尾序列整段跳过:还没刷盘的链状态和钱包数据停在数据库自己的日志里,内存池快照可能停在上一轮的版本上。
比特币核心的数据库设计本来就能扛崩溃重启,所以多数时候下一次开机一切如常:启动自检会抽样校验区块与链状态(由 checkblocks、checklevel 两个参数控制深度),发现状态不整就回滚或重建。但这正是代价所在:kill -9 后的第一次启动可能多花几分钟自检,偶尔会触发更大范围的修复重扫。它应当留给”进程真的卡死、优雅通道叫不动”的场合,而不是当作日常关机键;用完之后,看一眼启动日志里的自检与恢复行,确认数据库没有带伤上岗。
SIGHUP 不停机,只是让日志换一页
很多人第一次遇到SIGHUP会以为它是”重载配置”——比特币核心不是nginx,它改了配置文件通常仍要重启才生效。在这个项目里,SIGHUP的含义窄而专:把调试日志文件重新打开一次。源码里对应的动作只有一行标志位(m_reopen_file),实际用途是配合外部的日志轮转工具:logrotate先把 debug.log 改名或压缩,再给节点发一个SIGHUP,节点随后就会在新的文件句柄上继续写,而不是攥着旧句柄往被改名的文件里写黑洞。如果你没有部署日志轮转,SIGHUP对你几乎无用,乱发也不会造成停机。
SIGPIPE:一条被提前赦免的死刑法
还有一个你永远不会”收到”的信号:SIGPIPE。当另一端(比如一个写了一半就断开的RPC客户端)关闭了连接,往断开连接里写的进程默认会被SIGPIPE当场处死。比特币核心在启动时直接把它设为忽略,让写失败以普通错误返回——这就是为什么你用脚本反复断开RPC连接,也不会把节点带崩。这个登记在同一个信号安装段落里,属于节点防御性设计的一部分。
服务管理器视角:别让超时按下急杀键
把节点交给systemd这类服务管理器时,关停链路通常是:管理器先按单元配置的信号(默认SIGTERM)叫门,等待若干秒,进程还没退就补一刀SIGKILL。比特币核心在大钱包、大缓存或刚同步完的状态下,优雅关停可能需要可观的时间——如果管理器的超时设得太短,你以为的”正常重启”实际每次都是先礼后急杀,等价于反复的kill -9。这类超时的存在有迹可循:v31.0源码树里保留的0.12.0发布说明记录了OpenRC启动脚本的 BITCOIND_SIGTERM_TIMEOUT 变量。部署时把停止超时放宽、把日志级别调够,让节点每次都能自己走到那行Shutdown done,是对数据库最温柔的运维习惯。
一份可执行的停机清单
- 首选
bitcoin-cli stop:有回包、走完整关停序列,脚本里可以轮询进程退出做后续步骤; - systemd管理下用
systemctl stop,并检查单元里TimeoutStopSec是否留足收尾时间; - 前台调试实例用Ctrl+C,它与SIGTERM等价,不必另发明仪式;
- 只把
kill -9留给完全无响应的进程;用后下次启动务必检查日志中的自检、回滚与恢复行; - 想让日志滚动省磁盘,配logrotate加SIGHUP,别指望它能替代重启;
- 需要”到点自动停”的场景(如挖矿机随电价下线),用
stopatheight这类内置参数,而不是外挂cron乱发信号。
风险提示
本文讨论的是节点进程管理动作与其机制边界,不构成投资建议;任何停机方式都不改变链上数据的不可逆性,也不承诺异常断电后修复耗时的一致表现。文中行为描述以比特币核心v31.0源码为准,旧版与本仓库保留的早期发布说明为史实参照,具体参数默认值可能随版本变化,配置前请以你实际使用的版本文档为准。
发表评论
还没有评论,来说两句吧。
评论区为展示样式,提交不会被处理。