[TOC]
kernel/reboot.c 系统重启与关机(System Reboot and Shutdown) 有序关闭系统的协调中枢
历史与背景
这项技术是为了解决什么特定问题而诞生的?
kernel/reboot.c
中的代码是Linux内核中负责处理系统级重启(reboot)、关机(halt)和断电(poweroff)的核心逻辑。这项技术的诞生是为了解决在一个多任务、多组件的复杂操作系统中,如何安全、有序地关闭系统这一根本性问题。
简单地切断电源会对系统造成严重损害,尤其是在文件系统层面。一个有序的关闭过程是必需的,以应对以下挑战:
- 数据一致性:现代操作系统大量使用写缓存(write cache)来提升性能。在关闭前,必须确保所有缓存中的数据(特别是文件系统元数据和用户数据)都被完整地写入持久存储,否则会导致文件损坏或数据丢失。
- 服务优雅退出:系统中的服务和驱动程序在关闭前可能需要执行清理操作,例如通知网络对端连接将中断、保存设备状态、或将设备置于安全模式。
- 硬件状态管理:需要一种机制来最终告诉硬件执行重启或断电的物理操作。这个过程因架构(x86, ARM等)和平台(BIOS, UEFI, ACPI)的不同而有很大差异。
- 提供统一接口:内核需要为用户空间程序(如
shutdown
,reboot
命令)提供一个统一的、与硬件无关的系统调用接口,以触发关闭流程。
kernel/reboot.c
就是这个协调中枢,它提供了reboot()
系统调用,并负责编排整个有序关闭的流程。
它的发展经历了哪些重要的里程碑或版本迭代?
reboot()
系统调用是源自早期Unix的传统API,其在Linux中的实现随着内核的复杂化而不断演进。
- 基本实现:早期内核的实现相对简单,主要关注于同步文件系统和调用特定于体系结构的重启指令。
- 通知链(Notifier Chain)的引入:这是一个关键的里程碑。内核引入了
reboot_notifier_list
。这允许内核中的任何子系统或驱动程序注册一个回调函数,在系统重启流程的特定阶段被调用。这极大地增强了关闭过程的模块化和健壮性,使得各个组件可以独立地管理自己的关闭逻辑。 - 与ACPI/APM的集成:随着ACPI(高级配置与电源接口)取代APM成为主流,
kernel/reboot.c
与ACPI子系统的集成变得至关重要。现在,最终的断电操作通常是通过调用ACPI代码来让主板进入S5(软关机)状态实现的。 - “Magic”常量的引入:为了防止程序因内存错误等原因意外调用到
reboot()
这个破坏性极大的系统调用,内核为其增加了“魔法数字”校验。调用者必须传递特定的、无意义的常量值,否则调用会失败。 - PID命名空间支持:自Linux 3.4起,在PID命名空间内调用
reboot()
会“重启”该命名空间,即终止该命名空间内的init进程,这对于容器管理非常有用。
目前该技术的社区活跃度和主流应用情况如何?
kernel/reboot.c
是内核中一个极其核心和稳定的组件。
- 主流应用:所有用户空间用于关闭或重启系统的命令(
shutdown
,reboot
,halt
,poweroff
,以及通过systemctl
触发的相应操作)最终都会通过C库调用到内核的reboot()
系统调用。 它是所有Linux系统正常关闭流程的必经之路。
核心原理与设计
它的核心工作原理是什么?
kernel/reboot.c
的核心是一个由reboot()
系统调用触发的、精心设计的有序关闭序列。
- 用户空间触发:用户空间的程序调用
reboot(int cmd)
。cmd
参数指定了期望的操作,如LINUX_REBOOT_CMD_RESTART
(重启),LINUX_REBOOT_CMD_HALT
(停机), 或LINUX_REBOOT_CMD_POWER_OFF
(断电)。 - 权限与参数检查:内核首先检查调用进程是否拥有足够的权限(
CAP_SYS_BOOT
),并验证传入的“魔法数字”是否正确,以确保调用的意图是明确的。 - 执行通知链 (Notifier Chain):这是优雅关闭的关键步骤。内核会调用
blocking_notifier_call_chain(&reboot_notifier_list, ...)
。 这会遍历一个链表,依次执行所有已注册的回调函数。 许多关键子系统都会在此注册:- 文件系统:执行最后的同步操作。
- 设备驱动:将硬件置于安全状态。
- Watchdog驱动:通知硬件看门狗即将进行计划内的重启,避免其误触发硬件复位。
- 关闭设备:在通知链执行后,内核会调用
device_shutdown()
来系统性地关闭系统中所有设备。 - 迁移至单一CPU:在多核系统中,内核会执行
migrate_to_reboot_cpu()
,将执行流强制迁移到一个特定的“主”CPU上,并停止其他所有CPU的活动,以确保后续的硬件操作在一个可预测的环境中进行。 - 调用体系结构特定代码:最后,通用的内核代码会调用特定于硬件架构的函数(位于
arch/
目录下,如machine_restart()
,machine_power_off()
)来执行最终的物理操作。 例如,在x86平台上,这可能意味着向ACPI或键盘控制器发送命令,或者直接执行一条能让CPU复位的汇编指令。
它的主要优势体现在哪些方面?
- 有序与安全:通过通知链机制,确保了系统各组件能以正确的顺序优雅关闭,最大限度地保护数据和硬件。
- 抽象与可移植性:将通用的关闭流程与特定于平台的硬件操作分离开来,使得上层逻辑可以跨多种硬件架构复用。
- 健壮性:魔法数字等机制提供了一定程度的保护,防止意外的重启。
它存在哪些已知的劣势、局限性或在特定场景下的不适用性?
- 依赖所有组件的正确实现:有序关闭的成功,依赖于所有注册了
reboot_notifier
的驱动都能正确、快速地完成其清理工作。任何一个回调函数的挂起都可能导致整个关闭过程卡住。 - 无法应对内核完全锁死:如果内核已经发生严重死锁或崩溃(Kernel Panic),
reboot()
系统调用可能根本无法被执行。在这种情况下,系统将依赖于硬件看门狗或需要手动干预。
使用场景
在哪些具体的业务或技术场景下,它是首选解决方案?
reboot()
系统调用是所有需要从用户空间发起有序系统关闭场景的唯一标准方案。
- 标准关机命令:
shutdown -h now
或systemctl poweroff
。 - 标准重启命令:
reboot
或systemctl reboot
。 - 自动化运维:在需要自动进行系统维护(如内核升级后)的脚本中,最后一步通常是调用
reboot
命令。 - ACPI事件处理:当用户按下机箱上的物理电源按钮时,
acpid
守护进程会捕捉到这个ACPI事件,并调用shutdown
命令来触发有序关闭。
是否有不推荐使用该技术的场景?为什么?
- 内核或系统完全无响应:在这种情况下,用户空间的命令无法执行,
reboot()
系统调用也无法被触达。此时需要使用更低级的恢复手段。 - 需要立即、强制重启:在某些极端情况下,如果怀疑有序关闭过程会失败或耗时过长,可能会选择强制重启。但这通常被视为最后手段。
对比分析
请将其 与 其他相似技术 进行详细对比。
存在多种重启系统的方式,它们处于不同的层次,适用于不同的场景。
特性 | reboot() 系统调用 |
硬件看门狗 (Watchdog Timer) | Magic SysRq Key | 带外管理 (e.g., IPMI) |
---|---|---|---|---|
触发方式 | 用户空间软件调用。 | 内核/软件停止“喂狗”导致硬件定时器超时。 | 特殊的键盘组合键 (Alt +SysRq +B )。 |
通过独立的网络接口发送远程命令。 |
优雅程度 | 非常优雅。执行完整的、有序的关闭流程。 | 不优雅。是硬复位,不进行任何软件清理,可能导致数据损坏。 | 不优雅。绕过大部分关闭流程,直接触发重启,仅比硬复位稍好。 | 不优雅。通常是模拟按下电源或复位按钮,是硬复位。 |
可靠性 | 依赖于内核正常运行。内核崩溃则无效。 | 非常高。只要硬件正常,即使内核完全死锁也能触发。 | 较高。只要键盘驱动和中断处理等底层内核功能尚存,就有可能工作。 | 极高。完全独立于主机的操作系统和电源状态。 |
主要用途 | 常规的、计划内的系统关闭和重启。 | 从内核完全锁死等软件故障中自动恢复。 | 在系统严重卡顿、无响应时,由管理员进行手动紧急恢复。 | 对物理服务器进行远程的、最终的电源控制和恢复。 |
kernel/reboot.c
register_restart_handler
1 | /** |
kernel_shutdown_prepare: 准备内核关闭
此函数是内核关机或重启流程的第一阶段, 其核心原理是执行一个有序的、自上而下的”软件层面”关闭序列。它负责通知系统中的所有高级子系统和设备驱动程序”关机即将开始”, 让他们有机会执行各自的清理工作, 以此来”平息(quiesce)”整个系统, 为后续的硬件关机做好准备。
这个函数执行了三个关键的、按顺序排列的动作:
调用重启通知链 (Reboot Notifier Chain): 这是最重要的一步。内核维护了一个名为
reboot_notifier_list
的通知链, 其他内核子系统(如文件系统、网络栈、块设备驱动等)可以向这个链表注册一个回调函数。调用blocking_notifier_call_chain
会同步地、挨个地调用链表中的每一个回调函数。这实现了一个观察者设计模式, 使得核心关机代码无需知道每个子系统的具体实现, 就能让它们各自完成清理任务, 例如:- 文件系统会将所有缓存的数据同步(sync)到物理存储介质(如SD卡、eMMC)中, 保证数据不丢失。
- 网络驱动会关闭网络接口, 终止所有连接。
- 任何需要保存状态的驱动程序都可以在这里执行其保存操作。
禁用用户模式帮助程序 (Usermode Helper): “用户模式帮助程序”是内核用来请求用户空间执行特定任务(如加载固件)的机制。
usermodehelper_disable()
函数会禁止内核创建任何新的用户空间进程。这是一个关键的”静默”步骤, 因为在关机过程中, 用户空间本身正在被拆除, 此时再尝试启动新进程会引入不确定性和竞争条件, 必须被阻止。关闭设备 (Device Shutdown):
device_shutdown()
函数会遍历系统设备模型中的所有设备, 并调用每个设备驱动程序提供的.shutdown()
方法(如果驱动实现了这个方法)。这是比重启通知链更具体、更偏向硬件的通知。它给予每个驱动最后一次机会去执行硬件相关的关闭操作, 例如:- 向设备发送一个断电或复位命令。
- 禁用设备中断。
- 将设备置于一个已知的、安全的低功耗状态。
1 | /* |
unregister_sys_off_handler: 注销一个系统关闭处理程序
此函数的作用是从内核的关机/重启通知链中移除一个先前已注册的”系统关闭处理程序”(sys-off handler
)。这是与register_sys_off_handler
相配对的清理操作。
它的核心原理是根据处理程序注册时指定的类型(阻塞式或原子式), 在正确的通知链上执行注销操作, 然后释放为该处理程序分配的内存。这是一个标准的内核资源清理流程, 确保了在模块卸载或特定功能不再需要时, 不会留下悬空的、无效的回调函数指针, 从而维护了系统的稳定性和健壮性。
详细工作流程:
- 有效性检查: 函数首先通过
IS_ERR_OR_NULL
宏来检查传入的handler
指针是否有效。如果是一个无效指针(NULL或错误编码的指针), 它会直接返回, 避免了后续的非法内存访问。 - 条件性注销: 它检查
handler->blocking
标志位。这个标志位是在注册处理程序时设定的, 它记录了该处理程序是被注册到了阻塞式通知链还是原子式通知链。- 如果
blocking
为true
, 它就调用blocking_notifier_chain_unregister
在可休眠的通知链(如reboot_notifier_list
)上注销处理程序。 - 如果
blocking
为false
, 它就调用atomic_notifier_chain_unregister
在不可休眠的原子通知链(如power_off_handler_list
)上注销处理程序。 - 这个条件判断确保了注销操作总是作用于正确的链表。
- 如果
- 完整性检查:
WARN_ON(err)
是一个健全性检查。notifier_chain_unregister
函数在正常情况下应该总是成功并返回0。如果它返回错误, 意味着内核的通知链数据结构可能已损坏, 这是一个非常严重的、不应该发生的问题。WARN_ON
会在内核日志中打印一个警告信息, 以便开发者能够注意到这个异常情况。 - 内存释放: 在成功地从通知链中移除回调后,
handler
这个包装结构体本身也就不再需要了。函数最后调用free_sys_off_handler
来释放当初为它分配的内存。
1 | /** |
最终的硬件关机序列
此代码片段展示了Linux内核关机流程的最后、最底层的两个步骤。它们负责执行从”完全静默的软件状态”到”物理电源关闭”的最终过渡。machine_power_off
是这个过程的入口, do_kernel_power_off
是它调用的核心执行器。
machine_power_off
: 进入硬件关机前的最后准备
此函数是平台无关的关机序列的最后一站。它的核心原理是在执行真正的、不可逆的硬件关机操作之前, 对CPU状态进行最终的、最低级别的”静默”处理。
工作流程与原理:
- 禁用本地中断 (
local_irq_disable()
): 这是最关键的第一步。它会无条件地禁用当前CPU核心上的所有中断。在关机流程的这一最末端, 系统不能再被任何外部事件(如定时器、设备中断)所打扰, 必须保证后续的指令序列能够不被中断地完整执行。 - 停止其他CPU (
smp_send_stop()
): 在多核(SMP)系统上, 此函数会向所有其他”非主”CPU发送一个核间中断(IPI), 指示它们进入一个无限的while(1)
循环中并停止所有活动。这确保了只有一个CPU(即执行machine_power_off
的这个CPU)在执行最后的关机指令, 其他所有核心都已被冻结, 不会产生任何干扰。 - 执行关机动作 (
do_kernel_power_off()
): 在确保整个CPU系统完全静默之后, 它调用do_kernel_power_off
来执行真正的、平台相关的关机动作。
1 | /* |
do_kernel_power_off
: 关机处理程序的调度器
此函数是硬件关机操作的最终分发器。它的核心原理是提供一个可扩展的、基于优先级和通知链的机制, 来调用一个或多个由平台或驱动程序注册的、负责执行物理断电的函数。
工作流程与原理:
- 兼容旧版API (
pm_power_off
): 内核为了向后兼容, 仍然支持一个旧的、名为pm_power_off
的全局函数指针。如果板级支持包(BSP)定义了这个函数指针,do_kernel_power_off
会通过register_sys_off_handler
动态地将这个旧的函数包装成一个符合新框架的”系统关闭处理程序”(sys_off_handler
), 并注册它。 - 调用通知链 (
atomic_notifier_call_chain
): 这是现代内核的首选机制。它会遍历一个名为power_off_handler_list
的通知链, 并调用所有注册在上面的处理函数。- 原子性:
atomic_
前缀意味着这个调用链是在一个原子上下文(中断已禁用)中执行的, 所有注册的回调函数都必须是原子的, 即绝不能休眠。 - 可扩展性: 允许多个模块(例如, 一个电源管理芯片PMIC的驱动和一个看门狗驱动)都注册自己的关机回调。
- 优先级: 新的
sys_off
框架允许为每个处理程序指定优先级, 以确保它们按正确的顺序被调用。
- 原子性:
- 执行与终结: 在一个典型的关机流程中, 这个通知链中的某个函数(很可能就是那个由
pm_power_off
包装而来的函数)将会执行操作, 导致物理电源被切断。因此,atomic_notifier_call_chain
将永远不会返回。后面的unregister_sys_off_handler
调用只是为了代码逻辑的完整性, 以应对那些只执行了部分关机操作但未断电的异常情况。
1 | /** |
kernel_restart
框架: 内核的有序重启最终阶段
这是一个在Linux内核内部用于执行强制性、有序重启的核心框架。它的作用是在接收到重启命令后(例如, 从用户空间reboot
命令的最终执行, 或从内核其他部分的紧急调用), 以一个明确的、分阶段的、”不归路”式(point-of-no-return)的流程, 关闭内核的核心功能, 并最终触发平台相关的硬件复位。
该框架的原理是确保在执行最后的硬件复位之前, 内核有机会通知其他子系统进行最后的准备工作, 将系统置于一个已知的、稳定的”半停机”状态, 并尽可能地保存用于事后调试的信息。这是一个从通用内核代码到特定于硬件的机器代码的优雅交接过程。
对于STM32H750这样的单核嵌入式系统, 这个流程的原理和步骤同样适用且至关重要:
- 通知链 (
restart_prep_handler_list
): 即使没有多核, 系统中也可能有驱动程序需要在一个干净的重启前执行特定操作, 例如, 一个与外部非易失性存储器通信的驱动可能需要确保其硬件处于空闲状态。这个通知链为它们提供了这样的机会。 - 迁移到重启CPU (
migrate_to_reboot_cpu
): 在单核系统上, 这个函数的作用演变为确保后续的关机代码在一个”安全”的上下文中执行, 通常会禁用抢占和中断, 防止在执行这最后几行关键代码时被意外打断。 - 核心子系统关机 (
syscore_shutdown
): 这会调用STM32片上核心外设(如定时器、中断控制器)的晚期关机回调, 将它们置于一个已知的状态。 - 日志转储 (
kmsg_dump
): 这是调试的关键。如果系统是因为一个内核错误(panic)而重启, 这是将内核日志环形缓冲区的内容转储到持久化存储(如果板级支持代码实现了这个功能, 例如写入Flash的一个特定分区)的最后机会。 - 机器重启 (
machine_restart
): 这是最关键的平台特定步骤。对于绝大多数嵌入式系统, 包括STM32H750, 这个函数的最终实现几乎总是触发内部的看门狗定时器(Watchdog Timer)来强制复位整个SoC。这被认为是最可靠的硬件复位方式, 因为它能从许多软件无法恢复的硬件锁死状态中恢复系统。
restart_prep_handler_list
和 do_kernel_restart_prepare
: 重启准备通知
1 | /* |
kernel_restart
: 内核重启的协调中心
这是暴露给内核其他部分使用的、用于启动重启流程的标准API。
1 | void kernel_restart_prepare(char *cmd) |
kernel_power_off: 关闭系统电源
此函数负责执行一个完整、有序的系统关机流程。它的核心原理不是简单地切断电源, 而是通过一个预定义的、分阶段的序列, 逐级关闭内核的各个子系统, 确保所有数据被妥善处理、所有硬件进入安全状态, 最后才调用平台特定的代码来执行物理上的断电或进入等效的深度休眠状态。这个有序的过程对于保证文件系统完整性和硬件设备状态的一致性至关重要。
1 | static void do_kernel_power_off_prepare(void) |
“有序”关机/重启框架: 委托用户空间, 内核托底
这一组函数共同构成了Linux内核中实现**”有序”(orderly)关机和重启的核心机制。其核心原理是将关机/重启的主要任务委托给用户空间的程序, 同时保留一个内核级的、强制执行的后备方案(fallback)**。
这种设计的哲学是:
- 优雅处理优先: 一个”有序”的关机/重启过程不仅仅是断电或复位。它需要正确地停止服务、同步文件系统(将缓存数据写入磁盘)、卸载网络接口、执行清理脚本等。这些复杂的、策略性的任务最适合由用户空间的
init
系统(如systemd
,sysvinit
, 或嵌入式中常见的BusyBox init
)来管理。因此, 内核的首选策略是请求用户空间来执行这些操作。 - 内核提供最后保障: 内核也知道用户空间可能会因为各种原因(例如,
init
进程卡死, 文件系统损坏)而失败。在这种情况下, 为了响应一个紧急的关机/重启请求(例如, 来自hw_protection_trigger
), 内核必须有一个”B计划”。这个B计划就是跳过用户空间, 直接执行内核级的、更底层的强制关机/重启操作。
这个框架通过以下几个关键部分实现这一原理:
run_cmd
: 这是内核空间到用户空间的桥梁。它使用call_usermodehelper
API, 创建一个用户空间进程来执行一个指定的命令(如/sbin/poweroff
)。__orderly_poweroff
/__orderly_reboot
: 这是策略中心。它们首先尝试通过run_cmd
来执行优雅的关机/重启。如果用户空间程序启动失败(run_cmd
返回错误), 并且这是一个强制请求, 它们就会立即触发内核的后备方案。- Workqueue (
poweroff_work
): 关机操作被封装在一个工作项中。这确保了关机请求可以在一个安全的、非紧急的内核线程上下文(process context)中执行, 避免了在可能持有锁或处于中断上下文的调用路径中直接启动一个复杂的用户空间进程。
在STM32H750这样的单核嵌入式系统上, 这个框架同样适用且重要:
- 用户空间: 运行Linux的STM32系统通常会有一个基于BusyBox的最小化用户空间, 它提供了
/sbin/poweroff
和/sbin/reboot
这两个程序。 init
进程: 这些程序的工作通常是向系统的init
进程(PID 1)发送一个信号,init
进程会负责执行预定义的关机脚本。- 后备方案: 如果这个最小化的用户空间出现问题, 内核的后备方案(
kernel_restart
通常会触发看门狗复位)是恢复系统的唯一可靠途径。
run_cmd
: 内核到用户空间的命令执行器
1 | /* |
__orderly_reboot
和 __orderly_poweroff
: 策略中心与后备方案
1 |
|
工作队列封装
1 | /* |
orderly_poweroff
和 orderly_reboot
: 安全的、可从任何上下文调用的系统关闭/重启接口
这两个函数是Linux内核中用于启动一个有序的(graceful)、干净的系统关闭或重启的标准公共API。它们的核心原理是利用内核的工作队列(workqueue)机制, 将实际的、可能导致休眠或阻塞的关闭/重启操作, 从当前的、可能是原子(atomic)的执行上下文中, 安全地延迟(defer)到一个专用的、安全的内核工作线程中去执行。
这个”上下文延迟”机制是理解这两个函数的关键, 它解决了内核编程中的一个核心问题:
- 问题: 像文件系统同步、设备卸载等关机步骤, 都是需要花费时间并且可能会导致当前任务”休眠”(sleep)或”阻塞”(block)的。然而, 内核中的许多代码路径, 例如中断处理程序或持有自旋锁(spinlock)的代码, 都处于**原子上下文(atomic context)**中, 在这种上下文中休眠是绝对禁止的, 会导致系统崩溃。
- 解决方案:
orderly_poweroff
和orderly_reboot
本身并不执行任何关闭操作。它们只做一件非常快速且安全的事情: 调用schedule_work
。这个函数会将一个预先定义好的”工作项”(poweroff_work
或reboot_work
)放入一个全局的工作队列中。稍后, 一个专门的内核线程(kworker)会从这个队列中取出该工作项, 并在其自身的、安全的**进程上下文(process context)**中执行与该工作项关联的处理函数(poweroff_work_func
或reboot_work_func
)。在这个线程中, 执行休眠或阻塞操作是完全安全的。
因此, 任何内核代码, 无论它处于何种危险的上下文, 都可以安全地调用orderly_poweroff
或orderly_reboot
来”请求”一次关机或重启, 而不必担心违反上下文规则。
orderly_poweroff
: 请求有序关机
1 | /** |
orderly_reboot
: 请求有序重启
1 | /* |
硬件保护执行函数: 最后的强制措施
这一组函数是上一节中介绍的硬件保护框架的执行部分。它们是**”定时炸弹”的”引爆”代码**。当hw_failure_emergency_schedule
调度了一个延迟工作项后, 如果系统未能成功地有序关机或重启, 那么在超时之后, 内核的工作队列就会执行这里的hw_failure_emergency_action_func
函数。
这个函数的核心原理是采用一个多层次的、逐步升级的强制手段来确保系统最终能够被关闭或重启, 无论系统处于多么不稳定的状态。它是一个不计后果的、以保护硬件为最高优先级的最终执行者。
hw_protection_action_str: 将动作枚举转换为字符串
这是一个简单的辅助函数, 作用是将enum hw_protection_action
的数值转换为人类可读的字符串, 专门用于日志记录。
1 | /* |
hw_failure_emergency_action_func: 紧急动作的最终执行者
这是整个硬件保护框架的最后一道防线。当有序关机/重启失败, 并且超时定时器到期后, 这个函数会被内核的工作队列线程调用。
1 | /* |
硬件保护触发框架: 最后的硬件安全防线
这是一个内核级的、最后的硬件保护框架。它的核心作用是在检测到可能导致永久性硬件损坏的严重故障(例如, 调节器欠压、芯片过热)时, 启动一个健壮的、两阶段的紧急系统关机或重启程序。
该框架的原理是**”先尝试优雅处理, 同时设置一个强制执行的最后通牒”**, 以确保无论系统处于何种不稳定的状态, 保护硬件的最终目标都能达成:
第一阶段: 尝试”有序”关机/重启 (Attempt Graceful Action): 当
hw_protection_trigger
被调用时, 它会立即调用orderly_poweroff()
或orderly_reboot()
。这会启动正常的系统关闭流程, 允许内核尝试同步文件系统、卸载设备、执行清理程序等。这是一个”君子协定”, 试图以最安全、最干净的方式关闭系统。第二阶段: 调度”强制”关机/重启 (Schedule Forced Action): 与此同时, 它调用
hw_failure_emergency_schedule()
来调度一个延迟的内核工作项(delayed work)。这个工作项就像一个”定时炸弹”或”死士”。- 如果第一阶段的有序关机成功了, 系统将在定时器到期前就已经断电或重启, 那么这个工作项就永远没有机会执行。
- 但是, 如果系统因为严重的故障已经处于半死机状态, 导致有序关机流程被卡住, 那么在
ms_until_forced
毫秒的超时之后, 内核的工作队列(workqueue)仍然会调度并执行这个延迟工作项。该工作项的处理函数(hw_failure_emergency_action_func
)会执行一个强制的、不计后果的关机或重启(例如, 直接触发看门狗复位), 确保硬件最终被断电或重置。
为了防止在混乱的故障期间被重复触发, 整个框架使用了一个原子计数器(atomic_t allow_proceed
)作为一次性保险丝, 确保只有第一次触发是有效的。
对于STM32H750这样的单核嵌入式系统, 这个框架的原理和实现同样至关重要:
- 原子操作 (
atomic_t
): 即使只有一个CPU核心,atomic_dec_and_test
也能保证操作的原子性, 防止在任务上下文和一个可能触发此操作的中断上下文之间发生竞态条件。 - 紧急日志 (
pr_emerg
):pr_emerg
是最高优先级的内核日志。在系统即将崩溃时, 这条信息有最大的机会被成功地通过串口等控制台输出, 为事后调试提供关键的线索。 - 延迟工作 (
delayed_work
): 这依赖于内核的定时器中断和工作队列调度器。即使系统主应用逻辑已经卡死, 只要内核调度器和定时器中断还能工作, 这个最后的强制关机/重启操作就能被执行。 orderly_poweroff
/reboot
: 在STM32上, 这些函数最终会调用到板级支持代码中实现的pm_power_off
或machine_restart
函数, 这些函数会执行平台特定的操作, 例如通过GPIO控制外部PMIC断电, 或触发内部的看门狗定时器来复位系统。
hw_protection_trigger
和 __hw_protection_trigger
: 触发硬件保护
1 | /** |
hw_failure_emergency_schedule
: 调度强制后备动作
1 | /** |