当前位置:首页>Linux>Linux 内核互斥技术

Linux 内核互斥技术

  • 2026-08-18 23:12:05
Linux 内核互斥技术

Linux 内核运行环境天然具有高度并发特征。多核 SMP 系统、内核抢占、硬件中断、软中断、工作队列、内核线程以及用户态系统调用路径,都可能在相近时间访问同一份共享数据。若缺少合适的同步机制,就会出现竞态条件、数据不一致、引用计数错误、链表损坏、设备状态异常等问题。

内核互斥技术的核心目标,是在并发执行环境中保护临界区,使共享数据在多个执行路径之间保持一致。所谓临界区,是指访问共享资源的一段代码区域。进入临界区前需要获得某种排他能力,离开临界区后释放该能力,从而避免多个执行路径同时修改共享状态。

Linux 内核提供了多层次的互斥与同步机制:

  • 原子操作:用于单个变量或标志位的原子修改,开销最低。
  • 自旋锁:适合短临界区,不可睡眠,常用于中断上下文与高性能路径。
  • 互斥锁:适合进程上下文中的长临界区,允许睡眠,使用更灵活。
  • 信号量:计数型同步原语,可控制并发访问数量,也可用于事件同步。
  • 死锁检测:以 lockdep 为代表,用于发现锁依赖错误与潜在死锁。

不同机制的适用场景、限制条件和错误模式差异很大。选错同步原语,轻则引发性能下降,重则导致死锁、内核挂死、数据损坏。下面从并发模型出发,系统梳理 Linux 内核中的互斥技术。


一、并发执行模型与临界区保护

内核中的共享数据可能被多种执行路径访问:

                 +------------------+                 |    共享数据结构    |                 +------------------+                    ^      ^      ^                    |      |      |             进程上下文  中断上下文  软中断/工作队列

常见并发来源包括:

  1. 多核并行:多个 CPU 同时执行同一段内核代码。
  2. 内核抢占:进程上下文中的内核代码可能被调度器抢占。
  3. 硬件中断:当前 CPU 正在执行普通内核代码时,中断处理函数可能访问同一数据。
  4. 软中断与 tasklet:网络收发包、块设备完成处理等路径可能异步执行。
  5. 工作队列:workqueue 中的工作函数运行在进程上下文,但仍可能与其它线程并发。
  6. 内核线程:例如 writeback、kworker、rcu 等线程可能访问全局或设备私有数据。

一个典型的竞态例子是计数器自增:

counter++;

在高级语言中看似是一行代码,在机器指令层面通常包含:

读取 counter加 1写回 counter

如果两个 CPU 同时执行上述过程,就可能出现更新丢失:

CPU0: 读取 counter = 10CPU1: 读取 counter = 10CPU0: 计算 10 + 1 = 11CPU1: 计算 10 + 1 = 11CPU0: 写回 counter = 11CPU1: 写回 counter = 11

最终结果是只增加了一次,而不是两次。此类问题无法通过简单阅读代码发现,往往只在高并发、特定负载或特定硬件平台上偶发。

因此,内核互斥机制需要解决三个关键问题:

  1. 原子性:临界区内的操作不可被其它执行路径打断。
  2. 可见性:一个 CPU 对共享数据的修改,能够被其它 CPU 正确观察到。
  3. 顺序性:多个内存访问之间保持预期顺序,避免编译器和 CPU 乱序导致逻辑错误。

二、原子操作:最轻量的原子性保证

2.1 原子操作的基本概念

原子操作是最轻量的并发保护手段。它通常用于单个整型变量、计数器、标志位或引用计数。原子操作保证某次读-改-写操作不会被其它 CPU 打断,也不会出现中间状态。

Linux 内核提供 atomic_tatomic64_tatomic_long_t 等类型,并提供一组体系结构相关的原子操作接口。

基本头文件:

#include <linux/atomic.h>

常见定义方式:

static atomic_t counter = ATOMIC_INIT(0);

常见操作:

atomic_inc(&counter);atomic_dec(&counter);atomic_add(4, &counter);atomic_sub(2, &counter);int value = atomic_read(&counter);atomic_set(&counter, 0);int old = atomic_cmpxchg(&counter, 01);

典型引用计数示例:

static atomic_t refcnt = ATOMIC_INIT(0);void obj_get(struct obj *o){atomic_inc(&o->refcnt);}void obj_put(struct obj *o){if (atomic_dec_and_test(&o->refcnt))        release_object(o);}

atomic_dec_and_test() 会将计数减一,并判断结果是否为 0。该操作整体是原子的,适合引用计数释放场景。

2.2 原子操作的实现方式

不同体系结构实现原子操作的方式不同:

  • x86 平台通常使用带 LOCK 前缀的指令,例如 LOCK INCLOCK ADDLOCK CMPXCHG
  • ARM、RISC-V 等平台通常使用 LL/SC,即 Load-Linked / Store-Conditional,或者 LSE、AMO 等原子指令。
  • 部分架构会结合本地中断屏蔽或特殊屏障保证原子性。

内核将体系结构差异封装在统一 API 中,驱动和子系统代码通常只需使用标准接口。

