当前位置:首页>Linux>Linux 内核工作队列

Linux 内核工作队列

  • 2026-08-21 09:38:11
Linux 内核工作队列

在 Linux 内核中,大量工作不能也不应该在当前执行路径中立即完成。硬件中断要求快速返回,软中断上下文不允许睡眠,系统调用路径也不能因为一次慢速 I/O 而长时间阻塞。为了解决这一问题,内核提供了多种延迟执行和异步执行机制,其中工作队列(workqueue)是驱动开发、文件系统、电源管理、网络设备、块设备子系统中非常常见的一种基础设施。

工作队列的核心价值,在于把“需要稍后处理的任务”封装成一个工作项,然后交由内核线程在进程上下文中执行。相比软中断和 tasklet,工作队列允许睡眠、允许等待锁、允许执行可能阻塞的操作,因此特别适合驱动中需要访问 I2C、SPI、USB、块设备、文件系统接口的场景。

本文围绕 Linux 内核工作队列机制展开,重点讨论其执行模型、核心数据结构、常用 API、并发语义、生命周期管理、调试方法,以及一个典型的驱动卸载阶段 use-after-free 问题的分析与解决过程。文章面向内核驱动开发、嵌入式 Linux 应用开发、系统稳定性调试等场景,力求概念准确、代码可验证、调试思路可落地。


一、工作队列解决什么问题

Linux 内核中的许多事件具有突发性。例如:

  • GPIO 中断触发;
  • I2C 设备产生数据就绪信号;
  • 网卡收到数据包;
  • 块设备完成一次 I/O;
  • 定时器到期;
  • 电源状态发生变化;
  • 内核某个子系统需要异步回收资源。

这些事件发生时,当前执行上下文往往不适合做复杂处理。以硬件中断为例,中断处理程序运行在中断上下文,必须尽快结束,否则会影响其他中断响应,也可能导致系统实时性下降。中断上下文还有严格限制:不能睡眠,不能主动让出 CPU,不能调用可能阻塞的函数。

因此,内核通常将中断处理拆分为两个阶段:

  1. 上半部:快速确认中断、读取关键状态、清中断源;
  2. 下半部:执行耗时、复杂、可延迟的处理逻辑。

工作队列就是下半部机制中的一种重要实现。它将任务提交给内核工作线程执行,运行环境是进程上下文。进程上下文意味着:

  • 可以睡眠;
  • 可以等待互斥锁;
  • 可以调用可能阻塞的函数;
  • 可以进行内存分配,但需注意分配标志;
  • 可以执行较慢的设备访问;
  • 可以被调度器正常调度。

当然,工作队列也不是没有代价。相比软中断,工作队列需要经过内核线程调度,延迟更高。因此,对于极低延迟、极短处理时间的任务,软中断、tasklet 或 threaded IRQ 可能更合适;对于需要睡眠、需要复杂逻辑、需要与内核对象生命周期严格配合的任务,工作队列更合适。


二、工作队列的核心对象与执行模型

工作队列机制涉及几个关键对象:

  • struct work_struct:普通工作项;
  • struct delayed_work:延迟工作项;
  • struct workqueue_struct:工作队列;
  • struct worker:工作线程;
  • struct worker_pool:工作线程池。

一个典型执行流程如下:

中断 / 定时器 / 内核任务        |        vqueue_work() / schedule_work()        |        vworkqueue_struct / pwq        |        vworker_pool 待执行队列        |        vkworker 内核线程        |        vwork->func(work)

2.1 work_struct

struct work_struct 表示一个工作项。它包含工作处理函数和内部状态。驱动通常把它嵌入到自己的私有结构体中,然后通过 container_of() 恢复上下文。

典型结构如下:

struct sensor_priv {struct i2c_client *client;struct work_struct work;};

工作处理函数原型为:

static void sensor_work_func(struct work_struct *work){struct sensor_priv *priv;    priv = container_of(work, struct sensor_priv, work);/* 在这里执行实际处理逻辑 */}

这种设计使得一个工作项可以携带其所属设备对象,而不需要全局变量。

