当前位置:首页>学习笔记>brpc 学习笔记(四):butex——bthread 的 futex 同步原语

brpc 学习笔记(四):butex——bthread 的 futex 同步原语

  • 2026-05-02 18:53:09
brpc 学习笔记(四):butex——bthread 的 futex 同步原语

在前几篇文章中,我们拆解了 brpc 的内存管理基础设施:IOBuf 的零拷贝缓冲区、ObjectPool 的无锁对象池、ResourcePool 的 ID 索引对象池。

现在我们进入 bthread 的核心领域。

bthread 是 brpc 的 M:N 协程库——成千上万的用户态协程复用到少量 pthread 上运行。既然多个协程共享线程,就需要同步机制:一个协程等锁、等条件变量、等信号量时,需要一种方式挂起自己,等条件满足时再被唤醒。

Linux 内核提供了 futex(fast user-space mutex)实现这一语义。但 futex 是内核的系统调用,只适用于 pthread。bthread 需要一个自己的 futex——这就是 butex(bthread-futex)。

butex 是 bthread 互斥锁、条件变量、信号量的统一基石。它围绕一个 32 位原子整数,提供 wait/wake 操作,同时支持 bthread 和 pthread 的混合同步。

本文从源码出发,拆解 butex 的设计。


一、butex 是什么?

一句话概括:butex 是一个 32 位原子整数,配合 wait/wake 操作实现同步

核心语义与 Linux futex 类似:

  • wait
    :如果当前值等于期望值,则挂起当前线程/bthread,直到被 wake 或超时
  • wake
    :唤醒一个或多个在 butex 上等待的线程/bthread

但与 Linux futex 有几个关键区别:

Linux futex
butex
作用域
跨进程共享
进程内私有
超时时间
相对时间
绝对时间
调度对象
仅 pthread
bthread + pthread 混合

这种混合能力是 butex 的核心价值——同一个 butex 上,bthread 和 pthread 可以互相等待和唤醒。


二、核心数据结构

2.1 Butex:缓存行对齐的同步原语

struct BAIDU_CACHELINE_ALIGNMENT Butex {    butil::atomic<int> value;      // 32 位原子值    ButexWaiterList waiters;       // 等待者链表(FIFO)    FastPthreadMutex waiter_lock;  // 保护 waiters 的互斥锁};

三个要点:

1. value 必须在偏移 0 处。butex_create()返回&value而非Butex*。外部代码拿到的是一个int*,而butex_wake等函数通过container_ofvalue的地址反推出整个Butex的地址。这要求value 必须是第一个成员。

2. 缓存行对齐。 整个 Butex 恰好占 64 字节(一个缓存行),避免与其他 butex 发生伪共享。

3. 等待者是侵入式链表。waiters 是一个 LinkedList<ButexWaiter>,按 FIFO 顺序排列。waiter_lock 是一把 FastPthreadMutex(内部用 futex 实现),保护链表的并发访问。

2.2 ButexWaiter:等待者的统一抽象

struct ButexWaiter : public butil::LinkNode<ButexWaiter> {    bthread_t tid;                    // pthread 的 tid 为 0,bthread 不为 0    butil::atomic<Butex*> container;  // 所属的 Butex(用于安全移除)};

ButexWaiter 是一个侵入式双向链表节点,所有等待者都挂在 Butex::waiters 上。通过 tid 区分等待者类型:

  • tid == 0 → pthread 等待者(ButexPthreadWaiter
  • tid != 0 → bthread 等待者(ButexBthreadWaiter

container 字段指向等待者当前所在的 Butex。移除等待者时需要加锁二次验证,防止在并发操作(如 requeue)中被修改。

2.3 两种等待者

bthread 等待者——分配在栈上,包含调度所需的所有信息:

struct ButexBthreadWaiter : public ButexWaiter {    TaskMeta* task_meta;          // 关联的任务元数据    TimerThread::TaskId sleep_id; // 超时定时器 ID    WaiterState waiter_state;     // 等待状态    int expected_value;           // 期望的 butex 值    Butex* initial_butex;         // 初始等待的 butex    TaskControl* control;         // 所属的 TaskControl    const timespec* abstime;      // 超时时间(NULL = 无限等待)    bthread_tag_t tag;            // 标签,用于选择 TaskGroup};
pthread 等待者——更轻量,通过内核 futex 阻塞:
struct ButexPthreadWaiter : public ButexWaiter {    butil::atomic<int> sig;  // PTHREAD_NOT_SIGNALLED → PTHREAD_SIGNALLED};
pthread 没有用户态调度器,只能通过 futex 系统调用阻塞。sig 字段就是 futex 操作的地址——唤醒时设为 PTHREAD_SIGNALLED 并调用 futex_wake

2.4 等待者状态

enum WaiterState {    WAITER_STATE_NONE,            // 未使用    WAITER_STATE_READY,           // 已就绪,即将入队    WAITER_STATE_TIMEDOUT,        // 等待超时    WAITER_STATE_UNMATCHEDVALUE,  // 值不匹配    WAITER_STATE_INTERRUPTED,     // 被中断};

状态转换由 TimerThread(超时)、wake 操作(唤醒)和 interrupt(中断)触发。入队前的 READY 检查是一个关键的乐观锁机制——如果 TimerThread 在入队前就触发了超时,则放弃入队,避免无效等待。


三、创建与销毁

butex 的生命周期由 ObjectPool 管理(关于 ObjectPool 的详细设计,参见本系列第二篇文章)。

创建butex_create() 从 ObjectPool 分配一个 Butex,返回 &value(即 32 位整数的指针)。

voidbutex_create() {    Butex* b = butil::get_object<Butex>();    return b ? &b->value : NULL;}
销毁butex_destroy() 通过 container_of 从 value 地址反推 Butex 地址,归还到 ObjectPool。
voidbutex_destroy(void* butex){    Butex* b = container_of(        static_cast<butil::atomic<int>*>(butex), Butex, value);    butil::return_object(b);}

值得注意的是,ObjectPool 永不释放内存——归还的 Butex 仍留在池中等待复用。这是有意为之:butex_wake 和 butex_destroy 之间存在竞态——Event 对象在 signal 线程解锁后,wait 线程可能获取锁并销毁 Event,导致 signal 线程后续的 butex_wake 访问已释放的内存。ObjectPool 的"永不释放"特性完美解决了这个问题——归还后内存仍然有效,butex_wake 最多产生一次无害的虚假唤醒。

Butex 在 ObjectPool 中的 Block 容量也被特化为 128(默认为 4096):
template <> struct ObjectPoolBlockMaxItem<bthread::Butex> {    static const size_t value = 128;  // 每个 Block 最多 128 个 Butex};
这是因为每个 Butex 占 64 字节(一个缓存行),128 × 64 = 8KB,恰好是一个 Block 的默认大小。

四、唤醒操作

唤醒是 butex 的"发信号"侧。核心挑战是:等待者可能是 bthread,也可能是 pthread,需要用不同的方式唤醒

4.1 butex_wake:唤醒一个等待者

intbutex_wake(void* arg, bool nosignal){    Butex* b = container_of(static_cast<butil::atomic<int>*>(arg), Butex, value);    ButexWaiter* front = NULL;    {        BAIDU_SCOPED_LOCK(b->waiter_lock);        if (b->waiters.empty()) return 0;  // 没人等,直接返回        front = b->waiters.head()->value();        front->RemoveFromList();        front->container.store(NULL, butil::memory_order_relaxed);    }    if (front->tid == 0) {        // pthread → futex 唤醒        wakeup_pthread(static_cast<ButexPthreadWaiter*>(front));    } else {        // bthread → 取消定时器 + 调度执行        ButexBthreadWaiter* bbw = static_cast<ButexBthreadWaiter*>(front);        unsleep_if_necessary(bbw, get_global_timer_thread());        TaskGroup* g = get_task_group(bbw->control, bbw->tag);        if (g == tls_task_group) {            run_in_local_task_group(g, bbw->task_meta, nosignal);        } else {            g->ready_to_run_remote(bbw->task_meta, nosignal);        }    }    return 1;}
流程很清晰:
取出队头等待者 → 判断类型  ├─ pthread → wakeup_pthread(设 sig + futex 系统调用)  └─ bthread → 取消定时器 → 加入 TaskGroup 就绪队列
对于 bthread 等待者,还需要区分本地和远程:如果等待者属于当前线程的 TaskGroup,直接 exchange 切换执行(最快);否则放入远程 TaskGroup 的就绪队列。

4.2 butex_wake_n:唤醒 n 个等待者

当需要唤醒多个等待者时(如 broadcast),需要批量处理。butex_wake_n 的策略是先分流,再分别处理
加锁 → 从 waiters 取出 n 个等待者  ├─ 按 tid 分流到 bthread_waiters / pthread_waiters  └─ 解锁pthread_waiters → 逐个 wakeup_pthreadbthread_waiters:  ├─ 头节点单独摘出(最后 exchange,最高效的唤醒方式)  ├─ 其余从尾部弹出 → ready_to_run_general(批量调度)  ├─ flush 各 tag 的 TaskGroup  └─ 头节点 exchange 或 ready_to_run_remote
为什么头节点要留到最后?因为 exchange 会直接切换执行——如果先处理头节点,后续的批量操作就被延迟了。把 exchange 放在最后,确保所有其他等待者先调度好,再切换执行。

4.3 butex_wake_except:排除式唤醒

用于 Mutex 场景:锁持有者释放锁时唤醒其他等待者,但不唤醒自己。

intbutex_wake_except(void* arg, bthread_t excluded_bthread){    // 遍历 waiters,tid == excluded_bthread 的放回队列    // 其余正常唤醒}

4.4 butex_requeue:重排队(避免惊群效应)

这是 butex 中最精巧的操作,类似 Linux futex 的 FUTEX_REQUEUE

场景:ConditionVariable::broadcast 时,所有等待在条件变量 butex 上的线程被唤醒,然后它们立刻去竞争 Mutex butex——这就是"惊群效应"(thundering herd)。

解决方案:不唤醒所有人,而是唤醒第一个等待者,将其余等待者直接转移到 Mutex butex 的等待队列上。这样被转移的等待者不需要被唤醒再重新阻塞,省去了一次 futex 系统调用。

int butex_requeue(void* butex1, void* butex2) {    // 唤醒 butex1 的第一个等待者    // 将 butex1 的其余等待者转移到 butex2 的等待队列}
关键细节:双重锁。操作涉及两个 butex 的等待队列,必须同时加锁。为了避免死锁,使用 double_lock 按地址顺序加锁(先加地址小的锁)。

五、等待操作

等待是 butex 的"阻塞"侧。核心挑战是:bthread 不能真正阻塞线程——它需要让出 CPU,让其他 bthread 继续运行

5.1 butex_wait:上层统一接口

intbutex_wait(void* arg, int expected_value, const timespec* abstime, bool prepend){    Butex* b = container_of(static_cast<butil::atomic<int>*>(arg), Butex, value);    // 快速检查:值不匹配,立即返回    if (b->value.load(butil::memory_order_relaxed) != expected_value) {        errno = EWOULDBLOCK;        return -1;    }    TaskGroup* g = tls_task_group;    if (NULL == g || g->is_current_pthread_task()) {        // pthread 路径        return butex_wait_from_pthread(g, b, expected_value, abstime, prepend);    }    // bthread 路径    // ...(见下一节)}

入口处做两件事:值检查(值不匹配就不等了)和身份判断(当前是 bthread 还是 pthread)。

5.2 bthread 的等待路径

bthread 不能阻塞线程,它需要"挂起自己,让出 CPU"。这分两步完成:

第一步:设置 remained 回调 + 让出执行

ButexBthreadWaiter bbw;  // 栈上分配等待者bbw.tid = g->current_tid();bbw.waiter_state = WAITER_STATE_READY;bbw.expected_value = expected_value;bbw.initial_butex = b;// ...bbw.task_meta->current_waiter.store(&bbw, butil::memory_order_release);WaitForButexArgs args{ &bbw, prepend };g->set_remained(wait_for_butex, &args);  // 设置回调TaskGroup::sched(&g);                    // 让出 CPU

set_remained 注册 wait_for_butex 作为 remained 函数——当这个 bthread 被调度器切换出去后,remained 函数会在调度上下文中执行。

第二步:remained 函数中入队

voidwait_for_butex(void* arg) {    auto args = static_cast<WaitForButexArgs*>(arg);    ButexBthreadWaiter* bw = args->bw;    Butex* b = bw->initial_butex;    BAIDU_SCOPED_LOCK(b->waiter_lock);    if (b->value != bw->expected_value) {        // 值已变,不等了        bw->waiter_state = WAITER_STATE_UNMATCHEDVALUE;    } else if (bw->waiter_state == WAITER_STATE_READY && !bw->task_meta->interrupted) {        // 值匹配、未超时、未中断 → 入队        b->waiters.Append(bw);  // 或 Prepend(高优先级)        bw->container.store(b);        if (bw->abstime != NULL) {            // 注册超时定时器            bw->sleep_id = get_global_timer_thread()->schedule(                erase_from_butex_and_wakeup, bw, *bw->abstime);        }        return;    }    // 入队失败,直接标记为就绪    tls_task_group->ready_to_run(bw->task_meta);}

为什么要分两步?因为入队需要加锁。如果在 bthread 的执行上下文中直接加锁入队,会导致持有锁期间无法被调度器切换。分两步后:第一步是无锁的准备工作,切换到调度上下文后再加锁入队。

入队前的 READY 检查是一个乐观锁机制:在 set_remained 和实际入队之间,TimerThread 可能已经触发了超时。如果 waiter_state 不再是 READY,说明已被超时处理,放弃入队。

5.3 pthread 的等待路径

pthread 没有用户态调度器,只能通过内核 futex 阻塞:
// wait_pthread 的核心循环while (true) {    if (timeout_us > MIN_SLEEP_US || abstime == NULL) {        rc = futex_wait_private(&pw.sig, PTHREAD_NOT_SIGNALLED, ptimeout);        if (pw.sig != PTHREAD_NOT_SIGNALLED) {            return rc;  // 被唤醒        }    } else {        errno = ETIMEDOUT;        rc = -1;    }    if (rc != 0 && errno == ETIMEDOUT) {        // 超时 → 从 butex 队列中移除自己        erase_from_butex(&pw, false, WAITER_STATE_TIMEDOUT);        return rc;    }}
关键点:pthread 在 futex 上阻塞,唤醒后还需从 butex 的等待队列中移除自己。而 bthread 的唤醒由 butex_wake 负责移除——两种等待者的"谁负责清理"责任分配不同。

5.4 两种路径的对比

bthread 路径
pthread 路径
阻塞方式
sched()
 让出 CPU,其他 bthread 继续运行
futex_wait
 系统调用,线程真正阻塞
等待者分配
栈上分配 ButexBthreadWaiter
栈上分配 ButexPthreadWaiter
入队方式
remained 回调中入队
直接在当前栈上入队
唤醒方式
加入 TaskGroup 就绪队列
设 sig + futex_wake
清理责任
唤醒者负责移除
超时时自行移除

六、erase_from_butex:安全的等待者移除

这是 butex 中最易出错的操作——从等待队列中移除一个等待者。难在哪里?并发

  • TimerThread  可能正在超时移除
  • butex_wake 可能正在唤醒
  • butex_requeue 可能正在转移

erase_from_butex 用一个自旋+二次验证的模式解决这个问题:

inlineboolerase_from_butex(ButexWaiter* bw, bool wakeup, WaiterState state){    Butex* b;    while ((b = bw->container.load(butil::memory_order_acquire))) {        BAIDU_SCOPED_LOCK(b->waiter_lock);         // 加锁        if (b == bw->container.load(butil::memory_order_relaxed)) {            // 二次验证:container 没变,安全移除            bw->RemoveFromList();            bw->container.store(NULL);            if (bw->tid) {                static_cast<ButexBthreadWaiter*>(bw)->waiter_state = state;            }            break;        }        // container 变了(被 requeue 转移了),重新循环    }    // 可选:唤醒等待者}
循环的必要性:requeue 操作可能把等待者从 butex1 转移到 butex2,导致 container 改变。加锁后再次检查 container 是否一致,如果不一致说明正在被转移,需要重新定位。

七、可见性保证

butex 的 wait 和 wake 之间有严格的可见性保证,这是正确性的基石。

场景 1:wait 在 wake 之前

[thread1]              [thread2]wait()                  value = new_value                        wake()wait() 发现值不匹配,不阻塞
场景 2:wait 在 wake 之后
[thread1]              [thread2]                        value = new_value                        wake()wait()wake() 的内存屏障确保 value 的修改对 wait() 可见
在实现上,wait_pthread 中的 futex_wait 系统调用本身提供了内核级内存屏障;bthread 路径中,set_remained 的 release 语义与 wait_for_butex 入队时的锁获取(acquire 语义)共同保证了可见性。

八、总结

butex 的设计可以归纳为一句话:在 32 位原子整数上,实现同时支持 bthread 和 pthread 的 futex 语义

四个关键设计:

1. 统一的等待者抽象。ButexWaiter 作为基类,通过 tid 区分 bthread 和 pthread。同一个等待队列可以混合存放两种等待者,唤醒时按类型分发——这是 butex 支持混合同步的基础。

2. 两步等待(bthread)。 bthread 不能阻塞线程,因此等待分两步:先设置 remained 回调并让出 CPU,再在回调中加锁入队。入队前的 READY 状态检查实现了乐观锁,防止在两步之间已被超时移除的等待者被重复入队。

3. ObjectPool 的"永不释放"特性解决竞态。butex_destroy 和 butex_wake 之间存在天然的竞态——归还到 ObjectPool 后内存仍然有效,butex_wake 最多产生一次无害的虚假唤醒,无需额外的生命周期管理。

4. requeue 避免惊群效应。 唤醒第一个等待者,将其余等待者直接转移到另一个 butex 的等待队列,省去了"唤醒 → 重新阻塞"的往返开销。双重锁按地址顺序加锁避免死锁。

butex 是 bthread 的同步基础设施——后续文章将拆解基于 butex 构建的互斥锁、条件变量、信号量等上层同步原语。


本文基于 Apache brpc 源码(src/bthread/butex.hsrc/bthread/butex.cppsrc/bthread/sys_futex.cpp)撰写。

最新文章

随机文章

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