跳到内容
当前位置:首页>APP源码>嵌入式软件如何做组件化?让代码真正做到"拿来就用"

嵌入式软件如何做组件化?让代码真正做到"拿来就用"

  • 2026-10-23 11:05:00
嵌入式软件如何做组件化?让代码真正做到"拿来就用"

嵌入式软件如何做组件化?让代码真正做到"拿来就用"

从一个让人沮丧的场景开始

上一个项目做了一个环形缓冲区,这个项目又要用,把代码复制过来,改了改里面的类型名,跑了跑,不稳定。

排查半天,发现原来的版本有个隐藏假设:缓冲区大小必须是2的幂次,因为里面用了位掩码取模。这个项目的大小不是2的幂次,取模逻辑就出错了。

这种痛苦有一个名字:代码复用失败。

不是因为代码写得不好,而是代码写的时候没有想过"被别人用",更没有想过"被自己一年后用"。


组件化到底是什么

"组件化"这个词在嵌入式领域被说得很多,但真正理解它的人不多。

组件化不是"把代码分成多个文件",也不是"建多个文件夹"。文件夹多了只是代码的物理组织,不是组件化。

组件化的核心是一个判断:这块代码,在不了解它内部实现的情况下,能不能被正确使用?

能,就是组件。不能,就是代码片段。

这个判断里有三个关键词:

"不了解内部实现" — 使用者不需要看源码就知道怎么用。接口说明就足够了。

"被正确使用" — 不会因为使用方式稍有差异就出 bug。边界情况有处理,错误有提示。

"能" — 不是"理论上能",是"把头文件和源文件拿过去,编译通过,就可以用"。

做到这三点,才算组件。


为什么嵌入式组件化特别难

软件工程里的组件化理论很多,但照搬到嵌入式项目上往往不顺。原因有几个:

硬件依赖无处不在。

一个UART发送函数,调的可能是 USART1->DR = byte。这行代码在 STM32F4 上有效,在 NRF52 上没有。

资源约束改变设计。

PC上一个缓冲区大一点无所谓,嵌入式上缓冲区大小可能是系统能不能跑起来的关键。组件如果内部写死了缓冲区大小,到了另一个项目就要改源码。

没有标准的运行环境。

PC上有操作系统、有标准库、有文件系统。嵌入式项目可能有 RTOS、可能裸机、可能根本没有 printf。一个组件如果隐式依赖了某个运行环境,换个项目就不能用。

这三个问题,分别对应着组件化设计里的三个核心概念:硬件抽象、参数化、依赖显式化。


第一个核心:把硬件依赖收进一个地方

错误的做法:

// ring_buffer.c
voidRingBuffer_Flush(RingBuffer_t *rb) {
while (!RingBuffer_IsEmpty(rb)) {
uint8_t byte = RingBuffer_Read(rb);
while (!(USART1->SR & USART_SR_TXE));  // 直接操作寄存器
        USART1->DR = byte;
    }
}

这个函数把环形缓冲区的逻辑和 UART 硬件绑在了一起。拿到另一个项目,要改 USART1、要改 SR、要改 DR,要改很多地方。

正确的做法:缓冲区组件只管数据,不管数据怎么发出去。

// ring_buffer.h — 纯数据结构,无硬件依赖
typedefstruct {
uint8_t  *buffer;
uint16_t  size;
volatileuint16_t head;
volatileuint16_t tail;
} RingBuffer_t;

voidRingBuffer_Init(RingBuffer_t *rb, uint8_t *buf, uint16_t size);
boolRingBuffer_Write(RingBuffer_t *rb, uint8_t byte);
boolRingBuffer_Read(RingBuffer_t *rb, uint8_t *out);
boolRingBuffer_IsEmpty(const RingBuffer_t *rb);
boolRingBuffer_IsFull(const RingBuffer_t *rb);
uint16_tRingBuffer_Count(const RingBuffer_t *rb);

硬件操作放在调用者那里:

// uart.c — 知道硬件,调用缓冲区组件
#include"ring_buffer.h"