2.2 delayed_work

struct delayed_work 用于延迟执行的工作项。它内部包含一个 work_struct 和延迟提交机制。常见用途包括:

  • 周期性轮询传感器;
  • 延迟关闭背光;
  • 防抖处理;
  • 延迟重试;
  • 延迟释放资源;
  • 状态机超时处理。

定义方式如下:

struct sensor_priv {struct delayed_work dwork;};

处理函数中通过 dwork.work 恢复上下文:

static void sensor_poll_func(struct work_struct *work){struct delayed_work *dwork = to_delayed_work(work);struct sensor_priv *priv;    priv = container_of(dwork, struct sensor_priv, dwork);/* 执行轮询逻辑 */}

2.3 workqueue_struct

struct workqueue_struct 表示一个工作队列。驱动可以使用系统默认工作队列,也可以创建自己的工作队列。

系统默认工作队列适合简单、短小、通用的任务。若驱动有隔离性、优先级、并发度、生命周期管理方面的特殊要求,通常会创建独立工作队列。

创建工作队列常用接口:

struct workqueue_struct *alloc_workqueue(const char *fmt,unsigned int flags,int max_active);

销毁接口:

void destroy_workqueue(struct workqueue_struct *wq);

2.4 worker 与 worker_pool

工作项最终由内核线程执行,这些线程通常命名为 kworker。例如:

kworker/0:1kworker/u16:2kworker/1:3

其中数字与 CPU、workqueue 类型、绑核策略有关。工作队列内部通过 worker_pool 管理一组 worker。现代 Linux 工作队列采用并发管理机制,会根据负载创建或回收 worker,以避免过多线程造成调度压力,也避免单个任务阻塞整个工作队列。

对于普通驱动开发者而言,通常不需要直接操作 worker_pool,但理解其存在有助于分析 /procdebugfspsftrace 中的执行路径。


三、常用 API 与标准用法

3.1 初始化普通工作项

静态初始化:

static void my_work_func(struct work_struct *work);static DECLARE_WORK(my_work, my_work_func);

动态初始化:

INIT_WORK(&priv->work, my_work_func);

提交到系统默认工作队列:

schedule_work(&priv->work);

提交到指定工作队列:

queue_work(priv->wq, &priv->work);

3.2 初始化延迟工作项

静态初始化:

static void my_dwork_func(struct work_struct *work);static DECLARE_DELAYED_WORK(my_dwork, my_dwork_func);

动态初始化:

INIT_DELAYED_WORK(&priv->dwork, my_dwork_func);

提交延迟工作项:

schedule_delayed_work(&priv->dwork, msecs_to_jiffies(100));

提交到指定工作队列:

queue_delayed_work(priv->wq, &priv->dwork, msecs_to_jiffies(100));

修改延迟工作项的延迟时间或重新排队:

mod_delayed_work(priv->wq, &priv->dwork, msecs_to_jiffies(500));

3.3 创建工作队列

创建一个普通工作队列:

priv->wq = alloc_workqueue("sensor_wq"00);if (!priv->wq)return -ENOMEM;

创建一个 unbound 工作队列:

priv->wq = alloc_workqueue("sensor_wq", WQ_UNBOUND, 0);

创建一个有序工作队列:

priv->wq = alloc_ordered_workqueue("sensor_wq"0);

有序工作队列保证工作项按提交顺序执行,适合对顺序有严格要求的场景。

3.4 取消与等待

等待某个工作项完成:

flush_work(&priv->work);

取消某个工作项并等待其完成:

cancel_work_sync(&priv->work);

取消延迟工作项并等待其完成:

cancel_delayed_work_sync(&priv->dwork);

等待整个工作队列上的所有工作完成:

flush_workqueue(priv->wq);

销毁工作队列:

destroy_workqueue(priv->wq);

在驱动卸载路径中,常见安全顺序是:

  1. 停止新的工作来源,例如释放中断、停止定时器、关闭轮询;
  2. 取消已提交的工作项;
  3. 等待正在执行的工作项完成;
  4. 释放设备资源;
  5. 销毁工作队列。