2.3 atomic_read 与 atomic_set 的边界

需要特别注意的是,atomic_read() 与 atomic_set() 并不等同于“读-改-写”原子操作。它们通常对应 READ_ONCE() 与 WRITE_ONCE() 语义,主要保证单次访问不被撕裂,不构成完整的原子更新。

例如以下代码仍然存在竞态:

atomic_set(&counter, atomic_read(&counter) + 1);

因为读取、加一、写回三步不是整体原子的。正确写法应为:

atomic_inc(&counter);

或者:

atomic_add(1, &counter);

因此,原子操作适合保护单个变量的单次修改,不适合保护由多个字段组成的复杂数据结构。

2.4 原子位操作

除了整型原子变量,内核还提供原子位操作接口,常用于标志位、位图和简单状态机。

头文件:

#include <linux/bitops.h>

常见接口:

unsigned long flags;set_bit(0, &flags);clear_bit(0, &flags);change_bit(0, &flags);if (test_bit(0, &flags)) {/* bit 0 is set */}if (test_and_set_bit(0, &flags)) {/* bit 0 was already set */}

test_and_set_bit() 会原子地测试某一位并设置为 1,返回旧值。该操作可用于实现简单的一次性锁标志:

if (test_and_set_bit(DEVICE_BUSY, &dev->flags))return -EBUSY;/* 进入独占处理流程 */clear_bit(DEVICE_BUSY, &dev->flags);

原子位操作也有非原子版本,例如 __set_bit()__clear_bit()__test_and_set_bit()。这些接口性能更高,但只能在调用者已经保证不会并发访问时使用。

2.5 原子操作与内存顺序

原子操作保证变量本身的原子性,但不一定自动提供完整的内存屏障。若原子变量还承担发布、通知、状态切换等职责,需要考虑内存顺序问题。

例如:

data = 100;atomic_set(&ready, 1);

如果另一个 CPU 通过 atomic_read(&ready) 判断是否读取 data,在某些体系结构上可能看到 ready == 1,但尚未看到 data == 100。此时需要配合内存屏障,或使用更明确的同步机制。

常见做法包括:

smp_wmb();atomic_set(&ready, 1);

以及读取侧:

if (atomic_read(&ready)) {    smp_rmb();    value = data;}

在复杂发布/订阅场景中,通常更推荐使用 RCU、互斥锁、completion、wait queue 等更高层次同步机制,而不是手工组合原子变量和内存屏障。

2.6 原子操作的适用场景

适合:

  • 引用计数。
  • 简单统计计数。
  • 单个状态标志。
  • 无锁算法中的 CAS 操作。
  • 只需要保护单个变量,不涉及多字段一致性。

不适合:

  • 保护链表、红黑树、复杂结构体。
  • 同时修改多个字段并要求一致可见。
  • 临界区内需要睡眠、分配内存、拷贝用户数据。
  • 需要严格发布/订阅语义但缺少完整内存屏障设计。

原子操作的优势是开销低、使用简单;局限是表达能力有限。一旦临界区涉及多个共享字段,通常就需要锁。


三、自旋锁:短临界区的忙等待保护

3.1 自旋锁的基本原理

自旋锁是内核中最常用的互斥机制之一。若一个执行路径持有自旋锁,其它试图获取该锁的 CPU 会原地自旋等待,直到锁被释放。

自旋锁适合保护非常短的临界区。由于等待者不会睡眠,而是持续占用 CPU,因此临界区必须尽快执行完毕。

基本类型:

#include <linux/spinlock.h>DEFINE_SPINLOCK(my_lock);

动态初始化:

spinlock_t my_lock;spin_lock_init(&my_lock);

加锁与解锁:

spin_lock(&my_lock);/* 临界区 */spin_unlock(&my_lock);

3.2 自旋锁与本地抢占

在可抢占内核中,spin_lock() 会禁止本地 CPU 抢占。这样可以防止当前持有锁的执行路径被调度走,从而延长锁持有时间。

在 SMP 系统中,自旋锁还提供跨 CPU 的互斥能力。在单核非抢占内核中,自旋锁可能被编译为非常轻量的操作,甚至接近空操作,但代码仍必须遵守自旋锁语义。

3.3 自旋锁使用规则

持有自旋锁期间,不能执行任何可能睡眠的操作。常见禁止行为包括:

  • 调用 kmalloc(GFP_KERNEL)
  • 调用 copy_from_user() 或 copy_to_user()
  • 调用 schedule()
  • 获取 mutex。
  • 执行 down() 获取信号量。
  • 等待 completion。
  • 访问用户空间页面并可能触发缺页睡眠。
  • 调用任何带有 might_sleep() 语义的函数。

如果临界区需要分配内存,通常应使用 GFP_ATOMIC

ptr = kmalloc(size, GFP_ATOMIC);

GFP_ATOMIC 分配不会睡眠,但成功率低于 GFP_KERNEL,因此应谨慎使用。

3.4 自旋锁与中断

自旋锁经常需要与中断处理共享数据。若进程上下文持有自旋锁时,本 CPU 发生中断,而中断处理函数又尝试获取同一把锁,就会形成死锁:

