当前位置:首页>Linux>[LNX015] 深度解析 Linux ARM64 中断子系统:从硬件触发到驱动处理

[LNX015] 深度解析 Linux ARM64 中断子系统:从硬件触发到驱动处理

  • 2026-08-21 22:51:01
[LNX015] 深度解析 Linux ARM64 中断子系统:从硬件触发到驱动处理
文章内容
  • 1. IRQ 处理与向量表入口
  • 2. 从 IRQ 向量表到 IRQ 处理函数
  • 3. 中断域与 IRQ 派发逻辑
  • 4. IRQ 处理函数上半部与下半部

ARM64 平台 Linux-6.18.38 版本

1. IRQ 处理与向量表入口

ARM64 架构采用四级异常级别(Exception Level, EL)权限金字塔模型,这四个级别从 EL0 到 EL3,随着数字的增加,软件的执行权限也相应增加

在未启用 KVM 等虚拟化技术的情况下,Linux 操作系统的内核运行于 EL1,而用户空间的应用程序运行于 EL0

EL
名称
核心作用与权限
EL0
用户态
User
权限最低
运行日常 App、Shell 等,不能直接访问底层硬件或修改系统关键资源
EL1
内核态
Kernel
特权模式
运行操作系统内核,掌管资源调度、中断处理及内存管理等核心功能
EL2
虚拟机监控器
Hypervisor
较高权限
专为虚拟化设计(如 KVM),实现多系统共存与隔离,支持云原生环境
EL3
安全监控器
Secure Monitor
权限最高
安全世界的“守门人”,负责安全与非安全状态切换,控制可信执行环境 (TEE)

ARM64 和异常处理相关的硬件寄存器

寄存器
核心作用
读写时机与使用者
ELR_ELx
保存异常返回地址(PC)
硬件:异常发生时自动写入
内核:入口读出保存,返回前恢复
SPSR_ELx
保存异常前的处理器状态(PSTATE)
硬件:异常发生时自动写入
内核:返回时恢复(由 ERET 生效)
VBAR_ELx
异常向量表基址
固件/内核:初始化或切换向量表时写入
硬件:异常发生时读取并跳转
PSTATE
处理器状态(条件标志、中断屏蔽位等)
硬件:通过 SPSR 保存/恢复
内核:通过系统寄存器间接控制
ISR_EL1
异步异常/中断状态(如 SError 挂起标志)
硬件:发生异步异常时置位
内核:读取以检测和处理异常
ICC_IAR1_EL1
中断应答(获取待处理中断 ID)
内核:中断入口读取,触发 GIC 提供 ID
ICC_EOIR1_EL1
中断结束(通知 GIC 处理完成)
内核:中断处理结束时写入中断 ID
ICC_PMR_EL1
中断优先级掩码
内核:读/写以动态调整允许响应的中断优先级阈值

当发生 IRQ 时,CPU 会根据中断发生时的当前异常级别以及使用的栈指针,跳转到 EL1 异常向量表中对应的入口进行处理

处理器状态
向量表入口
说明
EL1t (内核态, 使用 SP_EL0)
kernel_ventry 1, t, 64, irq
内核在处理其他异常时,被 IRQ 打断。
EL1h (内核态, 使用 SP_EL1)
kernel_ventry 1, h, 64, irq
内核正常运行时,被 IRQ 打断。
EL0_64 (用户态, 64位)
kernel_ventry 0, t, 64, irq
64位用户程序运行时,被 IRQ 打断。
EL0_32 (用户态, 32位)
kernel_ventry 0, t, 32, irq
32位用户程序运行时,被 IRQ 打断。

2. 从 IRQ 向量表到 IRQ 处理函数

ARM64 异常向量表代码是由 kernel_ventry 宏定义的

核心职责可以精简为以下三点:

  1. 1. 硬件对齐:确保每个异常入口严格满足 ARM64 硬件要求的 128 字节对齐。
  2. 2. 栈溢出检测与逃生:在通用寄存器尚未保存的“真空期”,利用纯数学运算无损检测栈是否溢出。若溢出,立即强制切换到安全的备用栈(overflow_stack),防止内核崩溃。
  3. 3. 安全路由:完成底层的现场保护与安全检查后,将控制权精准移交给下一层的具体处理函数(如 el1h_64_irq)。