staticuint8_t tx_buf_data[256];
static RingBuffer_t tx_buf;

voidUART_Init(void) {
    RingBuffer_Init(&tx_buf, tx_buf_data, sizeof(tx_buf_data));
// 初始化 USART1...
}

voidUART_Write(uint8_t byte) {
    RingBuffer_Write(&tx_buf, byte);
    USART1->CR1 |= USART_CR1_TXEIE;  // 触发发送中断
}

voidUSART1_IRQHandler(void) {
if (USART1->SR & USART_SR_TXE) {
uint8_t byte;
if (RingBuffer_Read(&tx_buf, &byte)) {
            USART1->DR = byte;
        } else {
            USART1->CR1 &= ~USART_CR1_TXEIE;
        }
    }
}

现在 ring_buffer.c/.h 可以原封不动地用在任何项目里,它不知道也不关心 UART 是什么。


第二个核心:参数化,不要写死

写死的东西,就是以后要改源码的地方。

常见的写死方式:

// 写法一:魔数
#define BUFFER_SIZE 256

// 写法二:类型写死
staticuint32_t data[100];

// 写法三:行为写死
voidTimer_Init(void) {
// 写死100ms
    TIM2->ARR = 8000 - 1;
    TIM2->PSC = 1000 - 1;
}

不是说这些写法错,而是这些写法降低了组件的可复用性。

参数化的几种方式:

方式一:通过初始化函数传入

// 缓冲区大小由调用者决定
voidRingBuffer_Init(RingBuffer_t *rb, uint8_t *buf, uint16_t size) {
    rb->buffer = buf;
    rb->size   = size;
    rb->head   = 0;
    rb->tail   = 0;
}

// 调用者根据自己的情况决定大小
uint8_t small_buf[64];
RingBuffer_t rb1;
RingBuffer_Init(&rb1, small_buf, sizeof(small_buf));

uint8_t large_buf[1024];
RingBuffer_t rb2;
RingBuffer_Init(&rb2, large_buf, sizeof(large_buf));

方式二:通过配置结构体传入

当参数较多时,一个配置结构体比一堆参数更清晰,也更容易扩展:

typedefstruct {
uint32_t baudrate;
uint8_t  data_bits;    // 7 or 8
uint8_t  stop_bits;    // 1 or 2
bool     parity_en;
uint8_t  parity_mode;  // 0=even, 1=odd
} UART_Config_t;

voidUART_Init(const UART_Config_t *config);

好处:新增一个参数,不需要修改函数签名,只在结构体里加一个字段。

方式三:编译期参数(适合资源受限)

有些参数在编译期确定更合适,比如最大连接数、缓冲池大小:

// config.h — 每个项目可以有自己的配置
#ifndef UART_TX_BUF_SIZE
#define UART_TX_BUF_SIZE  256
#endif

#ifndef UART_RX_BUF_SIZE
#define UART_RX_BUF_SIZE  128
#endif

这样用 ifndef 保护,默认值在组件内部,项目如果需要改,在自己的 config.h 里覆盖就行,不需要改组件源码。


第三个核心:依赖显式化

最隐蔽的组件问题,是隐式依赖。

隐式依赖就是:这个组件能正常工作,需要某个前提,但这个前提没有被清楚地表达出来。

最常见的例子:

// log.c
voidLog_Write(constchar *msg) {
printf(msg);  // 隐式依赖:printf 必须可用
}

在有 stdio 的环境里,这没问题。在裸机项目里,printf 可能没有实现,或者实现了但是没有 stdout。把这个组件拿去用,编译报错,或者运行时没有任何输出,你不知道为什么。

用依赖注入暴露隐式依赖:

// log.h
typedefvoid(*Log_OutputFn)(constchar *str);

typedefstruct {
    Log_OutputFn output_fn;   // 输出函数,由使用者提供
uint8_t      min_level;   // 最小日志级别
} Log_Config_t;

voidLog_Init(const Log_Config_t *config);
voidLog_Write(uint8_t level, constchar *fmt, ...);
// 在有串口的项目里
voidmy_uart_output(constchar *str) {
    UART_SendString(str);
}

