当前位置:首页>Linux>Linux 内核功耗子系统(十七):hibernation 过程梳理

Linux 内核功耗子系统(十七):hibernation 过程梳理

  • 2026-08-18 23:10:32
Linux 内核功耗子系统(十七):hibernation 过程梳理

全景介绍

三种睡下去

Linux 里整机休眠实际有三种,功耗从低到高:

  • • s2idle(Suspend-To-Idle):CPU 全部进 deep idle,DRAM 保 self-refresh,外设保供电,唤醒 latency 微秒级
  • • s2ram / mem(Suspend-To-RAM,S3):CPU 断电、外设断电、DRAM 保 self-refresh,SoC 只留 always-on 域,唤醒 latency 毫秒级
  • • hibernation / disk(Suspend-To-Disk,S4):整机断电,把整个内存内容写到磁盘,然后 shutdown。下次开机重新加载

前两种醒过来之后,还是原来那个内核,DRAM 里的所有指针、任务、缓存都在原地。hibernation 不一样:下次开机时,是一个新内核从磁盘里读一张旧内核的完整照片,然后原地覆盖自己

这个两个内核的架构决定了 hibernation 的整套设计。

图 1 · 三种睡下去横向对比:s2idle / s2ram / hibernation

为什么还需要 hibernation

s2ram 已经省到只剩 DRAM refresh 的电流(几十 mW),为什么还要 S4?三个理由:

  • • 零功耗:磁盘写完就 poweroff,插头拔了都行,笔记本长时间不用、服务器机房断电演练
  • • 状态持久化:DRAM 数据不再受温度、电池老化、意外掉电的威胁
  • • 跨物理设备迁移的萌芽:早年 tuxonice / uswsusp 甚至试图把 image 拷到另一台机器 resume,虽然实际用得少

代价也大:恢复时间从毫秒级变到秒级(要读几百 MB 到几 GB 数据);对内存布局稳定性要求极高(image kernel 必须能重建那张历史照片)。

与前面几篇的关系

  • • system sleep(十四) 讲的是 s2ram/s2idle 的整机流程,freezer + DPM + suspend_ops->enter
  • • wakeup source(十六) 讲的是被谁抬回来,但 wakeup source 只在 s2ram/s2idle 时生效,因为 hibernation 已经 poweroff,只能靠电源键/RTC/WOL
  • • hibernation(本篇) 补上第三条路径:不是睡而是快照 + poweroff + 重启加载

三种模式在 kernel/power/ 目录下并列:suspend.c(s2ram/s2idle)、hibernate.c(S4)、autosleep.c(Android 自动 s2ram)。

一个容易理解错的问题

写驱动最容易混淆的是 hibernation 和 suspend 用的 是同一套struct dev_pm_ops 回调,但语义差别很大:

  • • suspend() / resume():s2ram/s2idle 通用
  • • freeze() / thaw():hibernation 的第一阶段(造快照前后)
  • • poweroff() / restore():hibernation 的第二阶段(写盘 poweroff / 从盘恢复)

只写 suspend()/resume() 的驱动,hibernation 时 PM Core 会自动回退用 suspend/resume。但硬件状态在 hibernation 里被彻底断电,如果驱动依赖某个不能持久化的资源(比如 GPU 上的 firmware image),必须在 restore() 里重新加载。这个 fallback 陷阱是 hibernation 相关 bug 的最大来源。

实际情况

hibernation 有三个关键设计取舍决定了它今天的样子。

取舍一:两阶段快照,不是单阶段 dump

朴素想法:一次性把 DRAM 全部写盘。这不可行,因为你正在运行的内核也在 DRAM 里,写盘时它在改自己。

swsusp 的解法是 两阶段

  • • 阶段一hibernation_snapshot(),把整个内存复制到另一半内存(叫 image pages)。快照过程中,freezer 保证用户态不动、GFP_ATOMIC 保证内核不产生新 page
  • • 阶段二hibernation_write_image(),从容地把 image pages 写盘,因为它们已经不再变化

这就要求 hibernation 前至少要有 50% 内存空闲,一半装原始数据、一半装快照。所以 image_size 内核参数默认是当前空闲内存的 2/5,超过就分批 shrink。