进程上下文:    spin_lock(&lock);    ---- 被中断 ----中断上下文:    spin_lock(&lock);   /* 永远等待 */

由于中断发生在同一 CPU,而进程上下文无法继续执行释放锁,因此形成死锁。

解决方式是获取锁时屏蔽本地中断:

unsigned long flags;spin_lock_irqsave(&my_lock, flags);/* 临界区 */spin_unlock_irqrestore(&my_lock, flags);

spin_lock_irqsave() 会保存当前中断状态并关闭本地中断;spin_unlock_irqrestore() 恢复之前的中断状态。

如果明确知道当前中断一定开启,并且只需要关闭中断而不需要保存状态,可以使用:

spin_lock_irq(&my_lock);spin_unlock_irq(&my_lock);

但 spin_lock_irqsave() 更通用,也更适合中断状态不确定的路径。

如果临界区只与软中断共享数据,可以使用:

spin_lock_bh(&my_lock);spin_unlock_bh(&my_lock);

spin_lock_bh() 会禁止本地软中断执行。

3.5 自旋锁死锁案例

自旋锁死锁常见形式包括:

1. 重复获取同一把锁

spin_lock(&lock);spin_lock(&lock); /* 死锁 */

Linux 内核中的普通自旋锁不可递归,同一执行路径重复获取同一把自旋锁会死锁。

2. ABBA 锁顺序死锁

CPU0:    lock A    lock BCPU1:    lock B    lock A

若 CPU0 已持有 A 并等待 B,CPU1 已持有 B 并等待 A,则双方永远无法继续执行。

3. 中断上下文与进程上下文共享锁但未屏蔽中断

进程上下文持有 lock本 CPU 中断触发中断处理函数尝试获取 lock进程上下文无法继续释放 lock

此类问题在中断驱动设备驱动中尤其常见。

3.6 读写自旋锁

内核还提供读写自旋锁:

DEFINE_RWLOCK(my_rwlock);read_lock(&my_rwlock);/* 读临界区 */read_unlock(&my_rwlock);write_lock(&my_rwlock);/* 写临界区 */write_unlock(&my_rwlock);

读写自旋锁允许多个读者并发访问,但写者独占。适合读多写少且临界区很短的场景。

不过,读写锁也可能带来写者饥饿、缓存行竞争等问题。若写操作频繁,或者临界区较长,读写锁未必优于普通自旋锁。

3.7 自旋锁适用场景

适合:

  • 中断上下文。
  • 软中断上下文。
  • 临界区极短。
  • 不能睡眠的路径。
  • 高频访问的小型数据结构。

不适合:

  • 临界区较长。
  • 临界区内需要睡眠。
  • 需要访问用户空间并可能缺页。
  • 需要复杂资源等待。
  • 持锁时间不可控。

自旋锁的核心原则是:短、快、不睡眠。


四、互斥锁:可睡眠的临界区保护

4.1 mutex 基本概念

Linux 内核中的 struct mutex 是一种可睡眠互斥锁。与自旋锁不同,若 mutex 已被其它任务持有,当前任务通常会进入睡眠状态,等待锁释放后被唤醒。

基本头文件:

#include <linux/mutex.h>

定义方式:

DEFINE_MUTEX(my_mutex);

动态初始化:

struct mutex my_mutex;mutex_init(&my_mutex);

加锁与解锁:

mutex_lock(&my_mutex);/* 可睡眠临界区 */mutex_unlock(&my_mutex);

可中断版本:

int ret;ret = mutex_lock_interruptible(&my_mutex);if (ret)return ret;/* 临界区 */mutex_unlock(&my_mutex);

mutex_lock_interruptible() 在等待锁时可被信号打断,返回非 0 值。系统调用路径中常用该接口,避免用户进程长时间处于不可中断状态。

4.2 mutex 的实现思路

mutex 通常包含快速路径和慢速路径:

mutex_lock()    |    +-- 快速路径:尝试原子获取锁    |    +-- 获取失败          |          +-- 乐观自旋:若锁持有者正在运行,可短暂自旋          |          +-- 仍无法获取:加入等待队列并睡眠

快速路径通常通过原子操作尝试获取锁。若失败,内核可能先进行乐观自旋,观察锁持有者是否正在运行。若持有者很快释放锁,等待者可以避免上下文切换。若短时间内无法获取,等待者会睡眠,从而避免浪费 CPU。

这种设计使 mutex 在长临界区场景下比自旋锁更合理。

4.3 mutex 使用规则

mutex 只能在进程上下文中使用。以下场景不能获取 mutex:

  • 硬件中断上下文。
  • 软中断上下文。
  • tasklet。
  • 持有自旋锁期间。
  • 关闭本地抢占期间。
  • 任何不能睡眠的执行路径。

错误示例:

spin_lock(&lock);mutex_lock(&mutex); /* 错误:持有自旋锁时不能获取 mutex */mutex_unlock(&mutex);spin_unlock(&lock);

mutex 也不支持递归加锁:

mutex_lock(&mutex);mutex_lock(&mutex); /* 死锁 */

此外,mutex 通常要求由获取锁的任务释放:

mutex_lock(&mutex);/* 临界区 */mutex_unlock(&mutex);

若由其它任务释放,可能触发调试告警或导致锁状态异常。

4.4 mutex 临界区可以做什么

由于 mutex 允许睡眠,临界区内可以执行更多操作:

mutex_lock(&dev->mutex);ptr = kmalloc(size, GFP_KERNEL);if (!ptr) {    mutex_unlock(&dev->mutex);return -ENOMEM;}ret = copy_from_user(ptr, user_ptr, size);if (ret) {    kfree(ptr);    mutex_unlock(&dev->mutex);return -EFAULT;}list_add_tail(&ptr->node, &dev->list);mutex_unlock(&dev->mutex);

这类代码不能放在自旋锁临界区中,因为 GFP_KERNEL 分配和用户态拷贝都可能睡眠。

4.5 mutex 死锁场景

mutex 死锁与自旋锁死锁在逻辑上类似,但由于 mutex 会睡眠,死锁往往表现为任务长时间阻塞,而不是 CPU 自旋占用。

常见场景:

1. 递归加锁

mutex_lock(&m);do_something();mutex_lock(&m); /* 死锁 */

若 do_something() 内部又获取同一把 mutex,就会死锁。

2. 锁顺序不一致

线程 A:    lock mutex1    lock mutex2线程 B:    lock mutex2    lock mutex1

3. 持锁等待资源,而资源释放依赖另一个持锁线程

例如:

线程 A 持有 mutex1,等待线程 B 完成某项工作线程 B 需要获取 mutex1 才能完成工作

这类等待关系会形成循环依赖。

4.6 mutex 与自旋锁的选择

临界区是否可能睡眠?        |        +-- 是 --> 使用 mutex        |        +-- 否              |              +-- 是否在中断/软中断上下文?              |        |              |        +-- 是 --> 使用自旋锁              |              +-- 临界区是否足够短?                       |                       +-- 是 --> 使用自旋锁                       |                       +-- 否 --> 重新设计或使用 mutex

一般原则:

  • 中断上下文:自旋锁。
  • 短临界区:自旋锁。
  • 长临界区:mutex。
  • 需要睡眠:mutex。
  • 持锁期间不能睡眠:自旋锁。
  • 拿不准时,不要盲目使用自旋锁掩盖设计问题。

五、信号量:计数型同步原语

5.1 信号量基本原理

Linux 内核中的信号量是一种计数型同步原语。它维护一个计数器,down() 操作减少计数,up() 操作增加计数。当计数为 0 时,down() 会睡眠等待,直到其它执行路径调用 up() 增加计数。

头文件:

#include <linux/semaphore.h>

初始化:

struct semaphore sem;sema_init(&sem, 1);

计数为 1 的信号量可作为二值信号量使用,功能类似互斥锁。但现代内核代码中,简单互斥更推荐使用 mutex。

5.2 down 与 up

基本操作:

down(&sem);/* 临界区或资源访问 */up(&sem);

可被信号打断的版本:

int ret;ret = down_interruptible(&sem);if (ret)return ret;/* 临界区或资源访问 */up(&sem);

down_interruptible() 在等待过程中若收到信号,会返回非 0 值。调用者必须处理该错误,不能继续进入临界区。

5.3 信号量的计数语义

信号量与 mutex 的重要区别在于计数。

若初始化计数为 N:

sema_init(&sem, N);

则最多允许 N 个执行路径同时进入临界区或访问资源。

例如,某硬件资源支持 4 个并发访问者:

sema_init(&sem, 4);down(&sem);use_hardware_resource();up(&sem);

这种计数语义是 mutex 不直接提供的。

5.4 信号量与中断上下文

down() 可能睡眠,因此不能在中断上下文、软中断上下文或持有自旋锁时调用。

up() 不会睡眠,可以在中断上下文中调用。例如:

static irqreturn_t dev_irq_handler(int irq, void *data){struct my_device *dev = data;/* 中断中释放资源计数 */    up(&dev->sem);return IRQ_HANDLED;}

这种模式可用于“中断释放资源、进程上下文等待资源”的同步场景。

5.5 信号量与 mutex 的差异

+--------------+-----------------------+-----------------------+| 特性         | mutex                 | semaphore             |+--------------+-----------------------+-----------------------+| 是否可睡眠   | 是                    | 是                    || 是否计数     | 否,通常为二值互斥     | 是                    || 是否跟踪 owner | 是                  | 否                    || 是否适合简单互斥 | 推荐              | 不优先推荐            || 是否可在中断中 down | 否           | 否                    || 是否可在中断中 up   | 不适用         | 可以                  || 调试支持     | 较强                  | 相对较弱              |+--------------+-----------------------+-----------------------+

对于简单互斥,mutex 更合适。信号量更适合真正需要计数语义的场景。

5.6 信号量的适用场景

适合:

  • 控制并发访问数量。
  • 生产者/消费者模型中的资源计数。
  • 中断中 up()、进程上下文中 down() 的同步。
  • 需要允许多个执行路径同时访问有限资源。