四、工作队列的并发语义

理解工作队列的并发语义,是避免竞态、死锁、重复提交、资源释放错误的关键。

4.1 同一个 work_struct 不会并发执行

同一个 struct work_struct 在同一时刻只会有一个执行实例。若该工作项已经处于 pending 状态,再次调用 queue_work() 不会创建新的执行实例。

例如:

queue_work(wq, &priv->work);queue_work(wq, &priv->work);

第二次提交时,如果第一次提交的工作项尚未执行或仍在队列中,第二次提交会失败。queue_work() 返回 false

这意味着:

  • 一个 work_struct 不能表达多个并发事件;
  • 若多个事件需要并行处理,应使用多个 work_struct
  • 若多个事件只需要合并处理,一个 work_struct 反而可以降低负载。

4.2 工作项不应假设固定执行 CPU

除非显式使用绑核提交接口,否则不应假设工作项一定运行在某个 CPU 上。工作项可能运行在不同 CPU,也可能被迁移。

若确有需要,可以使用:

queue_work_on(cpu, wq, &priv->work);

但绑核会带来负载不均衡和调度限制,应谨慎使用。

4.3 max_active 控制并发度

alloc_workqueue() 的 max_active 参数用于限制同一工作队列中同时执行的工作项数量。

传入 0 表示使用内核默认值。

若希望严格顺序执行,可以使用:

alloc_ordered_workqueue("my_wq"0);

其本质是限制并发度为 1。

4.4 工作项之间可能并发执行

同一个工作队列中的多个不同工作项,可能由多个 worker 并发执行。因此,多个工作项访问共享数据时,必须自行保证同步。

常见同步手段包括:

  • 自旋锁;
  • 互斥锁;
  • RCU;
  • 原子变量;
  • completion;
  • 状态标志;
  • 工作项私有化,避免共享。

五、工作队列标志选择

alloc_workqueue() 支持多种标志,不同标志适用于不同场景。

5.1 WQ_UNBOUND

WQ_UNBOUND

表示工作队列不绑定到提交工作项的 CPU。worker 可以在多个 CPU 上执行,适合长时间任务或需要较高并发度的任务。

典型场景:

  • 固件下载;
  • 大块数据搬运;
  • 慢速设备 I/O;
  • 后台清理;
  • 不影响实时路径的异步任务。

5.2 WQ_HIGHPRI

WQ_HIGHPRI

表示工作项应尽可能在高优先级 worker pool 中执行。适合对延迟敏感但仍希望运行在进程上下文的任务。

5.3 WQ_MEM_RECLAIM

WQ_MEM_RECLAIM

表示该工作队列可能参与内存回收路径。内核会保证在内存紧张时仍有机制推进该队列上的工作,避免因 worker 创建失败导致回收路径死锁。

典型场景:

  • 块设备回写;
  • 内存回收辅助任务;
  • 与工作队列相关的内存 reclaim 路径。

普通驱动若与内存回收路径无关,通常不需要设置该标志。

5.4 WQ_CPU_INTENSIVE

WQ_CPU_INTENSIVE

表示工作项是 CPU 密集型任务,不应阻塞其他工作项的并发调度。该标志适合长时间计算型任务。

5.5 WQ_FREEZABLE

WQ_FREEZABLE

表示工作队列可被系统冻结。系统挂起时,freezable 工作队列中的工作项会被冻结,避免影响 suspend 流程。

适合与后台轮询、低功耗状态切换相关的任务。


六、工作队列与其他下半部机制对比

Linux 内核常见的延迟执行机制包括 softirq、tasklet、workqueue、threaded IRQ 等。它们并不是简单替代关系,而是适用于不同场景。

机制
执行上下文
是否可睡眠
延迟特点
典型用途
softirq
软中断上下文
很低
网络收发、块设备完成
tasklet
软中断上下文
简单下半部处理
workqueue
内核线程进程上下文
相对较高
可睡眠 I/O、复杂逻辑
threaded IRQ
内核线程进程上下文
较低
中断线程化处理

6.1 softirq 与 tasklet