取舍二:两个内核,不是一个内核继续跑

为什么 resume 不是接着当时那个状态继续跑?因为 hibernation 已经彻底 poweroff,DRAM 内容清空,下次开机必然是一个全新的内核(bootloader 加载的、和当时那个不同副本、内核版本/config 甚至可能不同)。

于是 resume 路径变成:

  1. 1. boot kernel 启动到能挂载 swap 分区
  2. 2. 检测到 swap 里有 hibernation image、且 signature 有效
  3. 3. 分配一堆临时 pages 读出 image
  4. 4. swsusp_arch_resume()跳转到 image kernel 的入口,让它接管
  5. 5. image kernel 恢复自己的所有指针、任务、page 表,然后 thaw 所有设备
  6. 6. 原来 boot kernel 的所有 page 被丢弃,包括正在执行 swsusp_arch_resume() 的那段代码

这个从 A 内核跳到 B 内核的动作是 hibernation 里最魔幻的部分,它的实现是 arch 相关的汇编。arm64 平台的实现在 arch/arm64/kernel/hibernate-asm.S(x86_64 有一份对应的 arch/x86/power/hibernate_asm_64.S);本文的汇编片段都以 arm64 为准。

取舍三:freezer 是硬门槛,不是软约束

hibernation 开始前必须把所有用户态进程和大部分内核 kthread 冻住。这不是可选项:

  • • 用户态进程持续在写内存 → 快照瞬间不一致 → resume 后数据损坏
  • • 某些 kthread(比如 workqueue、io_wq)也会污染快照

freeze_processes() 用一个 flag PF_FROZEN + try_to_freeze() 的检查点让所有可冻结任务停在安全位置。任何一个进程冻结失败,hibernation 直接 abort,这就是为什么服务器上跑着某个顽固进程时,hibernate 会失败。

抽象对象

一句话概括:hibernation 是内存分区 → 快照容器 → 落盘协议三件套

中心一:struct swsusp_info(image header)

写盘时最先落盘的是一个 header:

// kernel/power/swsusp.c
struct swsusp_info {

struct new_utsname  uts;
            // uname 信息,用于校验
    u32                 version_code;   // 内核版本号
    unsigned
 long       num_physpages;  // 快照前的物理页数
    int
                 cpus;
    unsigned
 long       image_pages;    // image 中 page 数
    unsigned
 long       pages;          // 加上元数据后的总 page 数
    unsigned
 long       size;           // 字节大小
} __aligned(PAGE_SIZE);

这个 header 是这张照片属于哪个内核的身份证,resume 时 boot kernel 会先读这一页,看 uts 和 version_code 是不是自己(或至少是兼容的),不匹配就拒绝 resume。

中心二:struct snapshot_handle(快照迭代器)

实际的快照是一堆散乱的 page,用 handle 迭代访问:

// kernel/power/power.h
struct snapshot_handle {

    unsigned
 int        cur;            // 当前 page 索引
    void
                *buffer;        // 当前 page 的内容
    int
                 sync_read;      // 是否同步读
};

上层协议就是一个 snapshot_read_next(handle) / snapshot_write_next(handle) 循环,每次拿到下一页 4KB 数据。这个抽象让写盘和用户态 uswsusp 走同一套接口/dev/snapshot 就是把这个 handle 封给了用户态)。

中心三:swap_map[](磁盘位置索引)

image page 落到 swap 分区里的哪些扇区?用一个大数组记下来:

// kernel/power/swap.c
static
 sector_t swsusp_last_page = 0;
struct swap_map_page {

    sector_t
        entries[MAP_PAGE_ENTRIES];
    sector_t
        next_swap;      // 下一个 swap_map_page 的位置
};

swap_map_page 本身也占一个扇区,多个 map page 用链表串起来,read 时按链表一路遍历。第一个 swap_map_page 的位置存在 swap header 的 resume_offset 字段,bootloader 传给 boot kernel 后,kernel 就知道从哪读起。

图 2 · swap 分区 image 布局与 header/map/data 三段