下面时宏展开代码的详细注释

/* 
 * https://elixir.bootlin.com/linux/v6.18.38/source/arch/arm64/kernel/entry.S
 * 定义宏 kernel_ventry,接收 4 个必填参数 (req = required):
 * el     : 异常级别 (0 = EL0 用户态, 1 = EL1 内核态)
 * ht     : 栈指针模式 (t = Task/Thread 模式使用 SP_EL0, h = Handler 模式使用 SP_EL1)
 * regsize: 寄存器位宽 (32 或 64)
 * label  : 异常类型标签 (如 sync, irq, fiq, error)
 */

.macro kernel_ventry, el:req, ht:req, regsize:req, label:req
    .align 7                          // 强制 128 字节对齐 (2^7 = 128)
                                      // ARM64 硬件规范要求每个异常向量入口必须严格占据 128 字节

.Lventry_start\@:                     // 定义局部标签记录当前入口起始地址

    .if    \el == 0                      // 仅当异常来自用户态 EL0 时,才执行以下代码块
    /*
     * ========== 异常来自用户态 EL0  ==========
     * 这条分支指令必须是 EL0 向量表入口的第一条指令。
     * 在 KPTI (内核页表隔离) 机制下,Trampoline (蹦床) 向量表会故意跳过这条指令,
     * 从而触发下面的清理操作。
     */

    b    .Lskip_tramp_vectors_cleanup\@  // 正常进入向量表时,直接跳过下面的清理代码
    .if    \regsize == 64                // 如果是 64 位用户态
    mrs    x30, tpidrro_el0              // 从只读线程 ID 寄存器读取值到 x30 (用于 KPTI 恢复)
    msr    tpidrro_el0, xzr              // 将 tpidrro_el0 清零,防止用户态遗留的恶意数据干扰内核
    .else                             // 如果是 32 位兼容模式
    mov    x30, xzr                      // 直接将 x30 清零
    .endif
.Lskip_tramp_vectors_cleanup\@:       // 正常流程或 Trampoline 清理完毕后的继续执行点
    .endif                            // 结束 EL0 条件编译

    /* 
     * ========== 核心精华:无损栈溢出检测机制 ==========
     * 此时通用寄存器尚未保存到栈中,因此绝对不能破坏任何 GPR 来检测栈是否溢出。
     * 内核利用加减法数学运算,巧妙地在不破坏 sp 和 x0 的前提下完成了检测。
     */

    sub    sp, sp, 
#PT_REGS_SIZE         // 在内核栈上预留出 pt_regs 结构体大小的空间
                                      //       sp' = sp - sizoef(struct pt_regs)


    add    sp, sp, x0                    // 步骤1: sp'' = sp' + x0
    sub    x0, sp, x0                    // 步骤2: x0' = sp'' - x0 = (sp' + x0) - x0 = sp'
                                      //       x0' 现在保存了 sp'

    tbnz    x0, #THREAD_SHIFT, 0f     // 步骤3: 测试恢复出的原 sp' 的第 THREAD_SHIFT 位
                                      // 内核栈是 THREAD_SIZE 对齐的,未溢出时该位必为 0

                                      // 如果不为 0 (tbnz 触发),说明栈溢出了,跳转到标签 '0' 进行紧急处理


    sub    x0, sp, x0                    // 步骤4: x0'' = sp'' - x0' = (sp' + x0) - sp' = x0 
                                      //       x0'' 恢复了原来的 x0

    sub    sp, sp, x0                    // 步骤5: sp''' = sp'' - x0'' = (sp' + x0) - x0 = sp'
                                      //       sp'' 恢复了原来的值 sp'

    b    el\el\ht\()_\regsize\()_\label // 栈未溢出,直接跳转到具体的 C 语言处理函数

