当前位置:首页>Linux>Linux 内核 memblock 物理内存分配器:启动阶段的内存管理基石

Linux 内核 memblock 物理内存分配器:启动阶段的内存管理基石

  • 2026-06-30 12:46:56
Linux 内核 memblock 物理内存分配器:启动阶段的内存管理基石

 created_at: “2026-04-28” 

updated_at: “2026-04-28” 

tags:

  • Linux

  • Kernel

  • memory management

  • memblock

  • boot memory

  • physical memory knowledge_sources:

  • page: concepts/memory-management version: “6.12.17” quoted_at: “2026-04-28”


Linux 内核 memblock 物理内存分配器:启动阶段的内存管理基石

1. 分析目标

本文分析 Linux 内核启动阶段的物理内存分配器 memblock 的设计原理、核心数据结构和关键 API。重点回答以下问题:

  • 为什么需要 memblock?它替代了什么?

  • memblock 如何管理物理内存区域?

  • 核心分配和保留 API 的执行流程是什么?

  • 何时以及如何将内存管理权移交给伙伴系统?

本文分析基于 Linux 7.0 源码。

2. 源码位置

项目
内容
实现文件
mm/memblock.c
(2908 行)
接口头文件
include/linux/memblock.h
(623 行)
所属模块
Memory Management(mm/)
内核版本
Linux 7.0
维护者
rppt(Mike Rapoport)
代码仓库
git://git.kernel.org/pub/scm/linux/kernel/git/rppt/memblock.git

3. 前置知识

理解 memblock 需要以下基础知识:

  • 物理内存布局:系统启动时,BIOS/UEFI 或设备树提供物理内存区域信息

  • 伙伴系统(Buddy System):Linux 常规运行时的物理内存分配器,在启动后期才初始化

  • Bootmem:旧的启动期内存分配器,基于位图(bitmap)实现,已被 memblock 替代

  • Early Boot 阶段:内核解压、早期初始化到伙伴系统就绪之前的阶段,此阶段无法使用 kmalloc/vmalloc

memblock 从 Linux 3.0 左右开始替代 bootmem,成为主流的早期内存分配器。与 bootmem 的位图方式不同,memblock 基于区域(region)列表管理内存,支持不连续内存和更灵活的区域属性标记。

Open Question: memory-management.md 中标注"6.12.17 移除了 bootmem,使用 memblock",但 memblock 实际在 ~3.0 内核就已引入。该页面的 kernel_version 标注与内容存在不一致,需进一步确认具体移除 bootmem 的内核版本。

4. 关键数据结构

memblock 的核心设计是将物理内存视为连续区域(regions)的集合,而非单个页帧。所有操作都在 region 级别进行。

4.1 struct memblock_region — 单个内存区域

/* include/linux/memblock.h:34 */struct memblock_region {    phys_addr_t base;           /* 区域基地址 */    phys_addr_t size;           /* 区域大小 */    enum memblock_flags flags;  /* 区域属性标志 */    int nid;                    /* NUMA 节点 ID(仅 CONFIG_NUMA 时存在) */};

每个 region 描述一段连续的物理内存,包含基地址、大小、属性标志和 NUMA 节点信息。

4.2 struct memblock_type — 区域集合

/* include/linux/memblock.h:53 */struct memblock_type {    unsigned long cnt;                /* 当前区域数量 */    unsigned long max;                /* 数组容量上限 */    phys_addr_t total_size;           /* 所有区域总大小 */    struct memblock_region *regions;  /* 区域数组指针 */    char *name;                       /* 类型名称("memory"/"reserved"/"physmem") */};

memblock_type 管理一组同类型的 region。cnt 记录实际使用数量,max 记录数组容量。当 region 数量超过 max 时,会自动触发数组扩容。

4.3 struct memblock — 顶层元数据

/* include/linux/memblock.h:79 */struct memblock {    bool bottom_up;               /* 分配方向:true=自底向上,false=自顶向下 */    phys_addr_t current_limit;    /* 当前分配上限物理地址 */    struct memblock_type memory;  /* 可用内存区域 */    struct memblock_type reserved; /* 已保留区域 */    struct memblock_type physmem; /* 实际物理内存(仅部分架构支持) */};

全局实例(编译时静态初始化):