不适合:

  • 简单二值互斥,优先使用 mutex。
  • 短临界区且不能睡眠,优先使用自旋锁。
  • 复杂事件等待,通常 completion 或 wait queue 更清晰。

六、死锁:形成条件与常见内核场景

6.1 死锁的基本条件

经典死锁通常需要同时满足四个条件:

  1. 互斥:资源一次只能被一个执行路径持有。
  2. 持有并等待:执行路径持有资源,同时等待其它资源。
  3. 不可剥夺:资源不能被强制收回,只能由持有者释放。
  4. 循环等待:多个执行路径形成环形等待关系。

内核中的锁机制基本满足前三个条件,因此防止死锁的关键通常是破坏循环等待。

6.2 ABBA 死锁模型

ABBA 死锁是最典型的循环等待:

CPU0:    lock A    lock BCPU1:    lock B    lock A

若执行顺序演化为:

CPU0: lock ACPU1: lock BCPU0: try lock B   --> 等待 CPU1CPU1: try lock A   --> 等待 CPU0

两个 CPU 都无法继续执行。

解决方法包括:

  • 所有代码路径按照相同顺序获取锁。
  • 将大锁拆分为小锁,减少交叉依赖。
  • 使用 trylock,失败后释放已有锁并重试。
  • 使用 RCU 或无锁结构减少锁依赖。
  • 通过 lockdep 提前发现依赖问题。

6.3 中断上下文死锁

中断上下文死锁通常不是 ABBA,而是同一 CPU 上的重入等待:

进程上下文持有 lock硬件中断进入同一 CPU中断处理函数尝试获取 lock进程上下文无法继续执行

解决方式:

  • 共享数据若同时被中断访问,进程上下文获取锁时使用 spin_lock_irqsave()
  • 中断处理函数中使用 spin_lock() 或 spin_lock_irqsave(),视具体路径而定。
  • 避免在持锁期间触发可能访问同一数据的回调。

6.4 自旋锁与 mutex 混用死锁

错误地在自旋锁临界区内获取 mutex,会造成严重问题:

spin_lock(&lock);mutex_lock(&mutex);mutex_unlock(&mutex);spin_unlock(&lock);

问题在于:

  • 自旋锁持有期间不能睡眠。
  • mutex_lock() 可能睡眠。
  • 若 mutex 不可用,当前路径会尝试睡眠,但自旋锁仍被持有。
  • 其它 CPU 或本 CPU 其它路径可能永远无法获取自旋锁,导致系统异常。

正确做法是调整锁层次:

mutex_lock(&mutex);spin_lock(&lock);/* 短临界区 */spin_unlock(&lock);mutex_unlock(&mutex);

或者重新设计数据结构,避免同时持有两类锁。

6.5 睡眠函数误用于原子上下文

严格来说,这类问题不一定是死锁,但会导致严重内核告警或系统挂起。

典型错误:

spin_lock(&lock);ptr = kmalloc(size, GFP_KERNEL); /* 错误 */spin_unlock(&lock);

GFP_KERNEL 分配可能睡眠,而自旋锁临界区不允许睡眠。正确写法:

spin_lock(&lock);ptr = kmalloc(size, GFP_ATOMIC);spin_unlock(&lock);

或者:

ptr = kmalloc(size, GFP_KERNEL);if (!ptr)return -ENOMEM;spin_lock(&lock);/* 使用 ptr */spin_unlock(&lock);

七、死锁检测:lockdep 与调试手段

7.1 lockdep 的作用

lockdep 是 Linux 内核中的锁依赖检测工具。它不会等到系统真正挂死才报告问题,而是在运行时分析锁的获取顺序,提前发现潜在循环依赖。

lockdep 主要检测:

  • 循环锁依赖,即潜在 ABBA 死锁。
  • 中断上下文锁使用错误。
  • 硬中断、软中断、进程上下文之间的锁状态不一致。
  • 某些非法锁状态。
  • 与 might_sleep() 相关的原子上下文睡眠风险,通常配合其它调试选项使用。

lockdep 的核心思想是维护锁类和依赖图。每次获取锁时,lockdep 会记录当前已持有的锁与新获取锁之间的依赖关系。若发现依赖图中出现环,就会输出告警。

7.2 相关内核配置

常见配置项包括:

CONFIG_LOCKDEP=yCONFIG_PROVE_LOCKING=yCONFIG_DEBUG_LOCK_ALLOC=yCONFIG_DEBUG_MUTEXES=yCONFIG_DEBUG_ATOMIC_SLEEP=yCONFIG_LOCK_STAT=y

不同内核版本配置项名称可能略有差异,但核心思路一致。

其中:

  • CONFIG_LOCKDEP:启用锁依赖基础设施。
  • CONFIG_PROVE_LOCKING:启用锁证明和依赖检查。
  • CONFIG_DEBUG_LOCK_ALLOC:增强锁分配与初始化检查。
  • CONFIG_DEBUG_MUTEXES:增强 mutex 调试。
  • CONFIG_DEBUG_ATOMIC_SLEEP:检测原子上下文中调用可睡眠函数。
  • CONFIG_LOCK_STAT:提供锁竞争统计信息。

7.3 lockdep 报告的基本结构