0
:                                    // 【紧急逃生通道】如果上面的 tbnz 发现栈溢出,跳转到这里

    /*
     * 既然栈已经溢出,或者已经在溢出栈上再次发生异常,
     * 我们就不打算返回用户态了,因此可以随意破坏 (clobber) EL0 寄存器来腾出通用寄存器使用。
     */


    /* 将当前的 sp (即原 sp 减去 PT_REGS_SIZE 的值) 暂存到系统寄存器 tpidr_el0 中 */

                                      // sp'' = sp' + x0

                                      // x0' = sp'

    msr    tpidr_el0, x0                 // tpidr_el0 = x0'

    /* 通过数学运算恢复 x0 的原始值,并暂存到 tpidrro_el0 中 */

    sub    x0, sp, x0                    // x0'' = sp'' - x0' = (sp' + x0) - sp' = x0
    msr    tpidrro_el0, x0               // tpidrro_el0 = x0'' = x0

    /* 强制将栈指针切换到当前 CPU 专属的安全溢出栈 (overflow stack) 的顶部 */

    adr_this_cpu sp, overflow_stack + OVERFLOW_STACK_SIZE, x0 // sp 更新,SP

    /*
     * 检查我们是否已经处于溢出栈上。
     * 这种情况可能发生在内核 panic() 后重新开启中断的极端恶劣情况下。
     */

    mrs    x0, tpidr_el0                 // X0 = sp'
    sub    x0, sp, x0                    // X0' = SP - X0 = SP - sp'
    tst    x0, #~(OVERFLOW_STACK_SIZE - 1) // 测试差值是否在溢出栈的合法范围内
    b.ne    __bad_stack                 // 如果不在范围内 (说明溢出栈也溢出了),跳转到 __bad_stack 报错死机

    /* 如果差值合法,说明这是第一次栈溢出,恢复 sp/x0 并继续执行 */

    sub    sp, sp, x0                    // SP' = SP - X0' = SP - (SP - sp') = sp'
    mrs    x0, tpidrro_el0               // X0 = x0
                                      // 此时 SP 和 X0 都已经恢复

    b    el\el\ht\()_\regsize\()_\label // 跳转到具体的异常处理函数

.org .Lventry_start\@ + 128           // 【防御性校验】告诉汇编器:当前宏生成的代码必须精确填充到起始地址往后 128 字节
                                      // 如果代码超过 128 字节,汇编器会直接报错,确保严格符合硬件规范

    .endm                             // 宏定义结束

b el\el\ht\()_\regsize\()_\label  会跳转具体的异常处理 C 语言函数

.macro entry_handler el:req, ht:req, regsize:req, label:req
SYM_CODE_START_LOCAL
(el\el\ht\()_\regsize\()_\label) // 生成汇编函数入口(如 el1h_64_irq)
    kernel_entry \el, \regsize                       // 保存所有寄存器到现场(构建 pt_regs)
    mov    x0, sp                                       // 将栈指针作为第一个参数传给 C 函数
    bl    el\el\ht\()_\regsize\()_\label\()_handler    // 调用对应的 C 语言处理函数(如 el1h_64_irq_handler)
    .if \el == 0
    b    ret_to_user                                  // 若来自用户态(EL0),返回用户态
    .else
    b    ret_to_kernel                                // 若来自内核态(EL1),返回内核态
    .endif
SYM_CODE_END(el\el\ht\()_\regsize\()_\label)         // 标记函数结束
    .endm

那么 IRQ 处理的函数如下

向量表入口
IRQ 处理函数
kernel_ventry 1, t, 64, irq
el1t_64_irq_handler
kernel_ventry 1, h, 64, irq
el1h_64_irq_handler
kernel_ventry 0, t, 64, irq
el0t_64_irq_handler
kernel_ventry 0, t, 32, irq
el0t_32_irq_handler

最后统一使用 el0_interrupt 和 el1_interrupt() 两个函数处理

asmlinkage void noinstr el1h_64_irq_handler(struct pt_regs *regs)
{
    el1_interrupt(regs, handle_arch_irq);
}