softirq 和 tasklet 运行在软中断上下文,不能睡眠。它们适合非常短、非常关键、不能容忍调度延迟的任务。

缺点是:

  • 不能调用可能睡眠的函数;
  • 不能长时间运行;
  • 调试复杂度较高;
  • 容易影响系统整体响应。

6.2 workqueue

workqueue 的优势在于进程上下文,可以睡眠,适合复杂处理。

缺点是:

  • 延迟高于软中断;
  • 需要管理生命周期;
  • 需要处理取消、刷新、卸载等同步问题。

6.3 threaded IRQ

threaded IRQ 将中断处理放到内核线程中执行,也可以睡眠。它与 workqueue 的区别在于:

  • threaded IRQ 与某个中断源强绑定;
  • workqueue 更通用,适合异步任务;
  • threaded IRQ 适合中断发生后立即处理;
  • workqueue 适合将多个事件合并、延迟、排队处理。

很多驱动会组合使用:中断线程中只做简单判断,然后提交 work,由 workqueue 执行慢速设备访问。


七、工作队列生命周期管理

工作队列相关 Bug 往往不是出现在提交阶段,而是出现在卸载、热插拔、suspend/resume、设备 unbind 等生命周期切换阶段。

7.1 驱动 probe 阶段初始化

示例:

#include <linux/workqueue.h>#include <linux/i2c.h>#include <linux/interrupt.h>struct sensor_priv {struct i2c_client *client;struct work_struct work;struct workqueue_struct *wq;};static void sensor_work_func(struct work_struct *work){struct sensor_priv *priv;int val;    priv = container_of(work, struct sensor_priv, work);    val = i2c_smbus_read_word_data(priv->client, 0x00);if (val < 0)        dev_err(&priv->client->dev, "read register failed\n");}static irqreturn_t sensor_irq_handler(int irq, void *data){return IRQ_WAKE_THREAD;}static irqreturn_t sensor_irq_thread(int irq, void *data){struct sensor_priv *priv = data;    queue_work(priv->wq, &priv->work);return IRQ_HANDLED;}static int sensor_probe(struct i2c_client *client){struct sensor_priv *priv;int ret;    priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);if (!priv)return -ENOMEM;    priv->client = client;    i2c_set_clientdata(client, priv);    INIT_WORK(&priv->work, sensor_work_func);    priv->wq = alloc_workqueue("sensor_wq"00);if (!priv->wq)return -ENOMEM;    ret = request_threaded_irq(client->irq,                               sensor_irq_handler,                               sensor_irq_thread,                               IRQF_TRIGGER_FALLING | IRQF_ONESHOT,                               client->name,                               priv);if (ret) {        destroy_workqueue(priv->wq);return ret;    }return 0;}

7.2 驱动 remove 阶段清理

推荐顺序:

static void sensor_remove(struct i2c_client *client){struct sensor_priv *priv = i2c_get_clientdata(client);    free_irq(client->irq, priv);    cancel_work_sync(&priv->work);    destroy_workqueue(priv->wq);}

这里的关键是:

  1. free_irq() 先阻止新的中断进入;
  2. cancel_work_sync() 等待已经提交或正在执行的工作项结束;
  3. destroy_workqueue() 再销毁工作队列。

如果顺序反过来,可能出现:

  • 中断还在提交 work;
  • work 正在访问私有结构体;
  • 私有结构体已经被释放;
  • kworker 触发非法内存访问。

7.3 延迟工作项的停止

若驱动使用周期性延迟工作项:

static void sensor_poll_func(struct work_struct *work){struct delayed_work *dwork = to_delayed_work(work);struct sensor_priv *priv;    priv = container_of(dwork, struct sensor_priv, dwork);/* 读取数据 */    queue_delayed_work(priv->wq, &priv->dwork,                       msecs_to_jiffies(1000));}

启动:

INIT_DELAYED_WORK(&priv->dwork, sensor_poll_func);queue_delayed_work(priv->wq, &priv->dwork, msecs_to_jiffies(1000));

停止:

cancel_delayed_work_sync(&priv->dwork);