lockdep 告警通常出现在内核日志中,形式类似:

WARNING: possible circular locking dependency detectedCPU0: lock A is already held, trying to lock BCPU1: lock B is already held, trying to lock Aexisting dependency chain:A -> BB -> A

实际输出会更复杂,通常包含:

  • 当前任务。
  • 当前 CPU。
  • 已持有锁。
  • 正在尝试获取的锁。
  • 依赖链。
  • 多个 CPU 的调用栈。
  • 中断状态信息。
  • 锁类型与锁地址。

解读 lockdep 报告时,需要重点关注:

  1. 哪两把锁形成循环依赖。
  2. 每条依赖链是在哪个函数中建立的。
  3. 是否涉及硬中断或软中断。
  4. 是否存在不同上下文使用同一把锁但状态不一致。
  5. 是否存在动态分配锁未正确分类的问题。

7.4 锁类与动态锁

lockdep 通过锁类识别锁。对于静态定义并正常初始化的锁,lockdep 通常可以自动建立合理锁类。

但对于动态分配的锁,例如每个设备对象都包含一把锁,若所有锁使用相同初始化方式,lockdep 可能将它们视为同一类锁。这可能导致误报,也可能掩盖真实问题。

因此,动态锁应设置独立的 lock class key,使 lockdep 能够区分不同对象中的锁。

常见思路:

为每类动态锁定义唯一 lock_class_key初始化锁后设置 lock class为锁命名,便于日志分析

例如,每个网络设备、块设备、字符设备都可能有自己的锁。若所有设备锁共享一个锁类,lockdep 可能认为设备 A 的锁和设备 B 的锁是同一把锁,从而产生虚假依赖。

7.5 使用 might_sleep 与 lockdep_assert_held

内核代码中常用 might_sleep() 标注可能睡眠的函数。若该函数在原子上下文中被调用,调试内核可能输出告警。

示例:

void my_sleepable_function(void){    might_sleep();    mutex_lock(&my_mutex);/* ... */    mutex_unlock(&my_mutex);}

lockdep_assert_held() 可用于断言当前上下文已持有某把锁:

void update_state(struct device *dev){    lockdep_assert_held(&dev->lock);    dev->state = NEW_STATE;}

这类注解不会改变正常运行逻辑,但能在调试阶段提前暴露锁使用错误。

7.6 实际死锁与潜在死锁

lockdep 主要检测潜在死锁,而不是必须等到死锁实际发生。

真正发生死锁时,系统可能表现为:

  • 某个任务长时间阻塞。
  • 某个 CPU 长时间自旋。
  • 系统部分功能无响应。
  • 控制台无输出。
  • 触发 hung task 或 soft lockup 告警。

常见辅助检测机制:

  1. hung task detector 检测任务长时间处于不可中断睡眠状态,默认阈值通常为 120 秒。mutex 死锁、资源等待死锁可能触发该告警。

  2. soft lockup detector 检测 CPU 长时间在内态执行而未让出调度。自旋锁死循环、长时间持锁自旋可能触发该告警。

  3. hard lockup detector / NMI watchdog 检测 CPU 长时间不响应中断,通常意味着严重挂死。

  4. ftrace、bpftrace、perf 可用于追踪锁获取路径、函数调用关系和性能热点。

  5. lock_stat 可用于统计锁竞争、持有时间、等待时间等信息,适合分析性能问题。

7.7 死锁调试步骤

面对疑似死锁问题,建议按照以下步骤排查:

第一步:保留现场

保存以下信息:

  • dmesg 完整日志。
  • lockdep 告警。
  • hung task 告警。
  • soft lockup 告警。
  • /proc/lockdep 相关信息。
  • /proc/<pid>/stack,若任务仍可观察。
  • 复现负载、并发线程数、设备插拔顺序等触发条件。

第二步:确认锁类型

判断问题涉及:

  • 自旋锁。
  • mutex。
  • 信号量。
  • rwlock。
  • RCU。
  • 自定义等待队列。

不同锁类型的错误模式不同。

第三步:分析锁依赖

若 lockdep 输出循环依赖,需要整理依赖链:

路径 1:    lock A    lock B路径 2:    lock B    lock A

然后统一加锁顺序:

所有路径均先 lock A,再 lock B

若无法统一顺序,需要考虑 trylock 或重新设计数据结构。

第四步:检查中断上下文

重点确认:

  • 是否有中断处理函数访问该数据。
  • 进程上下文是否使用 spin_lock_irqsave()
  • 中断处理函数是否使用了正确的锁变体。
  • 是否存在软中断与进程上下文共享数据。

第五步:检查是否非法睡眠

若日志中出现类似:

BUG: sleeping function called from invalid context

需要检查是否在以下路径中调用了可睡眠函数:

  • 中断上下文。
  • 软中断上下文。
  • 自旋锁临界区。
  • 关闭抢占区域。
  • RCU 读临界区中不恰当睡眠。

第六步:缩小复现范围

死锁问题通常与并发时序有关。可通过以下方式提高复现率:

  • 增加并发压力。
  • 反复执行设备打开/关闭。
  • 反复加载/卸载模块。
  • 在关键路径插入延迟,放大竞态窗口。
  • 使用压力测试工具。
  • 启用调试内核。