asmlinkage void noinstr el0t_64_irq_handler(struct pt_regs *regs)
{
    __el0_irq_handler_common(regs);
}

asmlinkage void noinstr el0t_32_irq_handler(struct pt_regs *regs)
{
    __el0_irq_handler_common(regs);
}

static
 void noinstr __el0_irq_handler_common(struct pt_regs *regs)
{
    el0_interrupt(regs, handle_arch_irq);
}

最后统一使用 do_interrupt_handler() 处理 EL0 和 EL1 的中断

static __always_inline void __el1_irq(struct pt_regs *regs,
                      void
 (*handler)(struct pt_regs *))
{
    irqentry_state_t
 state;

    state = enter_from_kernel_mode(regs);

    irq_enter_rcu();
    do_interrupt_handler(regs, handler);
    irq_exit_rcu();

    exit_to_kernel_mode(regs, state);
}
static
 void noinstr el1_interrupt(struct pt_regs *regs,
                  void
 (*handler)(struct pt_regs *))

{
    write_sysreg(DAIF_PROCCTX_NOIRQ, daif);

    if
 (IS_ENABLED(CONFIG_ARM64_PSEUDO_NMI) && regs_irqs_disabled(regs))
        __el1_pnmi(regs, handler);
    else

        __el1_irq(regs, handler);
}

static
 void noinstr el0_interrupt(struct pt_regs *regs,
                  void
 (*handler)(struct pt_regs *))

{
    arm64_enter_from_user_mode(regs);

    write_sysreg(DAIF_PROCCTX_NOIRQ, daif);

    if
 (regs->pc & BIT(55))
        arm64_apply_bp_hardening();

    irq_enter_rcu();
    do_interrupt_handler(regs, handler);
    irq_exit_rcu();

    arm64_exit_to_user_mode(regs);
}

do_interrupt_handler() 函数调用传入的 handler - handle_arch_irq

static void do_interrupt_handler(struct pt_regs *regs,
                 void
 (*handler)(struct pt_regs *))

{
struct pt_regs *old_regs =
 set_irq_regs(regs);

    if
 (on_thread_stack())
        call_on_irq_stack(regs, handler);
    else

        handler(regs);

    set_irq_regs(old_regs);
}

handle_arch_irq 是一个函数指针,set_handle_irq() 函数可以注册 IRQ 处理的 handler

void (*handle_arch_irq)(struct pt_regs *) __ro_after_init = default_handle_irq;
void
 (*handle_arch_fiq)(struct pt_regs *) __ro_after_init = default_handle_fiq;

int
 __init set_handle_irq(void (*handle_irq)(struct pt_regs *))
{
    if
 (handle_arch_irq != default_handle_irq)
        return
 -EBUSY;

    handle_arch_irq = handle_irq;
    pr_info("Root IRQ handler: %ps\n", handle_irq);
    return
 0;
}

3. 中断域与 IRQ 派发逻辑

ARM64 最标准、最核心的 IRQ 驱动是 GIC 驱动,但具体的驱动选择是由底层硬件设备树决定的

以 GIC 驱动为例,分析 IRQ 派发流程

GIC 驱动初始化时,调用 set_handle_irq() 函数注册 gic_handle_irq

static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
{
    if
 (unlikely(gic_supports_nmi() && !interrupts_enabled(regs)))
        __gic_handle_irq_from_irqsoff(regs);
    else

        __gic_handle_irq_from_irqson(regs); // 通常的分支
}

__gic_handle_irq_from_irqson() 函数的逻辑

  • • 读取中断号并判断是否为 NMI;
  • • 若是 NMI,则进入 NMI 上下文并调用 NMI 处理函数;
  • • 若启用了优先级屏蔽,设置 PMR 并重新使能中断以允许更高优先级打断;
  • • 若不是 NMI,调用普通 IRQ 处理函数