若轮询逻辑内部会重新提交自身,卸载路径应确保停止条件明确。例如使用状态标志:

if (!priv->polling)return;queue_delayed_work(priv->wq, &priv->dwork,                   msecs_to_jiffies(1000));

remove 中:

priv->polling = false;cancel_delayed_work_sync(&priv->dwork);

这样可以避免取消后又被重新提交。


八、工作队列中的常见死锁模式

工作队列本身是异步机制,但同步等待接口如果使用不当,很容易引入死锁。

8.1 在工作项内部等待自身完成

错误示例:

static void my_work_func(struct work_struct *work){    flush_work(work);}

该写法没有意义,且会导致死锁。因为当前工作项正在执行,却又等待自己完成。

同理,也不应在自身处理函数中调用:

cancel_work_sync(work);

或:

cancel_delayed_work_sync(dwork);

来取消自身。

8.2 flush_workqueue 自等待

若工作项运行在 wq 上,而工作项内部调用:

flush_workqueue(wq);

则可能死锁。因为 flush_workqueue() 等待该工作队列上的所有工作项完成,而当前工作项本身就是其中之一。

正确做法:

  • 避免在工作项中刷新自身所在工作队列;
  • 若需要等待某个子任务,使用单独的 work_struct 和 flush_work()
  • 若需要复杂同步,使用 completion
  • 若任务之间存在强依赖,重新设计工作项拆分方式。

8.3 cancel_work_sync 与锁依赖

cancel_work_sync() 会等待工作项执行完成。若 remove 路径持有某个锁,而工作项正好也要获取该锁,则可能死锁。

例如:

remove:    mutex_lock(&priv->lock);    cancel_work_sync(&priv->work);    mutex_unlock(&priv->lock);work:    mutex_lock(&priv->lock);    ...

此时 remove 等待 work 完成,work 等待 remove 释放锁,形成死锁。

解决思路:

  1. remove 中不要在持有 work 可能竞争的锁时调用 cancel_work_sync()
  2. 使用单独的停止标志;
  3. 缩短锁保护范围;
  4. 将状态切换与资源释放分离;
  5. 使用 lockdep 检查锁依赖。

九、调试工作队列问题的常用方法

9.1 观察 kworker 线程

工作项最终由 kworker 执行。可通过 ps 查看:

ps ax -o pid,comm

输出示例:

  PID COMMAND   12 kworker/0:1   18 kworker/1:2   25 kworker/u16:2

若某个 kworker 长期占用 CPU,可结合 perfftrace/proc/<pid>/stack 分析。

9.2 使用 debugfs 查看 workqueue 状态

若内核开启 debugfs,可以查看:

cat /sys/kernel/debug/workqueue

该节点可以显示工作队列、pwq、worker pool、pending work 等信息。不同内核版本输出格式略有差异,但通常可用于确认工作队列是否存在、是否有工作项积压。

9.3 使用 ftrace 跟踪提交与执行路径

可跟踪:

queue_workqueue_work_onqueue_delayed_workprocess_one_work

示例:

cd /sys/kernel/tracingecho 1 > events/workqueue/enablecat trace

具体可用事件与内核版本有关。若系统支持 workqueue tracepoint,可直接观察 work 提交和执行过程。

9.4 使用 KASAN 定位 use-after-free

若工作项访问了已释放对象,KASAN 通常会输出类似信息:

BUG: KASAN: slab-use-after-free in sensor_work_func+0x...

这类问题通常说明:

  • 驱动卸载时未取消 work;
  • work 生命周期长于对象生命周期;
  • 中断或定时器仍在提交 work;
  • remove 顺序错误。

9.5 使用 lockdep 检查锁依赖

开启 CONFIG_PROVE_LOCKING 后,内核会检查锁依赖。若工作队列等待路径与设备锁形成循环依赖,lockdep 有机会在死锁发生前给出告警。


十、实际 Bug 分析:驱动卸载后 kworker 访问已释放对象

下面给出一个驱动开发中非常典型的问题。

10.1 问题背景