视角一:控制入口,/sys/power/state 和 /sys/power/disk

用户态发起 hibernation 有两条路:

  • • 写 /sys/power/state = disk:走内核默认路径,模式由 /sys/power/disk 决定
  • • 走 /dev/snapshot:用户态自己控制每一步(uswsusp/tuxonice 用),细粒度大

/sys/power/disk 有五个可选值:

  • • platform:调用 platform_ops->finish/enter 让固件把机器 poweroff(ACPI S4)
  • • shutdown:等价于 reboot -h,写盘完就 shutdown
  • • reboot:写盘完就 reboot,重启后 resume
  • • suspendhybrid sleep,先写盘,再 s2ram。电池撑得住就走 resume from RAM 快路径,撑不住就走 resume from disk
  • • test_resume / testproc:调试用

视角二:resume 触发,resume 内核参数 / initramfs 里的 resume 工具

resume 侧的入口有两个:

  • • 内核内建:cmdline 里 resume=/dev/sda2 resume_offset=12345 直接告诉内核到哪找 image
  • • initramfs 里的 resume 工具update-initramfs 会把 resume 逻辑打包进 initramfs,好处是可以选择 resume 前先 mount root、prompt 用户

内核路径最后都会走到 software_resume():读 swap header → 找 signature → 分配 image pages → 读盘 → 调 hibernation_restore() 跳转到 image kernel。

视角三:设备侧,dev_pm_ops 的第二组回调

驱动为 hibernation 提供的是四个第二组回调:

struct dev_pm_ops {
    // 第一组(s2ram/s2idle)

    int
 (*suspend)(struct device *dev);
    int
 (*resume)(struct device *dev);

    // 第二组(hibernation 特有)

    int
 (*freeze)(struct device *dev);      // 快照前,停止硬件活动
    int
 (*thaw)(struct device *dev);        // 快照完成,恢复正常运行(image 已在内存中)
    int
 (*poweroff)(struct device *dev);    // 写盘完成,即将 poweroff
    int
 (*restore)(struct device *dev);     // 从盘恢复后,重建硬件状态
};

时序上,一次完整的 hibernate + resume 是:

freeze → snapshot → thaw → (系统正常运行几秒钟) → poweroff → 写盘 → 断电
    ...下一次开机...
boot kernel 起来 → 读盘 → jump 到 image kernel → restore

freeze 和 poweroff 的差别:freeze 阶段设备只是暂停(快照要包含设备当前寄存器),thaw 后设备恢复继续跑;poweroff 阶段设备要做实际的下电准备(因为快照已经写完,接下来断电了)。

视角四:内存侧的 page 分类与 shrink

hibernation 把物理内存 page 分成几类,落盘策略不同:

  • • saveable pages:普通数据,进快照
  • • highmem pages:ARM32 / x86_32 遗留概念,需要临时映射后再拷
  • • nosave pages:kernel text/rodata、per-CPU init 数据、initrd 释放的 pages,不进快照(下次开机 boot kernel 会重建)
  • • unsaveable pages:某些 hardware DMA 缓冲、free pages,被跳过

如果 image 太大装不下,shrink_all_memory() 会主动收缩 page cache、释放 slab、swap out 部分匿名页,image 太大是 hibernate 失败的第二大原因(第一大是 freezer 失败)。

模型

hibernation 的核心模型有两个:快照生命周期 和 两内核 handoff

快照生命周期模型

一张快照从造出来到写盘到读回来到销毁,经历这些状态:

hibernation_snapshot()
[空闲]  ──────────────────────────► [in-memory image]
                                    │
                    hibernation_write_image()
                                    ▼
                            [written to disk]
                                    │
                            系统 poweroff
                                    │
                                    ▼
                            [持久化在磁盘上]
                                    │
                            下次开机 boot kernel
                            software_resume()
                                    │
                                    ▼
                            [in-memory image (in boot kernel)]
                                    │
                            swsusp_arch_resume()
                                    │
                                    ▼
                            [restored (in image kernel)]

关键点:image 在磁盘上可以躺任意长时间,只要 swap 分区不被写坏、内核版本兼容,一年后开机也能 resume(虽然实践中没人这么用)。