第七步:修复后回归验证

修复死锁后,应进行多轮验证:

  • lockdep 无告警。
  • 压力测试无挂死。
  • 长时间运行无 hung task。
  • 多核环境验证。
  • 中断频繁触发场景验证。
  • 模块卸载/重载验证。

7.8 避免死锁的设计原则

  1. 固定锁获取顺序。 若多个锁经常同时出现,应定义全局锁层次。

  2. 减少锁持有时间。 临界区越短,竞争和死锁概率越低。

  3. 避免持锁等待。 尽量不要在持有锁时等待另一个锁、事件或资源。

  4. 谨慎使用嵌套锁。 嵌套越深,依赖越复杂。

  5. 区分中断上下文与进程上下文。 共享数据时,必须明确所有访问路径。

  6. 优先使用成熟同步设施。 例如 wait queue、completion、workqueue、RCU,而不是手工复杂状态机。

  7. 使用 lockdep 注解。 对关键锁使用断言和命名,提高可维护性。

  8. 不要为了消除告警而掩盖问题。 lockdep 告警通常说明真实设计缺陷,不应简单忽略。


八、互斥机制综合对比

+------------+----------+----------+------------+--------------+| 机制       | 是否睡眠 | 典型上下文 | 保护对象   | 典型场景      |+------------+----------+----------+------------+--------------+| 原子操作   | 否       | 任意     | 单个变量   | 计数、标志位  || 自旋锁     | 否       | 任意     | 短临界区   | 中断、快路径  || 互斥锁     | 是       | 进程上下文 | 长临界区 | 设备状态、文件操作 || 信号量     | 是       | 进程上下文 | 计数资源 | 资源数量控制 |+------------+----------+----------+------------+--------------+

更细化的对比:

+----------------+------------------+------------------+------------------+| 对比项         | 自旋锁           | mutex            | 信号量           |+----------------+------------------+------------------+------------------+| 等待方式       | 忙等待           | 睡眠等待         | 睡眠等待         || 是否可递归     | 否               | 否               | 否               || 是否允许中断中获取 | 是           | 否               | 否               || 是否允许中断中释放 | 是           | 否               | 可以 up          || 是否支持计数   | 否               | 否               | 是               || 是否跟踪持有者 | 否               | 是               | 否               || 是否适合长临界区 | 否             | 是               | 视场景而定       || 是否推荐用于简单互斥 | 视上下文 | 推荐             | 不优先推荐       |+----------------+------------------+------------------+------------------+

九、典型选型流程

是否只修改单个变量或标志位?        |        +-- 是 --> 考虑原子操作或原子位操作        |        +-- 否              |              当前是否在中断/软中断上下文?                    |                    +-- 是 --> 使用自旋锁                    |                    +-- 否                          |                          临界区是否可能睡眠?                                |                                +-- 是 --> 使用 mutex                                |                                +-- 否                                      |                                      临界区是否足够短?                                            |                                            +-- 是 --> 使用自旋锁                                            |                                            +-- 否 --> 使用 mutex 或重新设计

若需要控制并发访问数量,则考虑信号量:

资源最多允许 N 个并发访问者?        |        +-- 是 --> 使用计数信号量        |        +-- 否 --> 使用 mutex 或自旋锁

若只是等待某个事件完成,通常 completion 比信号量更直观:

等待一个一次性事件完成?        |        +-- 是 --> 考虑 completion        |        +-- 否 --> 根据资源类型选择锁或等待队列

十、实际开发中的常见错误与修正方式

10.1 在自旋锁中分配可睡眠内存

错误:

spin_lock(&lock);p = kmalloc(size, GFP_KERNEL);spin_unlock(&lock);

修正:

p = kmalloc(size, GFP_KERNEL);if (!p)return -ENOMEM;spin_lock(&lock);/* 使用 p */spin_unlock(&lock);

或者:

spin_lock(&lock);p = kmalloc(size, GFP_ATOMIC);spin_unlock(&lock);

10.2 在中断上下文中获取 mutex

错误:

static irqreturn_t handler(int irq, void *data){    mutex_lock(&dev->mutex);/* ... */    mutex_unlock(&dev->mutex);return IRQ_HANDLED;}

修正方式:

static irqreturn_t handler(int irq, void *data){    spin_lock(&dev->lock);/* 短临界区 */    spin_unlock(&dev->lock);    schedule_work(&dev->work);return IRQ_HANDLED;}

复杂处理放到工作队列中执行,工作队列运行在进程上下文,可以获取 mutex。

10.3 使用 atomic_set 实现自增

错误:

atomic_set(&counter, atomic_read(&counter) + 1);

修正:

atomic_inc(&counter);

10.4 多个 CPU 同时修改链表但只使用原子变量

错误:

atomic_inc(&list_count);list_add_tail(&node->list, &head->list);

原子计数无法保护链表结构。链表插入需要锁保护:

spin_lock(&head->lock);list_add_tail(&node->list, &head->list);head->count++;spin_unlock(&head->lock);