Log_Config_t log_cfg = {
    .output_fn = my_uart_output,
    .min_level = LOG_LEVEL_DEBUG,
};
Log_Init(&log_cfg);

// 在有 printf 的项目里
Log_Config_t log_cfg = {
    .output_fn = puts,
    .min_level = LOG_LEVEL_INFO,
};
Log_Init(&log_cfg);

// 在不需要日志的场景里
Log_Config_t log_cfg = {
    .output_fn = NULL,  // 传 NULL,Log_Write 内部检查,什么都不输出
    .min_level = LOG_LEVEL_ERROR,
};
Log_Init(&log_cfg);

现在 log.c/.h 没有任何隐式依赖,它所需要的所有东西都通过初始化参数显式传入。拿到任何项目都能用。


一个完整的组件长什么样

以一个软件定时器组件为例,把上面三个核心都用进来:

// soft_timer.h

#ifndef SOFT_TIMER_H
#define SOFT_TIMER_H

#include<stdint.h>
#include<stdbool.h>

// ---- 类型定义 ----

typedefvoid(*SoftTimer_Callback)(void *user_data);

typedefenum {
    SOFT_TIMER_ONE_SHOT,   // 只触发一次
    SOFT_TIMER_PERIODIC,   // 周期触发
} SoftTimer_Mode_t;

typedefstruct {
uint32_t           period_ms;
    SoftTimer_Mode_t   mode;
    SoftTimer_Callback callback;
void              *user_data;   // 传给回调的用户数据,组件不关心内容
} SoftTimer_Config_t;

// 不透明句柄,使用者不需要知道内部结构
typedefstructSoftTimer_sSoftTimer_t;

// ---- 接口 ----

/**
 * 初始化软件定时器子系统
 * @param get_tick_fn  获取当前时间戳(ms)的函数,由使用者提供
 * @param max_timers   最多同时管理几个定时器(由使用者决定,内存由组件分配或由使用者提供)
 */

voidSoftTimer_SystemInit(uint32_t (*get_tick_fn)(void), uint8_t max_timers);

/**
 * 创建一个软件定时器
 * @return  定时器句柄,失败返回 NULL
 */

SoftTimer_t *SoftTimer_Create(const SoftTimer_Config_t *config);

voidSoftTimer_Start(SoftTimer_t *timer);
voidSoftTimer_Stop(SoftTimer_t *timer);
voidSoftTimer_Reset(SoftTimer_t *timer);
boolSoftTimer_IsRunning(const SoftTimer_t *timer);
voidSoftTimer_Destroy(SoftTimer_t *timer);

/**
 * 必须在主循环或定时任务中定期调用,驱动定时器工作
 */

voidSoftTimer_Tick(void);

#endif
// soft_timer.c

#include"soft_timer.h"
#include<string.h>
#include<stdlib.h>

// ---- 内部结构,使用者不可见 ----

structSoftTimer_s {
    SoftTimer_Config_t config;
uint32_t           last_tick;
bool               running;
bool               in_use;
};

// 编译期参数:如果项目没定义,用默认值
#ifndef SOFT_TIMER_MAX_COUNT
#define SOFT_TIMER_MAX_COUNT 16
#endif

staticstructSoftTimer_stimer_pool[SOFT_TIMER_MAX_COUNT];
staticuint32_t(*s_get_tick)(void) = NULL;
staticuint8_t s_max_timers = 0;

voidSoftTimer_SystemInit(uint32_t (*get_tick_fn)(void), uint8_t max_timers) {
    s_get_tick  = get_tick_fn;
    s_max_timers = (max_timers > SOFT_TIMER_MAX_COUNT) 
                    ? SOFT_TIMER_MAX_COUNT 
                    : max_timers;
memset(timer_pool, 0, sizeof(timer_pool));
}