图 3 · 快照生命周期(in-memory → disk → in-memory → restored)

两内核 handoff 模型

boot kernel 和 image kernel 共存的窗口只有几十毫秒,但是这段时间的语义最诡异:

图 4 · 两内核 handoff 双时序(boot kernel 与 image kernel)

[boot kernel]                       [image kernel]
    │
    software_resume():
      读 image 到 safe pages
      调用 arch handoff
      │
      ─────► swsusp_arch_resume() ────► restore_registers()
                                          │
                                          切换 page table
                                          │
                                          恢复所有 CPU 寄存器
                                          │
                                          ─────► [image kernel 完全接管]
                                                    │
                                                    thaw_processes()
                                                    resume_console()
                                                    ...

image kernel 醒来时看到的时间戳、系统状态、进程列表都是当年那个,它完全不知道刚才发生了一次 poweroff 和一次重启。所以:

  • • jiffies 会突然跳跃(RTC 会尝试修正,但精度有限)
  • • 网络连接全部失效(TCP 对端已经超时了)
  • • 硬件中断号如果 SoC 有变化就完蛋(虽然同一台机器不会有)
  • • 时钟源(clocksource)需要重新校准

数据流

从触发 hibernate 到下次 resume 完成,数据流分成四段。

图 5 · hibernate + resume 完整数据流泳道

阶段一:freeze + snapshot

用户态写 /sys/power/state = disk 触发 hibernate()

// kernel/power/hibernate.c
int
 hibernate(void)
{
    int
 error, nr_calls = 0;

    // 1. 通知所有 PM notifier

    error = __pm_notifier_call_chain(PM_HIBERNATION_PREPARE, -1, &nr_calls);

    // 2. 拿 hibernation 独占锁(防并发)

    lock_system_sleep();

    // 3. 冻结用户态

    error = freeze_processes();
    if
 (error)
        goto
 Thaw;

    // 4. 冻结内核态 kthreads

    error = freeze_kernel_threads();
    if
 (error)
        goto
 Thaw;

    // 5. sync 磁盘(保证脏 page 落盘)

    ksys_sync_helper();

    // 6. 造快照(这是核心)

    error = create_image(in_suspend);
    ...
}

create_image() 内部:

// kernel/power/hibernate.c
static
 int create_image(int platform_mode)
{
    // 关中断、把 CPU 降到 non-boot CPU 全下线

    error = disable_nonboot_cpus();

    // suspend 所有设备的 noirq 阶段

    error = dpm_suspend_end(PMSG_FREEZE);

    // 开始真正的 snapshot

    save_processor_state();

    // 这里是关键:hibernation_snapshot 会造出 in-memory image

    error = swsusp_arch_suspend();

    // 从这里返回可能是两条路:

    // - 造 image 结束(in_suspend == 1,接着 poweroff)

    // - image kernel 醒来(in_suspend == 0,接着 restore)


    restore_processor_state();
    dpm_resume_start(in_suspend ? PMSG_QUIESCE : PMSG_RECOVER);
    enable_nonboot_cpus();
    return
 error;
}

swsusp_arch_suspend() 是 arch 相关的核心(arm64 版):

// arch/arm64/kernel/hibernate-asm.S 精简版
SYM_CODE_START(swsusp_arch_suspend)
    // 把 callee-saved regs (x19-x30) + sp + ttbr1/tcr/sctlr 等

    // 保存到 struct sleep_stack_data

    save_cpu_regs
    bl      swsusp_save             // C 函数:拷贝 saveable pages 到 image_pages
    // 走到这里说明 snapshot 造好了,正常返回;C 侧 in_suspend 保持为 1

    mov     x0, #0
    ldp     x29, x30, [sp], #16
    ret
SYM_CODE_END
(swsusp_arch_suspend)

SYM_CODE_START
(_cpu_resume)         // resume 时 image kernel 从这里"醒过来"
    // 由 boot kernel 的 swsusp_arch_resume 跳过来

    // 恢复 ttbr1 / tcr / sctlr / vbar / callee-saved regs / sp

    restore_cpu_regs
    mov     x0, 