static void __gic_handle_irq_from_irqson(struct pt_regs *regs)
{
    bool
 is_nmi;
    u32 irqnr;

    irqnr = gic_read_iar();             // 获得当前待处理的中断号
                                        // 将中断从 Pending 推进到 Active 状态

    is_nmi = gic_rpr_is_nmi_prio();

    if
 (is_nmi) {
        nmi_enter();
        __gic_handle_nmi(irqnr, regs);
        nmi_exit();
    }

    if
 (gic_prio_masking_enabled()) {  // 启用了 GIC 优先级屏蔽(priority masking)特性
        gic_pmr_mask_irqs();           // 修改 GIC 的 PMR 寄存器
                                       // 允许比当前中断优先级更高的中断打断自己

        gic_arch_enable_irqs();        // 重新打开中断总开关
    }

    if
 (!is_nmi)
        __gic_handle_irq(irqnr, regs); // 处理该中断
}

__gic_handle_irq() 函数继续把从 GIC 获取的硬件中断号交给内核的 IRQ 子系统处理

static void __gic_handle_irq(u32 irqnr, struct pt_regs *regs)
{
    if
 (gic_irqnr_is_special(irqnr))    // 过滤特殊中断号
        return
;

    gic_complete_ack(irqnr);            // 完成硬件 ACK,通知 GIC 可以更新内部状态
                                        // 允许更高优先级的中断来抢占我

    if
 (generic_handle_domain_irq(gic_data.domain, irqnr)) {
        WARN_ONCE(true, "Unexpected interrupt (irqnr %u)\n", irqnr);
        gic_deactivate_unhandled(irqnr);
    }
}

generic_handle_domain_irq() 函数主要职责:

  • • 把硬件 irqnr 转为 Linux 的虚拟 IRQ(映射/域逻辑)
  • • 调用注册在该虚拟 IRQ 上的处理程序(驱动层的中断处理函数)
int generic_handle_domain_irq(struct irq_domain *domain, unsigned int hwirq)
{
    return
 handle_irq_desc(irq_resolve_mapping(domain, hwirq));
}

handle_irq_desc() 函数最后调用 IRQ 上半部处理函数

static inline void generic_handle_irq_desc(struct irq_desc *desc)
{
    desc->handle_irq(desc);
}

int
 handle_irq_desc(struct irq_desc *desc)
{
struct irq_data *data;


    if
 (!desc)
        return
 -EINVAL;

    data = irq_desc_get_irq_data(desc);
    if
 (WARN_ON_ONCE(!in_hardirq() && irqd_is_handle_enforce_irqctx(data)))
        return
 -EPERM;

    generic_handle_irq_desc(desc); // 触发设备驱动的上半部
    return
 0;
}

物理 IRQ(hwirq)与 Linux 虚拟 IRQ(virq)之间的映射,是通过 irq_domain(中断域)机制建立的

irq_desc {} 对象表示一个虚拟 IRQ,所有已经分配的虚拟 IRQ 保存在 space_irqs 对象中

irq_domain 是对底层中断控制器的软件抽象:revmap 和 revmap_tree 中保存了 hwirq ==> irq 的映射

  • • hwirq 小于 revmap_size 保存在 revmap 数组中
  • • 更大的 hwirq 保存在 revmap_tree 树结构中

通过 revmap 和 revmap_tree 可以找到 hwirq 对应的虚拟 IRQ

在现代 SoC 中,irq_domain 不是孤立的,而是呈树状层级结构,例如,GPIO 控制器的 irq_domain 会通过 parent 指针挂载到 GIC 的 irq_domain 下

gpio5: gpio@20a8000 {
    compatible = "fsl,imx6ul-gpio";
    reg = <0x020a8000 0x4000>;
    interrupts = <GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>; // 中断输出线,物理连接到了 GIC 的第 74 号 SPI 中断线上
    interrupt-parent = <&gic>;  // 明确声明父级是 GIC
    gpio-controller;
    #gpio-cells = <2>;

    interrupt-controller;       // 声明自己也是一个中断控制器
    #interrupt-cells = <2>;

};

__irq_resolve_mapping() 函数用于找到物理 hwirq 对应的虚拟 IRQ 及 irq_desc 对象