SoftTimer_t *SoftTimer_Create(const SoftTimer_Config_t *config) {
if (!config || !s_get_tick) returnNULL;

for (uint8_t i = 0; i < s_max_timers; i++) {
if (!timer_pool[i].in_use) {
            timer_pool[i].config  = *config;
            timer_pool[i].running = false;
            timer_pool[i].in_use  = true;
return &timer_pool[i];
        }
    }
returnNULL;  // 池子满了
}

voidSoftTimer_Start(SoftTimer_t *timer) {
if (!timer || !timer->in_use) return;
    timer->last_tick = s_get_tick();
    timer->running   = true;
}

voidSoftTimer_Stop(SoftTimer_t *timer) {
if (!timer) return;
    timer->running = false;
}

voidSoftTimer_Reset(SoftTimer_t *timer) {
if (!timer) return;
    timer->last_tick = s_get_tick();
}

boolSoftTimer_IsRunning(const SoftTimer_t *timer) {
return timer && timer->running;
}

voidSoftTimer_Destroy(SoftTimer_t *timer) {
if (!timer) return;
    timer->running = false;
    timer->in_use  = false;
}

voidSoftTimer_Tick(void) {
if (!s_get_tick) return;

uint32_t now = s_get_tick();

for (uint8_t i = 0; i < s_max_timers; i++) {
        SoftTimer_t *t = &timer_pool[i];
if (!t->in_use || !t->running) continue;

uint32_t elapsed = now - t->last_tick;  // 处理了溢出

if (elapsed >= t->config.period_ms) {
            t->last_tick = now;

if (t->config.mode == SOFT_TIMER_ONE_SHOT) {
                t->running = false;
            }

if (t->config.callback) {
                t->config.callback(t->config.user_data);
            }
        }
    }
}

使用者怎么用:

// 项目 A(STM32,FreeRTOS)
#include"soft_timer.h"

staticvoidon_blink(void *data) {
    LED_Toggle();
}

voidApp_Init(void) {
// 注入获取时间的函数,FreeRTOS 版本
    SoftTimer_SystemInit(xTaskGetTickCount, 8);

    SoftTimer_Config_t cfg = {
        .period_ms = 500,
        .mode      = SOFT_TIMER_PERIODIC,
        .callback  = on_blink,
        .user_data = NULL,
    };
    SoftTimer_t *blink_timer = SoftTimer_Create(&cfg);
    SoftTimer_Start(blink_timer);
}

voidApp_Loop(void) {
    SoftTimer_Tick();  // 在主循环里调用
}
// 项目 B(NRF52,裸机,自己的 tick 计数)
externvolatileuint32_t g_tick_ms;

uint32_tget_my_tick(void) {
return g_tick_ms;
}

voidApp_Init(void) {
// 注入自己的时间函数
    SoftTimer_SystemInit(get_my_tick, 4);  // 最多4个定时器,省内存
// ...
}

这个组件从 STM32 搬到 NRF52,改动:零。连 #include 都不用改。


组件的"合同":头文件即文档

一个好的头文件,是组件的使用说明书。看完头文件,不看源码,就能正确使用。

头文件里要写清楚的:

前置条件(precondition):

/**
 * 读取一个字节
 * @pre  必须先调用 RingBuffer_Init
 * @pre  调用前确认 !RingBuffer_IsEmpty(rb),否则返回值无意义
 */

boolRingBuffer_Read(RingBuffer_t *rb, uint8_t *out);

行为定义:

/**
 * 写入一个字节
 * @return  true  写入成功
 * @return  false 缓冲区已满,字节被丢弃
 * @note    此函数可以在中断中调用(线程安全)
 */

boolRingBuffer_Write(RingBuffer_t *rb, uint8_t byte);

线程安全说明(嵌入式里极其重要):

/**
 * @note  单生产者、单消费者场景下线程安全(ISR写,主循环读)
 * @note  多生产者或多消费者场景下需要外部加锁
 */

很多组件 bug 来自于"以为它是线程安全的,但其实不是"或者"以为不能在中断里用,其实可以"。这两件事必须在头文件里说清楚。


常见的假组件

有些代码看起来是组件,其实不是。识别它们:

假组件一:头文件里有全局变量