#0                  // 让返回值为 0,模拟 image kernel
                                    // 从 swsusp_arch_suspend 返回

    ret
SYM_CODE_END
(_cpu_resume)

这段汇编的讲究之处:同一段代码有两个返回点,一次是 snapshot 时 swsusp_arch_suspend 自己 return(in_suspend=1),一次是 resume 时 image kernel 从 _cpu_resume 醒来、假装是 swsusp_arch_suspend 返回(in_suspend=0)。C 代码上层用 in_suspend 变量区分自己是哪条路径。

arm64 相比 x86_64 多一件事:resume 时 MMU 一开就必须让 ttbr1_el1 指向 image kernel 的 swapper_pg_dir,否则地址翻译立刻错乱。所以 restore_cpu_regs 里先切 TTBR、再 isb,再恢复寄存器,最后 eret 之前 sctlr 里的 M 位必须已经稳定。

阶段二:write image

snapshot 造好之后,hibernation_write_image() 遍历 image pages 写盘:

// kernel/power/swap.c
int
 swsusp_write(unsigned int flags)
{
struct swap_map_handle handle;

struct snapshot_handle snapshot;

struct swsusp_info *header;


    // 拿到第一页(就是 swsusp_info header)

    snapshot_read_next(&snapshot);
    header = (struct swsusp_info *)snapshot.buffer;

    // 找 swap 分区里连续/离散的空闲扇区

    error = get_swap_writer(&handle);

    // 先写 header(写到第一个分配的扇区)

    error = swap_write_page(&handle, header, NULL);

    // 循环写剩余的 image pages

    while
 (1) {
        error = snapshot_read_next(&snapshot);
        if
 (error <= 0)
            break
;
        error = swap_write_page(&handle, snapshot.buffer, NULL);
    }

    error = flush_swap_writer(&handle);

    // 最后一步:把 swap header 里的 signature 改成 "S1SUSPEND"

    // 这一步是"发布"——之前所有数据都已经落盘,signature 一改,boot kernel 就能识别

    if
 (!error)
        error = mark_swapfiles(&handle, flags);

    return
 error;
}

mark_swapfiles() 就是 hibernation 里的 commit 点,像数据库事务一样,写完所有数据才最后一步提交。如果 poweroff 发生在这一步之前,image 无效;这一步之后,image 就是有效的。

如果开了 image 压缩(CONFIG_HIBERNATION_ENC 或 lzo/lz4/xz),中间会加一层压缩器,把 4KB page 压成几百字节再落盘,能省大约 50% 磁盘 IO。

阶段三:write signature + poweroff

image 写完、signature 打上,接下来 power_down()

// kernel/power/hibernate.c
static
 void power_down(void)
{
    switch
 (hibernation_mode) {
    case
 HIBERNATION_SUSPEND:
        // hybrid:进 s2ram

        error = suspend_devices_and_enter(PM_SUSPEND_MEM);
        break
;
    case
 HIBERNATION_PLATFORM:
        // 让固件(ACPI/PSCI)执行 S4

        hibernation_platform_enter();
        break
;
    case
 HIBERNATION_SHUTDOWN:
        // 直接 shutdown

        kernel_power_off();
        break
;
    case
 HIBERNATION_REBOOT:
        // 直接 reboot

        kernel_restart(NULL);
        break
;
    }
    // 如果走到这里,说明 poweroff 失败——只能 halt

    while
 (1);
}

HIBERNATION_PLATFORM 走 ACPI/PSCI 的意义:固件知道这次不是普通 poweroff,是 hibernation,BIOS/UEFI 里可以显示 resuming from hibernation 提示、跳过某些自检、加速下次启动。ACPI 的 S4 状态就是这个用途,PSCI 通过 SYSTEM_OFF 实现(没有专用 opcode,需要 platform 侧配合)。

阶段四:resume

下次开机,boot kernel 起来到 software_resume()