/* mm/memblock.c:130 */struct memblock memblock __initdata_memblock = {    .memory.regions       = memblock_memory_init_regions,    .memory.max           = INIT_MEMBLOCK_MEMORY_REGIONS,  /* 默认 128 */    .memory.name          = "memory",    .reserved.regions     = memblock_reserved_init_regions,    .reserved.max         = INIT_MEMBLOCK_RESERVED_REGIONS, /* 默认 128 */    .reserved.name        = "reserved",    .bottom_up            = false,    .current_limit        = MEMBLOCK_ALLOC_ANYWHERE,};
/* mm/memblock.c:125-127 */static struct memblock_region memblock_memory_init_regions[INIT_MEMBLOCK_MEMORY_REGIONS] __initdata_memblock;static struct memblock_region memblock_reserved_init_regions[INIT_MEMBLOCK_RESERVED_REGIONS] __initdata_memblock;

memblock 在编译时静态初始化,无需动态分配。memory 和 reserved 各有 128 个初始 region 槽位(INIT_MEMBLOCK_REGIONS = 128)。这保证了它在启动最早期就可使用。

4.4 enum memblock_flags — 区域属性标志

标志
含义
MEMBLOCK_NONE
0x0
无特殊请求
MEMBLOCK_HOTPLUG
0x1
可热插拔的内存区域
MEMBLOCK_MIRROR
0x2
镜像内存区域
MEMBLOCK_NOMAP
0x4
不加入内核直接映射
MEMBLOCK_DRIVER_MANAGED
0x8
由驱动检测的内存
MEMBLOCK_RSRV_NOINIT
0x10
不初始化 struct pages
MEMBLOCK_RSRV_KERN
0x20
内核保留(所有 memblock 分配自动设置)
MEMBLOCK_KHO_SCRATCH
0x40
kexec handover 临时内存

4.5 数据结构层次关系

下图展示了 memblock 的三级层次结构:

上图说明:

  • 顶层:全局 struct memblock 实例,编译时静态初始化

  • 中间层:三个 struct memblock_type(memory、reserved、physmem),各自管理一组 region

  • 底层:region 数组,初始各 128 个槽位,运行时动态扩容

5. Kconfig 依赖分析

memblock 相关配置项定义在 mm/Kconfig 中。以下列出与本文分析相关的配置项:

5.1 CONFIG_HAVE_MEMBLOCK_PHYS_MAP

# mm/Kconfigconfig HAVE_MEMBLOCK_PHYS_MAP    bool

这是一个架构级选择配置(bool,用户不可见)。启用的架构可以在 memblock 中维护 physmem 类型,用于记录实际物理内存(不受 mem= 命令行参数限制)。仅在 CONFIG_HAVE_MEMBLOCK_PHYS_MAP 为 y 时,memblock_physmem_init_regions 和 physmem 结构才会被编译。

/* mm/memblock.c:127-130 */#ifdef CONFIG_HAVE_MEMBLOCK_PHYS_MAPstatic struct memblock_region memblock_physmem_init_regions[INIT_PHYSMEM_REGIONS];#endif/* mm/memblock.c:143-148 */#ifdef CONFIG_HAVE_MEMBLOCK_PHYS_MAPstruct memblock_type physmem = {    .regions    = memblock_physmem_init_regions,    .max        = INIT_PHYSMEM_REGIONS,    .name       = "physmem",};#endif

5.2 CONFIG_ARCH_KEEP_MEMBLOCK

# mm/Kconfigconfig ARCH_KEEP_MEMBLOCK    bool

启用后,memblock 的 region 数据在 memblock_discard() 后不会被释放。这允许在正常运行阶段继续查询 memblock 数据(例如用于内存热插拔的合法性检查)。默认不启用。

5.3 CONFIG_MEMBLOCK_KHO_SCRATCH

# mm/Kconfigconfig MEMBLOCK_KHO_SCRATCH    bool

启用 memblock 对 kexec handover(KHO)临时内存的支持。被 kernel/liveupdate/Kconfig 的 CONFIG_LIVEUPDATE 自动 select

5.4 配置总结

配置项
类型
默认
说明
CONFIG_HAVE_MEMBLOCK_PHYS_MAP
bool(不可见)
架构决定
控制 physmem 类型是否编译
CONFIG_ARCH_KEEP_MEMBLOCK
bool
n
移交后是否保留 memblock 数据
CONFIG_MEMBLOCK_KHO_SCRATCH
bool
被 LIVEUPDATE select
kexec handover 支持

注意:memblock 核心功能(memory + reserved 两种类型)不依赖任何可选 Kconfig。所有架构默认启用 memblock,这是内核启动阶段内存管理的基础设施。

6. 调用链

memblock 在启动阶段的调用链如下:

架构早期初始化 (arch/*)  └─ memblock_add() / memblock_add_node()      /* 告知内核物理内存布局 */      └─ memblock_add_range()                  /* 合并相邻区域,必要时扩容 */内核解压/初始化 (init/main.c)  ├─ memblock_alloc_or_panic()                 /* 分配 saved_command_line 等 */  ├─ memblock_reserve_kern()                   /* 保留内核镜像占用区域 */  └─ memblock_reserve()                        /* 保留 initrd/设备树/ACPI 表 */伙伴系统初始化 (mm/mm_init.c)  ├─ memblock_free_all()                       /* 释放低内存给伙伴系统 */  ├─ mem_init()                                /* 架构特定的 mm 初始化(__weak) */  └─ memblock_discard()                        /* 在 memblock_free_all 内部调用 */

上图说明:

  • 左侧链路:架构代码通过 memblock_add() 注册物理内存,最终调用 memblock_add_range() 合并区域

  • 中间链路init/main.c 初始化期间,memblock_alloc() → memblock_alloc_internal() → memblock_alloc_range_nid() → memblock_find_in_range_node() 根据 bottom_up 标志选择搜索方向

  • 右侧链路mm_core_init() 调用 memblock_free_all() 释放低内存给伙伴系统,再根据 CONFIG_ARCH_KEEP_MEMBLOCK 决定是否释放 memblock 自身数据

7. 核心执行流程

7.1 内存区域添加:memblock_add() → memblock_add_range()

/* mm/memblock.c:752 */int __init_memblock memblock_add(phys_addr_t base, phys_addr_t size){    phys_addr_t end = base + size - 1;    memblock_dbg("%s: [%pa-%pa] %pS\n", __func__,             &base, &end, (void *)_RET_IP);    return memblock_add_range(&memblock.memory, base, size, MAX_NUMNODES, 0);}

memblock_add() 是架构代码告知内核物理内存布局的入口函数。它调用核心函数 memblock_add_range(),实际实现比表面看起来更复杂:

/* mm/memblock.c:611 */static int __init_memblock memblock_add_range(struct memblock_type *type,        phys_addr_t base, phys_addr_t size,        int nid, enum memblock_flags flags){    bool insert = false;    phys_addr_t obase = base;    phys_addr_t end = base + memblock_cap_size(base, &size);    int idx, nr_new, start_rgn = -1, end_rgn;    struct memblock_region *rgn;    if (!size)        return 0;    /* 特殊处理:空数组(第一个 region) */    if (type->regions[0].size == 0) {        WARN_ON(type->cnt != 0 || type->total_size);        type->regions[0].base = base;        type->regions[0].size = size;        type->regions[0].flags = flags;        memblock_set_region_node(&type->regions[0], nid);        type->total_size = size;        type->cnt = 1;        return 0;    }    /* 判断是否需要扩容:最坏情况下需要 type->cnt + 1 个空位 */    if (type->cnt * 2 + 1 <= type->max)        insert = true;repeat:    base = obase;    nr_new = 0;    /* 第一遍:计算需要插入多少个新 region */    for_each_memblock_type(idx, type, rgn) {        phys_addr_t rbase = rgn->base;        phys_addr_t rend = rbase + rgn->size;        if (rbase >= end)            break;        if (rend <= base)            continue;        /* 新区域在现有 region 之前有部分未覆盖 */        if (rbase > base) {            nr_new++;            if (insert)                memblock_insert_region(type, idx++, base,                        rbase - base, nid, flags);        }        base = min(rend, end);    }    /* 插入剩余部分 */    if (base < end) {        nr_new++;        if (insert)            memblock_insert_region(type, idx, base, end - base,                        nid, flags);    }    if (!nr_new)        return 0;    /* 如果需要扩容且是第一遍,则扩容后重复 */    if (!insert) {        while (type->cnt + nr_new > type->max)            if (memblock_double_array(type, obase, size) < 0)                return -ENOMEM;        insert = true;        goto repeat;    } else {        /* 合并相邻区域 */        memblock_merge_regions(type, start_rgn, end_rgn);        return 0;    }}

核心逻辑:

  1. 空数组特殊处理:如果数组为空(第一个 region),直接填入,不需要合并逻辑

  2. 两遍算法:第一遍计算需要插入多少个新 region,第二遍实际插入

  3. 重叠区域分割:新区域与现有区域重叠时,分割成多个部分分别插入

  4. 自动扩容:如果空间不足,调用 memblock_double_array() 翻倍数组容量

  5. 合并相邻区域:插入后调用 memblock_merge_regions() 合并相邻且属性相同的 region,保持列表紧凑

7.2 内存保留:memblock_reserve()

/* mm/memblock.c:1006 */int __init_memblock __memblock_reserve(phys_addr_t base, phys_addr_t size,                    int nid, enum memblock_flags flags){    return memblock_add_range(&memblock.reserved, base, size, nid, flags);}

memblock_reserve() 将一段内存标记为已保留。它的实现完全复用 memblock_add_range(),只是操作的是 memblock.reserved 列表而非 memblock.memory

保留的内存不会被后续分配使用。典型场景:

  • 内核镜像自身占用的区域

  • initrd/initramfs 区域

  • 设备树(DT)blob

  • ACPI 表

  • 固件保留区域

所有通过 memblock 分配函数分配的内存都会自动带上 MEMBLOCK_RSRV_KERN 标志(内核保留)。

7.3 内存分配:从 memblock_phys_alloc() 到 memblock_alloc_range_nid()

memblock 提供两类分配接口:物理地址返回 和 虚拟地址返回

物理地址分配:memblock_phys_alloc()

/* include/linux/memblock.h:407-411 */static __always_inline phys_addr_t memblock_phys_alloc(phys_addr_t size,                        phys_addr_t align){    return memblock_phys_alloc_range(size, align, 0,                     MEMBLOCK_ALLOC_ACCESSIBLE);}

memblock_phys_alloc() 是一个 static __always_inline 函数,直接委托给 memblock_phys_alloc_range():

/* mm/memblock.c:1652 */phys_addr_t __init memblock_phys_alloc_range(phys_addr_t size,                    phys_addr_t align,                    phys_addr_t start,                    phys_addr_t end){    memblock_dbg("%s: %llu bytes align=0x%llx from=%pa max_addr=%pa %pS\n",             __func__, (u64)size, (u64)align, &start, &end,             (void *)_RET_IP);    return memblock_alloc_range_nid(size, align, start, end, NUMA_NO_NODE,                    false);}

核心分配路径:memblock_alloc_range_nid()

这是所有 memblock 分配 API 的最终公共路径:

/* mm/memblock.c:1566 */phys_addr_t __init memblock_alloc_range_nid(phys_addr_t size,                    phys_addr_t align, phys_addr_t start,                    phys_addr_t end, int nid,                    bool exact_nid){    enum memblock_flags flags = choose_memblock_flags();    /* 安全保护:slab 就绪后不应再调用 memblock */    if (WARN_ON_ONCE(slab_is_available())) {        void *vaddr = kzalloc_node(size, GFP_NOWAIT, nid);        return vaddr ? virt_to_phys(vaddr) : 0;    }    if (!align) {        dump_stack();        align = SMP_CACHE_BYTES;    }again:    found = memblock_find_in_range_node(size, align, start, end, nid,                        flags);    if (found && !__memblock_reserve(found, size, nid, MEMBLOCK_RSRV_KERN))        goto done;    /* 如果指定了 NUMA 节点但非精确分配,尝试任意节点 */    if (numa_valid_node(nid) && !exact_nid) {        found = memblock_find_in_range_node(size, align, start,                        end, NUMA_NO_NODE, flags);        if (found && !memblock_reserve_kern(found, size))            goto done;    }    /* MEMBLOCK_MIRROR 标志失败后重试 */    if (flags & MEMBLOCK_MIRROR) {        flags &= ~MEMBLOCK_MIRROR;        pr_warn_ratelimited("Could not allocate %pap bytes of mirrored memory\n",            &size);        goto again;    }    return 0;done:    kmemleak_alloc_phys(found, size, 0);    accept_memory(found, size);    return found;}

关键设计点:

  1. 分配后自动保留:找到空闲区域后立即调用 __memblock_reserve() 标记为 reserved,防止重复分配

  2. NUMA fallback:如果指定节点分配失败且非精确分配(exact_nid=false),自动降级到任意节点

  3. MEMBLOCK_MIRROR 重试:镜像内存分配失败后,去掉 MIRROR 标志重试

  4. 安全保护:slab 就绪后调用会触发 WARN 并降级使用 kzalloc

  5. 内存接受:在 TDX/SEV-SNP 等机密计算环境中,分配后需要调用 accept_memory() 接受内存

搜索方向:__memblock_find_range_bottom_up vs __memblock_find_range_top_down

memblock_find_in_range_node() 根据 memblock.bottom_up 标志选择不同的搜索函数:

/* mm/memblock.c:309 */static phys_addr_t __init_memblock memblock_find_in_range_node(phys_addr_t size,                phys_addr_t align, phys_addr_t start,                phys_addr_t end, int nid,                enum memblock_flags flags){    /* end = MEMBLOCK_ALLOC_ACCESSIBLE 时使用 current_limit */    if (end == MEMBLOCK_ALLOC_ACCESSIBLE ||        end == MEMBLOCK_ALLOC_NOLEAKTRACE)        end = memblock.current_limit;    /* 避免分配第 0 页(NULL 指针保护) */    start = max_t(phys_addr_t, start, PAGE_SIZE);    end = max(start, end);    if (memblock_bottom_up())        return __memblock_find_range_bottom_up(start, end, size, align,                               nid, flags);    else        return __memblock_find_range_top_down(start, end, size, align,                              nid, flags);}

两个搜索函数的核心区别在于遍历方向:

  • bottom_up:使用 for_each_free_mem_range(),从低地址向高地址扫描,返回第一个满足条件的地址

  • top_down:使用 for_each_free_mem_range_reverse(),从高地址向低地址扫描,返回最后一个满足条件的地址

默认 top-down 策略保护低端内存供 DMA 等硬件使用。

上图说明:

  • 物理内存布局:绿色为空闲区域,红色为已保留区域(内核镜像、DTB 等)

  • top-down(默认):使用 for_each_free_mem_range_reverse(),从高地址向低地址扫描,优先分配 0x40000-0x9FFFF,保护低端内存

  • bottom-up:使用 for_each_free_mem_range(),从低地址向高地址扫描,优先分配 0x10000-0x2FFFF

虚拟地址分配:memblock_alloc()

/* include/linux/memblock.h:424-427 */static __always_inline void *memblock_alloc(phys_addr_t size, phys_addr_t align){    return memblock_alloc_try_nid(size, align, MEMBLOCK_LOW_LIMIT,                      MEMBLOCK_ALLOC_ACCESSIBLE, NUMA_NO_NODE);}

虚拟地址分配内部实现:memblock_alloc_internal()

这是所有返回虚拟地址的 memblock 分配 API 的公共路径:

/* mm/memblock.c:1703 */static void * __init memblock_alloc_internal(                phys_addr_t size, phys_addr_t align,                phys_addr_t min_addr, phys_addr_t max_addr,                int nid, bool exact_nid){    phys_addr_t alloc;    if (max_addr > memblock.current_limit)        max_addr = memblock.current_limit;    alloc = memblock_alloc_range_nid(size, align, min_addr, max_addr, nid,                    exact_nid);    /* 如果分配失败,取消 min_addr 限制重试 */    if (!alloc && min_addr)        alloc = memblock_alloc_range_nid(size, align, 0, max_addr, nid,                        exact_nid);    if (!alloc)        return NULL;    return phys_to_virt(alloc);}

关键设计:

  1. 取消下限重试:如果带 min_addr 限制分配失败,会取消下限限制再试一次

  2. 虚拟地址转换:使用 phys_to_virt() 将物理地址转换为虚拟地址(非 __va(),两者在部分架构上可能有差异)

7.4 分配失败处理:memblock_alloc_or_panic()

/* mm/memblock.c:1837 */void *__init __memblock_alloc_or_panic(phys_addr_t size, phys_addr_t align,                       const char *func){    void *addr = memblock_alloc(size, align);    if (unlikely(!addr))        panic("%s: Failed to allocate %pap bytes\n", func, &size);    return addr;}

对于关键分配(如命令行缓冲区),使用 memblock_alloc_or_panic() 确保分配失败时内核直接 panic,而不是继续运行在不一致状态。

8. 分支路径分析

8.1 动态扩容:memblock_double_array()

当 region 数量超过数组容量时,memblock 会自动扩容:

/* mm/memblock.c:430 */static int __init_memblock memblock_double_array(struct memblock_type *type,                    phys_addr_t new_area_start,                    phys_addr_t new_area_size){    struct memblock_region *new_array, *old_array;    phys_addr_t old_alloc_size, new_alloc_size;    phys_addr_t old_size, new_size, addr, new_end;    int use_slab = slab_is_available();    int *in_slab;    /* 检查扩容开关 */    if (!memblock_can_resize)        panic("memblock: cannot resize %s array\n", type->name);    old_size = type->max * sizeof(struct memblock_region);    new_size = old_size << 1;    old_alloc_size = PAGE_ALIGN(old_size);    new_alloc_size = PAGE_ALIGN(new_size);    /* 判断当前数组是否在 slab 中分配 */    if (type == &memblock.memory)        in_slab = &memblock_memory_in_slab;    else        in_slab = &memblock_reserved_in_slab;    /* 尝试分配新数组 */    if (use_slab) {        new_array = kmalloc(new_size, GFP_KERNEL);        addr = new_array ? __pa(new_array) : 0;    } else {        /* slab 不可用时,通过 memblock 自身查找空闲空间 */        if (type != &memblock.reserved)            new_area_start = new_area_size = 0;        addr = memblock_find_in_range(new_area_start + new_area_size,                memblock.current_limit,                new_alloc_size, PAGE_SIZE);        if (!addr && new_area_size)            addr = memblock_find_in_range(0,                min(new_area_start, memblock.current_limit),                new_alloc_size, PAGE_SIZE);        if (addr) {            accept_memory(addr, new_alloc_size);            new_array = __va(addr);        } else {            new_array = NULL;        }    }    if (!addr) {        pr_err("memblock: Failed to double %s array from %ld to %ld entries !\n",               type->name, type->max, type->max * 2);        return -1;    }    /* 复制数据 */    memcpy(new_array, type->regions, old_size);    memset(new_array + type->max, 0, old_size);    old_array = type->regions;    type->regions = new_array;    type->max <<= 1;    /* 释放旧数组 */    if (*in_slab)        kfree(old_array);    else if (old_array != memblock_memory_init_regions &&         old_array != memblock_reserved_init_regions)        memblock_free(old_array, old_alloc_size);    /* 新数组标记为 reserved */    if (!use_slab)        BUG_ON(memblock_reserve_kern(addr, new_alloc_size));    *in_slab = use_slab;    return 0;}

扩容策略:

  1. 容量翻倍new_size = old_size << 1,对齐到 PAGE_SIZE

  2. 分配器选择

    • slab 可用:使用 kmalloc(),设置 in_slab = true

    • slab 不可用:使用 memblock_find_in_range() 在 memblock 自身查找空间

  3. 旧数组释放

    • slab 分配的:kfree()

    • memblock 分配的(且不是静态初始数组):memblock_free()

    • 静态初始数组:不释放

  4. 扩容开关:必须调用 memblock_allow_resize() 设置 memblock_can_resize = true 后才允许扩容

8.2 分配方向控制

/* mm/memblock.c:2173 */void __init memblock_allow_resize(void){    memblock_can_resize = true;}void __init memblock_set_bottom_up(bool enable){    memblock.bottom_up = enable;}
  • 默认:top-down(自顶向下),保护低端内存

  • 可选:bottom-up(自底向上),通过 memblock_set_bottom_up(true) 切换

  • 扩容开关:必须调用 memblock_allow_resize() 后才允许动态扩容

9. 并发与生命周期分析

9.1 单线程设计

memblock 不是线程安全的。它设计为单线程环境使用,仅在内核启动早期(SMP 尚未启动)被调用。因此:

  • 无锁保护

  • 无原子操作

  • 依赖启动阶段的单线程执行模型

一旦 SMP 启动后,不应再调用 memblock API。代码中通过 WARN_ON_ONCE(slab_is_available()) 作为安全保护,如果 slab 就绪后调用分配函数会触发 WARN 并降级使用 kzalloc。

9.2 生命周期:从初始化到移交

memblock 的生命周期分为三个阶段,状态转换如下:

上图说明:

  • 静态初始化:编译时静态初始化,无需运行时分配

  • 阶段 1(添加区域):架构代码注册物理内存布局

  • 阶段 2(分配与保留):内核解压/初始化期间分配数据结构和保留关键区域,可能触发动态扩容

  • 阶段 3(移交):通过 free_low_memory_core_early() 将空闲页释放给伙伴系统

  • CONFIG_ARCH_KEEP_MEMBLOCK 决策点:=n 则释放 memblock 自身内存(默认),=y 则保留供热插拔使用

memblock 的生命周期具体分为三个阶段:

阶段 1:初始化(架构早期)

  • 全局 memblock 结构体编译时静态初始化

  • 架构代码调用 memblock_add() / memblock_add_node() 注册物理内存布局

  • 初始区域数组大小:INIT_MEMBLOCK_REGIONS = 128

阶段 2:分配与保留(内核解压/初始化)

  • init/main.c 中分配命令行缓冲区、未知选项缓冲区等

  • init/initramfs.c 中保留 initramfs 区域

  • 各子系统保留自身使用的内存区域

阶段 3:移交(伙伴系统就绪)

移交过程发生在 mm/mm_init.c:mm_core_init() 中:

/* mm/mm_init.c:2717 */void __init mm_core_init(void){    arch_mm_preinit();    init_zero_page_pfn();    /* ... */    /* 将可用内存释放给伙伴系统 */    memblock_free_all();    /* 架构特定的 mm 初始化(默认为空函数) */    mem_init();    /* slab 分配器初始化(此后 memblock 不应再被调用) */    kmem_cache_init();    /* ... */}

核心移交函数 memblock_free_all():

/* mm/memblock.c:2431 */void __init memblock_free_all(void){    unsigned long pages;    free_unused_memmap();    reset_all_zones_managed_pages();    memblock_clear_kho_scratch_only();    pages = free_low_memory_core_early();    totalram_pages_add(pages);}

移交流程:

  1. free_unused_memmap():释放稀疏内存模型中未使用的 memmap 页

  2. reset_all_zones_managed_pages():重置所有内存区域的 managed_pages 计数

  3. free_low_memory_core_early():遍历 for_each_free_mem_range(),将 memblock 中所有空闲区域释放给伙伴系统(调用 __free_pages_core()

  4. totalram_pages_add(pages):更新总内存页数统计

  5. memblock_discard():如果未启用 CONFIG_ARCH_KEEP_MEMBLOCK,释放 memblock 自身的 region 数组

memblock_discard() 的实现:

/* mm/memblock.c:384 */void __init memblock_discard(void){    phys_addr_t size;    void *addr;    if (memblock.reserved.regions != memblock_reserved_init_regions) {        addr = memblock.reserved.regions;        size = PAGE_ALIGN(sizeof(struct memblock_region) *                  memblock.reserved.max);        if (memblock_reserved_in_slab)            kfree(addr);        else            memblock_free(addr, size);    }    if (memblock.memory.regions != memblock_memory_init_regions) {        addr = memblock.memory.regions;        size = PAGE_ALIGN(sizeof(struct memblock_region) *                  memblock.memory.max);        if (memblock_memory_in_slab)            kfree(addr);        else            memblock_free(addr, size);    }    memblock_memory = NULL;}#endif /* CONFIG_ARCH_KEEP_MEMBLOCK 的 #else 分支 */

如果启用了 CONFIG_ARCH_KEEP_MEMBLOCK,memblock_discard() 为空函数(被 #ifdef 排除),memblock 数据在移交后仍然可用。

10. 调试与验证方法

10.1 验证环境

项目
说明
内核版本
Linux 7.0(源码路径相对于内核根目录)
CPU 架构
源码分析针对通用实现,架构差异见 Section 3 前置知识
内核配置
CONFIG_DEBUG_FS=y
(debugfs 支持)、CONFIG_FTRACE=y(ftrace 支持)
运行环境
需要 root 权限访问 debugfs 和 ftrace

10.2 查看 memblock 状态

内核启动时可以通过 dmesg 查看 memblock 的内存布局信息:

# 在已启动的系统上执行dmesg | grep -i memblock

典型输出示例(x86_64 虚拟机,2GB 内存):

[    0.000000] memblock: 128 memory regions[    0.000000] memblock:   0: [0x0000001000-0x000009ffff] 639KB[    0.000000] memblock:   1: [0x0000100000-0x0000ffffff] 15MB[    0.000000] memblock:   2: [0x0001000000-0x7fffffff] 2047MB[    0.000000] memblock: reserved regions:[    0.000000] memblock:   0: [0x0000001000-0x0000001fff] 4KB (kernel)[    0.000000] memblock:   1: [0x0001000000-0x00015fffff] 6MB (kernel)

注意:实际输出取决于内核日志级别和架构实现。部分架构可能不会在 dmesg 中打印 memblock 详细信息。

10.3 通过 debugfs 查看信息

# 在内核根目录下执行(需要 root 权限)cat /sys/kernel/debug/memblock/memorycat /sys/kernel/debug/memblock/reserved

输出格式:每行一个 region,显示基地址和大小。

预期输出:

  • memory 文件:列出所有可用物理内存区域

  • reserved 文件:列出所有已保留区域(内核镜像、initrd 等)

10.4 使用 ftrace 追踪分配

# 在 sysfs 挂载点下执行(需要 root 权限)echo function > /sys/kernel/debug/tracing/current_tracerecho 'memblock_*' > /sys/kernel/debug/tracing/set_ftrace_filterecho 1 > /sys/kernel/debug/tracing/tracing_on# 运行一段时间查看结果cat /sys/kernel/debug/tracing/trace | head -50

预期输出:显示所有 memblock_* 函数的调用记录,包括调用者、参数和返回值。

10.5 在内核模块中验证

在内核代码(非模块,因为 memblock 仅在 early boot 可用)或自定义启动代码中,可以添加以下验证代码:

/* 在 arch/*/mm/init.c 或类似位置添加 */phys_addr_t total = memblock_phys_mem_size();phys_addr_t reserved = memblock_reserved_size();pr_info("memblock: total=%pa, reserved=%pa, free=%pa\n",    &total, &reserved, &(total - reserved));

注意:memblock API 仅在 memblock_free_all() 调用之前可用。在常规内核模块中调用会触发 WARN_ON_ONCE(slab_is_available()) 并返回错误值。

11. 总结

memblock 解决了什么问题

memblock 替代了旧的 bootmem 分配器,提供更高效、更灵活的早期物理内存管理:

  • 基于区域而非位图:支持不连续内存,避免位图带来的内存浪费

  • 支持区域属性标记:MEMBLOCK_NOMAP、MEMBLOCK_HOTPLUG 等标志实现精细化管理

  • 自动合并相邻区域:减少碎片,保持 region 列表紧凑

  • 动态扩容:支持复杂系统的大量内存区域

  • NUMA 感知:支持指定 NUMA 节点分配,提供 exact_nid 控制

核心结论

  1. memblock 是编译时静态初始化的全局分配器,memory 和 reserved 各有 128 个初始槽位,无需依赖 kmalloc/slab

  2. 核心数据结构是三级层次:memblock → memblock_type → memblock_region

  3. 分配策略默认自顶向下(top-down),保护低端内存;分配后自动标记为 reserved

  4. 核心分配公共路径是 memblock_alloc_range_nid(),支持 NUMA fallback 和 MEMBLOCK_MIRROR 重试

  5. 在 mm_core_init() 中通过 memblock_free_all() → free_low_memory_core_early() 移交内存管理权给伙伴系统,自身释放或保留

实践中应该注意什么

  • memblock 仅在启动早期单线程环境使用,SMP 启动后不应调用

  • 分配失败时关键路径使用 memblock_alloc_or_panic() 直接 panic

  • 启用 CONFIG_ARCH_KEEP_MEMBLOCK 可在移交后保留 memblock 数据(用于内存热插拔等场景)

  • region 数组初始容量 128,复杂系统可能需要调用 memblock_allow_resize() 启用动态扩容

  • memblock_phys_alloc() 返回物理地址,memblock_alloc() 返回虚拟地址,按需选择

  • 所有配置项(CONFIG_HAVE_MEMBLOCK_PHYS_MAP、CONFIG_ARCH_KEEP_MEMBLOCK)均为 bool 不可见配置,由架构或上层配置自动决定

后续可以继续研究什么

  • memblock 在 NUMA 系统上的节点分配策略(memblock_alloc_try_nid、exact_nid 参数)

  • 不同架构(x86、ARM64、RISC-V)的 memblock 初始化差异

  • memblock 与设备树内存节点的集成方式

  • CONFIG_ARCH_KEEP_MEMBLOCK 开启后的内存热插拔支持

  • memblock 与 kexec handover(KHO)的交互

  • memblock 中 confidential computing(TDX/SEV-SNP)的 accept_memory 流程

12. 参考资料

  • Linux Kernel Documentation: Memory Management

  • Linux kernel source tree (v7.0): mm/memblock.c (2908 行), include/linux/memblock.h (623 行), mm/mm_init.cmm/Kconfig

  • Mike Rapoport: memblock maintainer, git://git.kernel.org/pub/scm/linux/kernel/git/rppt/memblock.git

  • init/main.cinit/initramfs.c 中的 memblock 使用示例

本文主要基于 Linux 7.0 源码分析整理。

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-07-04 16:55:51 HTTP/2.0 GET : https://f.mffb.com.cn/a/489876.html
  2. 运行时间 : 0.104333s [ 吞吐率:9.58req/s ] 内存消耗:4,919.92kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=d445eff2ce4791b5f11a1f0cea3595ab
  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.000428s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.000804s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000476s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000283s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.000469s ]
  6. SELECT * FROM `set` [ RunTime:0.000207s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.000481s ]
  8. SELECT * FROM `article` WHERE `id` = 489876 LIMIT 1 [ RunTime:0.000648s ]
  9. UPDATE `article` SET `lasttime` = 1783155351 WHERE `id` = 489876 [ RunTime:0.027913s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 67 LIMIT 1 [ RunTime:0.000262s ]
  11. SELECT * FROM `article` WHERE `id` < 489876 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.000562s ]
  12. SELECT * FROM `article` WHERE `id` > 489876 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.000539s ]
  13. SELECT * FROM `article` WHERE `id` < 489876 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.000847s ]
  14. SELECT * FROM `article` WHERE `id` < 489876 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.001609s ]
  15. SELECT * FROM `article` WHERE `id` < 489876 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.004549s ]
0.105897s