// 不好的 flash.h
externuint8_t flash_write_buf[256];  // 暴露内部状态
externbool flash_busy;               // 使用者不应该直接访问

组件的内部状态不应该暴露。如果需要查询状态,提供查询函数。

假组件二:初始化函数调用了其他模块

// 不好的 display.c
voidDisplay_Init(void) {
    SPI_Init();        // 假设 SPI 还没初始化
    GPIO_Init_LCD();   // 假设 GPIO 还没初始化
    delay_ms(100);     // 假设 delay_ms 可用
}

初始化函数里调用其他模块的初始化,制造了隐式的初始化顺序依赖。这个组件用到其他项目里,必须保证 Display_Init 被调用之前那些前置初始化都完成了,但头文件里没有说这件事,很容易出问题。

正确做法:Display_Init 只做自己的事,需要的外部资源通过参数传进来,或者用文档说明前置条件。

假组件三:只能有一个实例

// 不好的 uart.c
staticuint8_t rx_buffer[128];  // 写死了只支持一个 UART

voidUART_Init(uint32_t baudrate) { /* ... */ }
voidUART_Send(uint8_t *data, uint16_t len) { /* ... */ }

如果你需要两个 UART 怎么办?要复制一份 uart.c、改个名字。这不是组件,是一份源码拷贝。

正确做法:实例作为参数传入。

// 好的设计
typedefstruct {
    USART_TypeDef  *uart_reg;    // 哪个 UART
uint8_t        *rx_buf;
uint16_t        rx_buf_size;
// ...
} UART_Handle_t;

voidUART_Init(UART_Handle_t *handle, const UART_Config_t *config);
voidUART_Send(UART_Handle_t *handle, constuint8_t *data, uint16_t len);

// 使用
UART_Handle_t uart1, uart2;
UART_Init(&uart1, &uart1_config);
UART_Init(&uart2, &uart2_config);

组件测试

真正可复用的组件,应该可以在没有目标硬件的情况下测试。

上面的软件定时器组件,可以完全在 PC 上测试:

// test_soft_timer.c(在 PC 上运行)
#include"soft_timer.h"
#include<stdio.h>
#include<assert.h>

// 模拟时间
staticuint32_t fake_tick = 0;
uint32_tget_fake_tick(void) { return fake_tick; }

intmain(void) {
    SoftTimer_SystemInit(get_fake_tick, 4);

int fire_count = 0;

    SoftTimer_Config_t cfg = {
        .period_ms = 100,
        .mode = SOFT_TIMER_PERIODIC,
        .callback = [](void *d) { (*(int*)d)++; },
        .user_data = &fire_count,
    };

    SoftTimer_t *t = SoftTimer_Create(&cfg);
    SoftTimer_Start(t);

// 模拟时间推进
for (int i = 0; i < 350; i++) {
        fake_tick++;
        SoftTimer_Tick();
    }

    assert(fire_count == 3);  // 100ms、200ms、300ms 各触发一次
printf("Test passed! fire_count = %d\n", fire_count);

return0;
}

能在 PC 上跑测试,说明这个组件:

  • • 没有硬件依赖(硬件依赖都被注入进来了)
  • • 逻辑是独立的
  • • 真的可以在任何环境里用

不能在 PC 上测试的组件,大概率有隐式硬件依赖还没清理干净。


最后说一件事

很多工程师做到上面这些之后,会陷入另一个极端:过度设计。

为了"可复用",把一个简单的功能拆成三层抽象,加了七八个回调,配置结构体有二十个字段。最后这个"组件",比直接写寄存器要难用十倍。

好的组件设计有一个朴素的检验标准:

把这个组件拿给另一个工程师用,他看了头文件,花多长时间能写出第一个能跑的用例?

如果超过30分钟,设计可能太复杂了。

如果5分钟内能跑起来,剩下的学习靠深入看文档和源码,这个组件的接口设计是成功的。

组件化的目标不是"让代码看起来很专业",是"让别人(和一年后的你自己)能快速用起来,用对,用出预期的结果"。

做到这三点,才叫"拿来就用"。

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