// kernel/power/hibernate.c
static
 int software_resume(void)
{
    // 1. 拿 resume 设备(cmdline resume= 或 sysfs /sys/power/resume)

    resume_device = ...;
    swsusp_resume_device = resume_device;

    // 2. 打开 swap 分区

    error = swsusp_check();
    if
 (error)
        goto
 Unlock;   // signature 不对,说明没有 image,正常启动

    // 3. 冻结用户态、准备 image

    error = freeze_processes();

    // 4. 分配临时 pages 装 image(叫 safe pages)

    error = swsusp_read(&flags);

    // 5. arch 相关的 handoff

    error = hibernation_restore(flags & SF_PLATFORM_MODE);

    // 走到这里说明 restore 失败——上面 hibernation_restore

    // 如果成功,控制权已经交给 image kernel,永远不会回来

    ...
}

swsusp_check() 里最有意思的一段:

// kernel/power/swap.c
int
 swsusp_check(void)
{
struct block_device *bdev;


    bdev = blkdev_get_by_dev(swsusp_resume_device, FMODE_READ, ...);

    // 读 swap 分区第一个 page(swap header)

    hib_submit_io(REQ_OP_READ, 0, 0, swsusp_header);

    // 看 signature

    if
 (memcmp(HIBERNATE_SIG, swsusp_header->sig, 10) == 0) {
        // 找到 hibernation image

        // 把 signature 改回 swap 的原始值 "SWAPSPACE2"

        // 这样下次开机(没有 hibernation image)时就是普通 swap

        memcpy
(swsusp_header->sig, swsusp_header->orig_sig, 10);
        hib_submit_io(REQ_OP_WRITE, REQ_SYNC, 0, swsusp_header);
        return
 0;
    }
    return
 -EINVAL;
}

signature 一读到就立刻改回去的目的:一旦 boot kernel 开始 resume,就消费这个 image,如果 resume 中途失败(比如 image 损坏),下次不会再尝试同一张损坏的 image。

hibernation_restore() 里最关键的是把 image pages 从临时位置搬回原始物理地址,因为 image kernel 醒来时,指针、page 表都指向当年那个物理地址,boot kernel 读进来时不一定能占到那些地址(可能被 boot kernel 自己占了),所以先读到 safe pages,最后在关中断状态下一次性 memcpy 到原地址,然后跳转。

运行

用户态发起姿势

姿势一:一次性 hibernate(最常见)

# 系统层
sudo
 systemctl hibernate

# 或者直接写 sysfs

echo
 disk | sudo tee /sys/power/state

# 用 pm-utils(老系统)

sudo
 pm-hibernate

姿势二:hybrid sleep

# 先写盘再进 s2ram,电池够就走 s2ram 快恢复
echo
 suspend | sudo tee /sys/power/disk
sudo
 systemctl hybrid-sleep

hybrid 的好处:s2ram 期间如果电池撑住,resume 是毫秒级;一旦电池耗尽 SoC 断电,下次开机走 hibernate resume,状态不丢。这是笔记本厂商推荐的默认模式。

姿势三:uswsusp / userspace hibernate

老工具 uswsusp 通过 /dev/snapshot 让用户态完全接管 image 处理,好处是可以做加密、压缩策略、进度显示。现在基本被内核内建的 lz4 压缩取代。

resume 相关配置

/etc/default/grub 里 kernel cmdline 常见的三个参数:

resume=UUID=xxxxx-xxxx-xxxx      # swap 分区(或 swap 文件所在设备)
resume_offset=12345              # swap 文件的偏移(用 filefrag 拿)
no_console_suspend               # 保留 console 输出,方便调试

用 swap 文件 hibernate 时 resume_offset 是必须的,因为 swap 文件在文件系统里可以不连续,内核需要知道 image 的物理起点:

# 拿 swap 文件的物理起点
sudo
 filefrag -v /swapfile | awk 'NR==4 {print $4}' | tr -d '..'
# 假设输出 234567

# 写到 /etc/default/grub:

# GRUB_CMDLINE_LINUX_DEFAULT="... resume=UUID=xxx resume_offset=234567"

驱动实现要点

hibernation-safe 的驱动需要额外考虑三件事:

要点一:freeze/thaw 只是暂停,不要彻底 shutdown