某 I2C 传感器驱动在中断线程中提交工作项,工作项中读取传感器寄存器。驱动加载、触发中断、读取数据均正常。但在快速执行加载、触发中断、卸载后,内核出现 KASAN 报错。

复现步骤:

modprobe sensor_drvecho test > /sys/kernel/debug/sensor/trigger_irqrmmod sensor_drv

10.2 错误日志

内核日志类似:

BUG: KASAN: slab-use-after-free in sensor_work_func+0x48/0xd0Read of size 8 at addr ffff888105a3b000 by task kworker/1:3/87Workqueue: sensor_wq sensor_work_funcCall Trace: dump_stack print_address_description print_report kasan_report sensor_work_func process_one_work worker_thread kthread ret_from_fork

10.3 初步分析

日志中关键信息有三点:

  1. 崩溃发生在 sensor_work_func
  2. 执行上下文是 Workqueue: sensor_wq
  3. 访问类型是 slab-use-after-free

这说明 kworker 正在执行驱动的工作项,而工作项访问的对象已经被释放。

10.4 检查 remove 路径

原始 remove 代码如下:

static void sensor_remove(struct i2c_client *client){struct sensor_priv *priv = i2c_get_clientdata(client);    free_irq(client->irq, priv);    kfree(priv);}

问题很明显:

  • free_irq() 只能保证之后不再有新的中断线程提交 work;
  • 但在 free_irq() 之前,可能已经有 work 被提交;
  • 甚至可能正在执行;
  • kfree(priv) 释放对象后,kworker 仍可能访问 priv

10.5 修复方案

正确做法是在释放对象前取消并等待工作项完成。

修复后的 remove:

static void sensor_remove(struct i2c_client *client){struct sensor_priv *priv = i2c_get_clientdata(client);    free_irq(client->irq, priv);    cancel_work_sync(&priv->work);    destroy_workqueue(priv->wq);    kfree(priv);}

若使用延迟工作项:

free_irq(client->irq, priv);cancel_delayed_work_sync(&priv->dwork);destroy_workqueue(priv->wq);kfree(priv);

若工作项内部还会重新提交自身,应增加停止标志:

static void sensor_poll_func(struct work_struct *work){struct delayed_work *dwork = to_delayed_work(work);struct sensor_priv *priv;    priv = container_of(dwork, struct sensor_priv, dwork);if (!priv->polling)return;/* 轮询逻辑 */    queue_delayed_work(priv->wq, &priv->dwork,                       msecs_to_jiffies(1000));}

remove:

priv->polling = false;free_irq(client->irq, priv);cancel_delayed_work_sync(&priv->dwork);destroy_workqueue(priv->wq);kfree(priv);

10.6 验证方法

修复后需要反复验证:

modprobe sensor_drvrmmod sensor_drv

同时制造中断:

echo test > /sys/kernel/debug/sensor/trigger_irq

验证标准:

  • KASAN 无 slab-use-after-free;
  • lockdep 无告警;
  • rmmod 不卡死;
  • 多次 bind/unbind 稳定;
  • dmesg 无 workqueue 警告;
  • 设备资源释放后无残留 kworker 访问。

十一、工作队列设计建议

11.1 工作项应尽量短小

虽然工作队列运行在进程上下文,可以睡眠,但并不意味着工作项可以无限制执行。过长的处理函数会:

  • 占用 worker;
  • 增加延迟;
  • 影响同一工作队列上的其他任务;
  • 增加 suspend/resume 复杂度;
  • 增加取消和等待时间。

对于长任务,可以拆分为多个阶段,或者使用延迟工作项分步执行。

11.2 避免用工作队列做硬实时任务

工作队列受调度器影响,不能保证严格纳秒级或微秒级延迟。若系统有硬实时要求,应考虑:

  • threaded IRQ;
  • RT 调度策略;
  • 专用内核线程;
  • 硬件定时器;
  • 中断直接处理关键路径。

11.3 明确工作项的所有权

每个 work_struct 都应有明确的生命周期归属。通常归属于某个设备对象、子系统对象或驱动私有结构体。