struct irq_desc *__irq_resolve_mapping(struct irq_domain *domain,
                       irq_hw_number_t
 hwirq,
                       unsigned
 int *irq)
{

struct irq_desc *desc =
 NULL;
struct irq_data *data;


    /* Look for default domain if necessary */

    if
 (domain == NULL)
        domain = irq_default_domain;
    if
 (domain == NULL)
        return
 desc;

    if
 (irq_domain_is_nomap(domain)) { // hwirq == virq
        if
 (hwirq < domain->hwirq_max) {
            data = irq_domain_get_irq_data(domain, hwirq);
            if
 (data && data->hwirq == hwirq)
                desc = irq_data_to_desc(data);
            if
 (irq && desc)
                *irq = hwirq;
        }

        return
 desc;
    }

    rcu_read_lock();
    /* Check if the hwirq is in the linear revmap. */

    if
 (hwirq < domain->revmap_size)
        data = rcu_dereference(domain->revmap[hwirq]);
    else

        data = radix_tree_lookup(&domain->revmap_tree, hwirq);

    if
 (likely(data)) {
        desc = irq_data_to_desc(data);
        if
 (irq)
            *irq = data->irq;
    }

    rcu_read_unlock();
    return
 desc;
}

4. IRQ 处理函数上半部与下半部

在 Linux 内核驱动开发中,中断处理的核心原则是快进快出

由于硬件中断发生时 CPU 处于中断上下文,此时系统关闭了抢占甚至屏蔽了同级中断,任何耗时操作都会导致系统响应延迟甚至死锁

为了解决这一矛盾,Linux 将中断处理逻辑拆分为两个阶段

  1. 1. 上半部(Top Half / Hard IRQ):负责紧急且不可阻塞的硬件交互
  2. 2. 下半部(Bottom Half):负责耗时、可阻塞或复杂的业务逻辑

把耗时的、可延后或可被并行处理的工作从上半部移走,减少中断关闭时间,提升系统响应

随着内核版本的演进,下半部的实现机制已从传统的 Softirq/Tasklet 逐渐向 Workqueue 和 Threaded IRQ 迁移,理解它们的执行上下文差异是编写高质量驱动的关键

特性
上半部
下半部 (Softirq/Tasklet)
下半部 (Workqueue/IRQ Thread)
执行上下文
中断上下文
软中断上下文
进程上下文
能否睡眠
绝对不能
绝对不能
可以
能否被抢占
调度方式
硬件触发,立即执行
raise_softirq / tasklet_schedule
schedule_work / 内核线程调度器

为了彻底解决复杂中断处理带来的实时性问题,Linux 引入了 Threaded IRQ(线程化中断) 机制,通过直接调用 request_threaded_irq(),开发者可以将中断处理完美地一分为二

  • • handler 是上半部
  • • thread_fn 是下半部,在专属的内核线程上下文执行

request_irq() 函数也是 request_threaded_irq() 函数的封装

extern int __must_check
request_threaded_irq
(unsigned int irq, irq_handler_t handler,
             irq_handler_t
 thread_fn,
             unsigned
 long flags, const char *name, void *dev)
;

static
 inline int __must_check
request_irq
(unsigned int irq, irq_handler_t handler, unsigned long flags,
        const
 char *name, void *dev)

{
    return
 request_threaded_irq(irq, handler, NULL, flags | IRQF_COND_ONESHOT, name, dev);
}

设置的 handler 和 thread_fn 封装成 irqaction {} 对象,添加到 irq_desc {} 对象中

handle_irq_event_percpu() 函数会遍历 irq_desc 对象上 action 链表上的所有 irqaction 对象,执行 handler 和 thread_fn

irqreturn_t handle_irq_event_percpu(struct irq_desc *desc)
{
    irqreturn_t
 retval;

    retval = __handle_irq_event_percpu(desc);

    add_interrupt_randomness(desc->irq_data.irq);

    if
 (!irq_settings_no_debug(desc))
        note_interrupt(desc, retval);
    return
 retval;
}