或者使用 RCU 等适合链表的同步机制。

10.5 忽略 down_interruptible 返回值

错误:

down_interruptible(&sem);use_resource();up(&sem);

修正:

int ret;ret = down_interruptible(&sem);if (ret)return ret;use_resource();up(&sem);

10.6 持锁时间过长导致系统卡顿

错误:

mutex_lock(&dev->mutex);do_long_operation();mutex_unlock(&dev->mutex);

修正思路:

  • 将长操作拆分为多个阶段。
  • 只在访问共享数据时持锁。
  • 将耗时计算、IO、固件下载等操作移出锁外。
  • 使用 per-device 锁代替全局大锁。
  • 使用无锁队列或 RCU 降低竞争。

十一、锁粒度与性能平衡

互斥机制不仅影响正确性,也影响性能。锁粒度过大,会导致大量执行路径串行化;锁粒度过小,会增加锁数量和依赖复杂度。

常见优化方向:

11.1 减小临界区

只保护真正共享的数据:

prepare_data(local);spin_lock(&lock);commit_data(local, shared);spin_unlock(&lock);

不要在临界区内做无关计算。

11.2 使用 per-CPU 数据

若数据只在本 CPU 内频繁更新,最后才汇总,可以使用 per-CPU 变量减少全局锁竞争。

11.3 使用 RCU

RCU 适合读多写少场景。读者通常无需传统锁,写者通过宽限期保证读者安全退出。

典型适用对象:

  • 路由表。
  • 模块列表。
  • 设备查找表。
  • 频繁读取、很少更新的数据。

但 RCU 使用复杂度较高,需要理解宽限期、回调、内存释放时机等概念。

11.4 避免热点锁

若多个 CPU 高频竞争同一把锁,即使临界区很短,也可能出现缓存行 bouncing 和性能下降。

可考虑:

  • 分片锁。
  • per-device 锁。
  • per-CPU 锁。
  • 无锁数据结构。
  • 批量处理减少锁获取次数。

Linux 内核互斥技术并不是单一工具,而是一套面向不同并发场景的机制集合。原子操作解决单变量原子修改问题,自旋锁解决短临界区不可睡眠互斥问题,mutex 解决进程上下文中可睡眠临界区问题,信号量提供计数型资源控制能力,lockdep 则帮助提前发现锁依赖错误和潜在死锁。

选型的核心在于明确执行上下文、临界区长度、是否允许睡眠以及共享数据的访问路径。原子操作最轻量,但不能保护复杂结构;自旋锁性能直接,但要求临界区短且不能睡眠;mutex 使用灵活,但只能用于进程上下文;信号量适合计数资源,但不应简单替代 mutex;死锁检测工具能显著提升调试效率,但不能替代正确的锁设计。

实际内核开发中,最可靠的方式不是依赖经验猜测并发问题,而是建立清晰的锁模型:明确每把锁保护哪些数据,明确每把锁的获取顺序,明确哪些路径可能进入中断或软中断,明确哪些函数可能睡眠。在此基础上配合 lockdep、debug mutexes、debug atomic sleep 等调试设施,可以在开发阶段尽早发现竞态和死锁隐患。

互斥机制的最终目标不是让代码“看起来线程安全”,而是在所有可能并发路径下保持数据结构一致、设备状态稳定、系统行为可预期。正确理解和选择 Linux 内核互斥技术,是编写高质量驱动、文件系统和内核子系统的基础能力。

阅读其他文章

OpenWrt的动态库与静态库

OpenWrt 补丁(Patch)机制详解

OpenWrt 启动过程深度解析

LuCI的Web UI开发实践

丢包?连接失败?如何对嵌入式Linux做网络诊断

内网穿透 -- 公网访问OpenWRT

实用的OpenWrt日志调试技巧

OpenWrt的Makefile构建体系

ESP32解析GPS 定位数据

WSL:Windows下的Linux开发利器

OpenWrt 启动过程深度解析

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-08-21 14:35:57 HTTP/2.0 GET : https://f.mffb.com.cn/a/510325.html
  2. 运行时间 : 0.364386s [ 吞吐率:2.74req/s ] 内存消耗:4,908.66kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=a25e92ba2adf6697c1e20a5a3c89274f
  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.000928s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.001785s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000875s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.008623s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.002000s ]
  6. SELECT * FROM `set` [ RunTime:0.001265s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.001945s ]
  8. SELECT * FROM `article` WHERE `id` = 510325 LIMIT 1 [ RunTime:0.020729s ]
  9. UPDATE `article` SET `lasttime` = 1787294157 WHERE `id` = 510325 [ RunTime:0.007817s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 67 LIMIT 1 [ RunTime:0.002358s ]
  11. SELECT * FROM `article` WHERE `id` < 510325 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.001666s ]
  12. SELECT * FROM `article` WHERE `id` > 510325 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.009626s ]
  13. SELECT * FROM `article` WHERE `id` < 510325 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.053791s ]
  14. SELECT * FROM `article` WHERE `id` < 510325 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.024528s ]
  15. SELECT * FROM `article` WHERE `id` < 510325 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.074134s ]
0.365811s