对象释放前,必须确保:

  • 工作项不再 pending;
  • 工作项不再 executing;
  • 不会再被新的路径提交。

11.4 停止源头比取消工作项更重要

很多 Bug 不是因为没有 cancel,而是因为 cancel 之后又有新的提交。

常见新提交来源:

  • 中断未关闭;
  • 定时器未删除;
  • 用户态接口仍可触发;
  • 内核子系统回调仍有效;
  • 轮询逻辑自动重新提交;
  • 电源管理恢复路径重新启动任务。

因此,安全卸载的核心不是简单调用 cancel_work_sync(),而是先切断提交来源。

11.5 慎用 flush_workqueue

flush_workqueue() 的等待范围很大,容易造成不必要的阻塞。多数场景下,应优先使用:

flush_work(&priv->work);

或:

cancel_work_sync(&priv->work);

只等待与当前对象相关的工作项,而不是等待整个工作队列。

11.6 合理使用 ordered workqueue

如果多个工作项必须按顺序执行,可以使用 ordered workqueue:

alloc_ordered_workqueue("my_ordered_wq"0);

但需要注意,顺序执行会降低并发度。如果任务之间没有依赖关系,不必强制顺序执行。

11.7 合理使用 unbound workqueue

对于长时间、低实时性、可并发的后台任务,可以使用:

WQ_UNBOUND

这样可以避免长时间任务绑定在某个 CPU 上影响该 CPU 的其他工作项调度。

11.8 与电源管理配合

若驱动参与 suspend/resume,需要考虑:

  • suspend 前是否停止轮询;
  • resume 后是否恢复轮询;
  • 工作项是否访问已挂起设备;
  • 是否需要 freezable 工作队列;
  • 是否需要 cancel/flush 后再进入低功耗状态。

否则可能出现 suspend 卡住、resume 后设备状态异常、工作项访问未唤醒设备等问题。


十二、工作队列在驱动中的典型应用场景

12.1 中断后读取慢速设备

硬件中断只通知事件,真正读取 I2C/SPI 寄存器放到 work 中执行。

优点:

  • 中断路径短;
  • I2C/SPI 访问可睡眠;
  • 可以合并多次中断;
  • 便于错误重试。

12.2 按键防抖

GPIO 按键中断触发后,不立即上报,而是提交延迟工作项。延迟一段时间后再次读取 GPIO 状态,确认是否为有效按键。

12.3 传感器周期轮询

没有中断的传感器,可以使用 delayed work 定时读取数据。

12.4 异步上报输入事件

输入设备驱动中,中断采集原始数据,work 中调用 input 子系统上报事件。

12.5 延迟关闭资源

例如背光、LED、电源域等。用户操作停止后,延迟一段时间再关闭,避免频繁开关。

12.6 状态机推进

复杂驱动状态机可以在 work 中执行状态切换,避免在中断上下文中做复杂判断。


十三、工作队列使用中的常见误区

13.1 认为 schedule_work 一定会立即执行

schedule_work() 只是提交工作项,真正执行时间取决于:

  • worker 是否空闲;
  • 系统负载;
  • 工作队列并发度;
  • CPU 调度;
  • 工作项优先级;
  • 是否处于 suspend/freezer 路径。

因此不能假设提交后立即完成。

13.2 认为 cancel_work_sync 可以取消任何未来提交

cancel_work_sync() 只处理调用时已经提交或正在执行的工作项。如果之后还有路径继续提交同一个工作项,问题依旧存在。

13.3 认为 free_irq 后工作项一定不存在

free_irq() 只能阻止新的中断处理,不能保证之前已经提交的工作项已经完成。因此仍需要 cancel_work_sync() 或 flush_work()

13.4 认为 destroy_workqueue 可以替代所有清理

destroy_workqueue() 会进行清理,但驱动仍应显式取消工作项。否则可能出现:

  • 工作项访问已释放对象;
  • 销毁路径等待过久;
  • 内核告警;
  • 难以复现的竞态。

13.5 认为工作项可以随意睡眠

工作项可以睡眠,但不能因此忽略性能。长时间睡眠或长时间等待会占用 worker,影响其他任务。