irqreturn_t
 __handle_irq_event_percpu(struct irq_desc *desc)
{
    irqreturn_t
 retval = IRQ_NONE;
    unsigned
 int irq = desc->irq_data.irq;
struct irqaction *action;


    record_irq_time(desc);

    for_each_action_of_desc(desc, action) { // 遍历该中断描述符下的所有 irqaction 链表
        irqreturn_t
 res;

        /*
         * If this IRQ would be threaded under force_irqthreads, mark it so.
         */

        if
 (irq_settings_can_thread(desc) &&
            !(action->flags & (IRQF_NO_THREAD | IRQF_PERCPU | IRQF_ONESHOT)))
            lockdep_hardirq_threaded();

        trace_irq_handler_entry(irq, action);

        if
 (static_branch_unlikely(&irqhandler_duration_check_enabled)) {
            u64 ts_start = local_clock();

            res = action->handler(irq, action->dev_id);
            irqhandler_duration_check(ts_start, irq, action);
        } else {
            res = action->handler(irq, action->dev_id); // 处理上半部
        }

        trace_irq_handler_exit(irq, action, res);

        if
 (WARN_ONCE(!irqs_disabled(),"irq %u handler %pS enabled interrupts\n",
                  irq, action->handler))
            local_irq_disable();

        switch
 (res) {
        case
 IRQ_WAKE_THREAD:
            /*
             * Catch drivers which return WAKE_THREAD but
             * did not set up a thread function
             */

            if
 (unlikely(!action->thread_fn)) {
                warn_no_thread(irq, action);
                break
;
            }

            __irq_wake_thread(desc, action); // 通知线程处理下半部
            break
;

        default
:
            break
;
        }

        retval |= res;
    }

    return
 retval;
}

devm_request_threaded_irq() 函数在 request_threaded_irq 的基础上,引入了内核的 设备资源管理机制,是更推荐的 API

  • • 自动生命周期管理:它将中断资源挂载到设备(device)的资源链表中。当驱动卸载或探测失败时,内核会自动调用 free_irq,无需开发者手动清理
  • • 防止资源泄漏:彻底杜绝了因错误路径遗漏 free_irq 而导致的系统崩溃或中断占用问题
extern int __must_check
devm_request_threaded_irq
(struct device *dev, unsigned int irq,
              irq_handler_t
 handler, irq_handler_t thread_fn,
              unsigned
 long irqflags, const char *devname,
              void
 *dev_id)
;

static
 inline int __must_check
devm_request_irq
(struct device *dev, unsigned int irq, irq_handler_t handler,
         unsigned
 long irqflags, const char *devname, void *dev_id)

{
    return
 devm_request_threaded_irq(dev, irq, handler, NULL, irqflags | IRQF_COND_ONESHOT,
                     devname, dev_id);
}

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-08-21 23:45:56 HTTP/2.0 GET : https://f.mffb.com.cn/a/507229.html
  2. 运行时间 : 0.254964s [ 吞吐率:3.92req/s ] 内存消耗:4,821.07kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=50dba5d1ca0c65f5ec2134ffaa2b53eb
  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.001219s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.001717s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000749s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000725s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.001229s ]
  6. SELECT * FROM `set` [ RunTime:0.000615s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.001362s ]
  8. SELECT * FROM `article` WHERE `id` = 507229 LIMIT 1 [ RunTime:0.001199s ]
  9. UPDATE `article` SET `lasttime` = 1787327156 WHERE `id` = 507229 [ RunTime:0.082757s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 67 LIMIT 1 [ RunTime:0.000953s ]
  11. SELECT * FROM `article` WHERE `id` < 507229 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.001329s ]
  12. SELECT * FROM `article` WHERE `id` > 507229 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.001050s ]
  13. SELECT * FROM `article` WHERE `id` < 507229 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.001875s ]
  14. SELECT * FROM `article` WHERE `id` < 507229 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.001996s ]
  15. SELECT * FROM `article` WHERE `id` < 507229 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.002373s ]
0.258622s