static int mydev_freeze(struct device *dev)
{
struct mydev *d =
 dev_get_drvdata(dev);

    // 停止硬件活动、保存需要持久化的寄存器状态

    mydev_stop_dma(d);
    mydev_save_regs(d);
    // 不要 clk_disable / regulator_disable

    // 因为 thaw 之后设备要立刻恢复运行

    return
 0;
}

static
 int mydev_thaw(struct device *dev)
{
struct mydev *d =
 dev_get_drvdata(dev);
    mydev_restore_regs(d);
    mydev_start_dma(d);
    return
 0;
}

要点二:poweroff 才做实际的下电

static int mydev_poweroff(struct device *dev)
{
struct mydev *d =
 dev_get_drvdata(dev);

    // 这时候 image 已经在盘上了,即将断电

    // 可以放心 clk_disable / regulator_disable

    mydev_shutdown(d);
    return
 0;
}

static
 int mydev_restore(struct device *dev)
{
struct mydev *d =
 dev_get_drvdata(dev);

    // 从磁盘恢复回来,硬件是"完全冷启动"状态

    // 必须重新初始化——不能假设寄存器保留了任何 freeze 时的值

    mydev_hw_init(d);
    mydev_restore_regs(d);
    mydev_start_dma(d);
    return
 0;
}

要点三:不能有内存里的 firmware handle

如果驱动的运行状态依赖某个当时用 request_firmware() 加载的 blob,restore 时必须重新调用 request_firmware(),因为 image 里的 firmware 副本理论上还在,但很多 GPU/DSP firmware 会被写到设备内 SRAM,磁盘 image 里的副本已经过期了。

Nvidia/AMD GPU、DSP、部分 WiFi 芯片是这一类问题的重灾区,所以 hibernation 在这些平台上一直被打上 experimental 标签。

总结

hibernation 子系统做的三件事:造快照、写磁盘、下次开机让新内核加载旧内核状态。这三件事分别对应三层设计:

  • • 造快照 = hibernation_snapshot + freezer + swsusp_arch_suspend 保存 CPU 状态 + swsusp_save 拷贝 saveable pages
  • • 写磁盘 = swsusp_write 逐页写 → swap_map_page 记录扇区 → mark_swapfiles 提交 signature
  • • 加载 + 交接 = boot kernel 起来 → software_resume 读盘 → swsusp_arch_resume 跳转 image kernel → 恢复所有硬件

hibernation 的机制不在任何单一函数,它的核心难点在于:同一份内存有两个内核在写、同一段代码有两次 return、同一批 page 落盘时可能被压缩、被加密、被 shrink。任何一处理解偏差都会体现为 resume 后崩溃或 resume 一半失败。

在实际部署上,hibernation 用得越来越少,SSD 普及后 image 写入更快,但用户对秒开的期待也高了。

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-08-21 18:46:12 HTTP/2.0 GET : https://f.mffb.com.cn/a/504698.html
  2. 运行时间 : 0.204716s [ 吞吐率:4.88req/s ] 内存消耗:5,249.08kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=07e38522a965b40639cce198502cdd59
  1. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/runtime/temp/067d451b9a0c665040f3f1bdd3293d68.php ( 11.98 KB )
  140. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.001011s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.001500s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000607s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000636s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.001313s ]
  6. SELECT * FROM `set` [ RunTime:0.000575s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.001359s ]
  8. SELECT * FROM `article` WHERE `id` = 504698 LIMIT 1 [ RunTime:0.002828s ]
  9. UPDATE `article` SET `lasttime` = 1787309173 WHERE `id` = 504698 [ RunTime:0.013574s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 67 LIMIT 1 [ RunTime:0.000611s ]
  11. SELECT * FROM `article` WHERE `id` < 504698 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.000985s ]
  12. SELECT * FROM `article` WHERE `id` > 504698 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.001045s ]
  13. SELECT * FROM `article` WHERE `id` < 504698 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.002113s ]
  14. SELECT * FROM `article` WHERE `id` < 504698 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.003423s ]
  15. SELECT * FROM `article` WHERE `id` < 504698 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.010371s ]
0.208373s