13.6 忽略工作项重入语义

同一个 work_struct 不会并发执行,但可能被多次顺序执行。设计时要考虑:

  • 多次执行是否安全;
  • 是否需要合并事件;
  • 是否需要记录状态;
  • 是否需要丢弃过期事件。

十四、从内核设计角度看工作队列的价值

工作队列不只是一个简单的任务队列,它体现了内核异步执行框架的几个重要设计思想。

14.1 上下文转换

工作队列将任务从中断上下文、软中断上下文或原子上下文,转移到进程上下文。这样可以使用更丰富的内核 API,也更容易处理复杂逻辑。

14.2 并发控制

工作队列内部通过 worker pool 和并发管理,避免每个任务都创建独立内核线程。系统可以根据负载动态调整 worker 数量,兼顾吞吐和资源消耗。

14.3 生命周期抽象

工作项、工作队列、worker 三者分离,使驱动可以只关心任务本身,而不必手动管理线程创建、唤醒、退出。

14.4 可组合性

工作队列可以与中断、定时器、completion、wait queue、电源管理、设备模型组合,形成完整的异步处理链路。


工作队列是 Linux 内核中非常基础但又极其重要的机制。它将异步任务从不可睡眠的上下文转移到进程上下文,使驱动能够安全地执行慢速 I/O、复杂状态处理和可阻塞操作。理解工作队列,不仅要掌握 INIT_WORKschedule_workqueue_workcancel_work_sync 等 API,更要理解其背后的执行模型、并发语义和生命周期约束。

实际开发中,工作队列相关 Bug 往往集中在几个方向:

  1. 工作项访问已释放对象;
  2. remove 路径未正确取消工作项;
  3. 中断或定时器仍在持续提交工作项;
  4. flush_workqueue 或 cancel_work_sync 引发死锁;
  5. 同一个 work_struct 被误用于多个并发事件;
  6. 延迟工作项自调度导致卸载不干净;
  7. suspend/resume 阶段工作状态不一致。

解决这些问题的核心思路并不复杂:明确工作项归属,切断提交来源,释放对象前完成取消与等待,避免自等待和锁循环,合理选择工作队列类型与标志。

对于驱动开发而言,工作队列用得好,可以显著降低中断压力、提升系统稳定性;用得不好,则容易引入竞态、死锁和内存安全问题。掌握工作队列机制,是 Linux 驱动开发从“能跑”走向“稳定可维护”的关键一步。

阅读其他文章

读写锁和RCU
Linux 内核互斥技术
设备树dts到probe流程

Linux 中断机制与下半部设计

OpenWrt 启动过程深度解析

LuCI的Web UI开发实践

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

内网穿透 -- 公网访问OpenWRT

实用的OpenWrt日志调试技巧

OpenWrt的Makefile构建体系

ESP32解析GPS 定位数据

WSL:Windows下的Linux开发利器

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-08-21 13:27:48 HTTP/2.0 GET : https://f.mffb.com.cn/a/510987.html
  2. 运行时间 : 0.113184s [ 吞吐率:8.84req/s ] 内存消耗:5,045.16kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=8a498b8b955ceff982a8a7dff51d3b78
  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.000506s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.000532s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000301s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000345s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.000468s ]
  6. SELECT * FROM `set` [ RunTime:0.000194s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.000587s ]
  8. SELECT * FROM `article` WHERE `id` = 510987 LIMIT 1 [ RunTime:0.000541s ]
  9. UPDATE `article` SET `lasttime` = 1787290068 WHERE `id` = 510987 [ RunTime:0.006588s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 67 LIMIT 1 [ RunTime:0.000281s ]
  11. SELECT * FROM `article` WHERE `id` < 510987 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.000421s ]
  12. SELECT * FROM `article` WHERE `id` > 510987 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.000353s ]
  13. SELECT * FROM `article` WHERE `id` < 510987 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.001370s ]
  14. SELECT * FROM `article` WHERE `id` < 510987 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.008474s ]
  15. SELECT * FROM `article` WHERE `id` < 510987 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.014682s ]
0.117005s