当前位置:首页>Linux>解决Linux内核内存问题:MMU机制实操解析

解决Linux内核内存问题:MMU机制实操解析

  • 2026-02-10 09:18:41
解决Linux内核内存问题:MMU机制实操解析

内存管理是 Linux 内核的核心支柱,而内存泄漏、地址冲突、访问异常等问题,往往成为内核开发与运维中的“拦路虎”,其根源多与内存地址映射的底层逻辑密切相关。MMU(内存管理单元)作为 CPU 与内存之间的关键中间层,承担着虚拟地址与物理地址转换、内存访问权限控制、内存分页/分段管理等核心职责,是解决 Linux 内核内存问题的核心突破口。脱离 MMU 机制的实操认知,仅靠表面排查,难以从根本上解决内核内存异常。

本文将跳出纯理论框架,聚焦 MMU 机制的实操落地,结合 Linux 内核内存管理的实际场景,拆解 MMU 的工作原理、地址转换流程,剖析其在解决内存隔离、权限管控、碎片化等常见内核内存问题中的作用。通过实操案例串联核心知识点,帮助开发者快速掌握 MMU 机制的调试方法与应用技巧,打通从理解原理到解决实际内存问题的链路,为 Linux 内核内存优化、故障排查提供可落地的实操指引。

一、MMU 机制基础概念

1.1 MMU 是什么

MMU,即内存管理单元(Memory Management Unit) ,从硬件层面来讲,它是计算机中专门处理中央处理器(CPU)内存访问请求的关键硬件。打个比方,计算机内存像一个大型仓库,里面存放着各类数据和程序,而运行的众多程序就如同来取物资的客户。如果没有管理机制,客户随意翻找,仓库就会混乱,物资易损坏丢失。MMU 就像仓库大管家,制定严格有序的管理规则。每个程序访问内存,都要通过 MMU “登记” 和 “授权”,它把程序的 “需求指令”(虚拟地址)精准转换为仓库中实际的 “物资存放位置”(物理地址),确保程序顺利获取所需,也保证了内存的秩序和数据安全。

从功能上看,MMU 主要有两大核心功能。一方面是虚拟地址到物理地址的转换 。在现代操作系统中,为了给每个进程提供独立的地址空间,引入了虚拟地址的概念。每个进程都认为自己拥有从 0 开始的连续内存空间,而 MMU 负责将进程使用的虚拟地址映射到实际的物理内存地址上,就像在庞大的图书馆中,快速准确地找到所需书籍的位置,让每个进程仿佛拥有专属的内存天地,彼此独立运行,互不干扰。

另一方面,MMU 提供硬件机制的内存访问授权,严格审查每一次内存访问请求,只有合法的访问才能通过,有效杜绝非法操作对系统内存的破坏,为系统稳定运行筑牢防线。例如,当一个进程试图访问不属于它的内存区域时,MMU 会及时阻止,并向操作系统报告,防止进程间相互干扰和数据泄露 。

1.2 MMU 起源

在计算机发展的早期阶段,硬件资源十分有限,就像是一个狭小的仓库,内存空间非常小,而且程序对内存的访问是直接而简单粗暴的,就如同在一个小房间里随意堆放物品,没有任何管理规则。程序员需要手动分配和释放内存,这就要求他们对内存的使用有深入的了解,稍有不慎就可能出现内存泄漏或其他错误,就像在小房间里找东西时,不小心把东西放错地方或者弄丢了一样。那个时候,程序直接访问物理内存,操作系统也只是简单地 “加载”“运行” 或 “卸载” 应用程序。

随着计算机技术的飞速发展和软件的不断膨胀,计算机需要处理的任务越来越复杂,内存需求也越来越大。就好比一个小仓库要容纳越来越多的物资,单任务批处理的模式已经无法满足需求,多任务处理的需求应运而生。同时,应用程序所需的内存量也不断增加,甚至超过了物理内存的大小。这就像小仓库已经装不下所有的物资了,怎么办呢?

为了解决这些问题,聪明的计算机科学家们提出了虚拟内存的思想。虚拟内存就像是给计算机内存这个小仓库加了一个 “虚拟扩展空间”,程序所需的内存可以远超物理内存的大小,操作系统会把当前需要执行的部分留在内存中,而不需要执行的部分留在磁盘中,就像把暂时不用的物资存放到仓库外面的临时存储区。这样,就可以满足多个应用程序同时驻留内存并并发执行,就好像在小仓库有限的空间里,通过合理调配物资,让多个客户都能顺利拿到自己需要的东西。

在这样的背景下,MMU 应运而生,它就像是专门为管理这个复杂的内存 “仓库” 而聘请的高级大管家。MMU 接替了操作系统内存管理中比较复杂的部分,比如地址翻译,将虚拟地址翻译成物理地址,就像大管家能够准确地把客户的 “虚拟需求位置” 转换为实际的 “物资存放位置”。同时,内存访问效率则交给了 cache(高速缓存)去做,或者通过提高内存总线的带宽来实现,就像给仓库配备了快速通道,让物资的搬运更加高效。

1.3 为什么需要 MMU

在没有 MMU 的早期计算机系统中,程序直接访问物理内存,这种方式存在诸多严重问题。从内存安全角度来说,多个程序共享物理内存,它们可以随意访问和修改内存中的任何数据,一个程序的错误操作就可能导致其他程序甚至整个系统崩溃。例如,某个程序因为编写错误,意外修改了其他程序正在使用的内存数据,就会使被修改数据的程序无法正常运行,就像在一个共享的文件柜里,有人不小心把别人存放的重要文件给涂改了,导致文件所有者无法正常使用文件。

再从内存碎片化角度来看,随着程序的频繁加载和卸载,物理内存容易出现碎片化。内存碎片化是指内存中出现大量不连续的小空闲块,这些小空闲块难以满足大程序的内存分配需求。比如,有一个大程序需要一块连续的较大内存空间来运行,但由于内存碎片化,虽然总的空闲内存空间足够,但都是分散的小块,无法为该程序提供连续的大块内存,导致程序无法正常运行,这就好比一个大箱子要放进一个堆满零散物品的仓库,虽然仓库空间总体够大,但没有一块连续的空间能放下这个大箱子。

而 MMU 通过引入虚拟地址空间,很好地解决了这些问题。对于内存安全问题,MMU 为每个进程分配独立的虚拟地址空间,不同进程的虚拟地址空间相互隔离,一个进程无法直接访问其他进程的内存区域,有效防止了进程间的非法访问和干扰 。对于内存碎片化问题,MMU 采用分页或分段等内存管理技术,将物理内存划分成固定大小的页或根据程序逻辑结构划分成段,程序使用的虚拟地址可以映射到不连续的物理页或段上,操作系统通过管理页表或段表来实现虚拟地址到物理地址的转换,从而避免了物理内存碎片化对程序运行的影响。就好像把大箱子拆分成一个个小箱子(分页),分别放到仓库不同位置,然后通过一个清单(页表)记录每个小箱子的存放位置,需要使用大箱子时,再根据清单把小箱子组合起来,这样就解决了仓库空间不连续无法存放大型物品的问题。

二、MMU 核心工作原理

2.1 地址转换机制

在 Linux 系统中,地址转换是 MMU 最核心的功能之一,它如同一场精密的魔术表演,将程序使用的虚拟地址巧妙地转换为实际的物理地址 。其转换过程大致如下:当 CPU 产生一个虚拟地址时,MMU 会首先介入。假设我们有一个 32 位的虚拟地址,它会被 MMU 按照特定规则划分为两部分:页号和页内偏移 。例如,对于一个页大小为 4KB(2 的 12 次方字节)的系统,虚拟地址的低 12 位就是页内偏移,而剩下的高位部分则是页号 。

MMU 会拿着这个页号去查找页表(Page Table),页表就像是一本详细的地址映射字典,记录着每个虚拟页对应的物理页框号 。通过页号作为索引,MMU 在页表中精准定位到对应的表项,从该表项中获取物理页框号 。然后,MMU 将获取到的物理页框号与虚拟地址中的页内偏移进行组合,最终得到实际的物理地址 。例如,假设虚拟地址 0x08048000,经过划分,页号为 0x0804,页内偏移为 0x8000,通过页表查到对应的物理页框号为 0x1000,那么最终的物理地址就是 0x10008000 。这个物理地址就会被用于访问实际的物理内存,获取程序所需的数据或指令 。整个地址转换过程虽然看似复杂,但在 MMU 的高效运作下,几乎是瞬间完成的,为程序的快速运行提供了坚实保障 。

2.2 页表:地址转换的关键

页表在地址转换过程中扮演着至关重要的角色,它是虚拟地址与物理地址映射关系的关键记录者 。从结构上看,页表是一个数据结构,通常以数组的形式存在 。在 32 位系统中,若页大小为 4KB,那么页表中可能会有多达 100 万个条目(因为 2 的 32 次方除以 2 的 12 次方等于 2 的 20 次方,即 1048576) 。每个条目对应一个虚拟页,被称为页表项(Page Table Entry,PTE) 。

页表项包含了丰富且关键的信息 。其中,物理帧号(Frame Number)是最为重要的部分,它明确指示了与虚拟页相对应的物理内存块编号 。例如,当 MMU 通过页号找到对应的页表项后,就可以从该页表项的物理帧号字段中获取到物理页框号,从而完成虚拟地址到物理地址的关键转换步骤 。有效位(Valid/Present Bit)也是一个重要字段,它用于指示对应的页是否当前存在于物理内存中 。若此位为 1,表示该页已成功加载到内存中,可以直接访问;若为 0,则表明该页未加载或已被交换到磁盘上,此时访问该页会触发缺页异常,操作系统需要从磁盘中读取该页到内存 。

修改位(Modified/Dirty Bit)用于记录页面的修改情况 。当某一页的数据被写入时,该标志位会被设置 。它在操作系统进行页面置换算法时发挥着关键作用,比如当内存不足需要淘汰某些页面时,如果一个页面的修改位被设置,就意味着该页面在内存中被修改过,需要先将其写回到磁盘,然后才能被替换出去 。访问位(Accessed/Referenced Bit)则记录了页面的访问情况 。每当进程访问某个特定的虚存页时,这个标记就会被更新 。它可以辅助诸如 LRU(Least Recently Used,最近最少使用)之类的页面调度策略,通过判断哪些页面最近没有被使用过,从而优先考虑淘汰这些页面 。

mmu 开启以后会有以下特点:

  1. 多个程序独立运行
  2. 虚拟地址是连续的(物理内存可以有碎片)
  3. 允许操作系统管理内存

下图显示的系统说明了内存的虚拟和物理视图。单个系统中的不同处理器和设备可能具有不同的虚拟地址映射和物理地址映射。操作系统编写程序,使 MMU 在这两个内存视图之间进行转换:

要做到这一点,虚拟内存系统中的硬件必须提供地址转换,即将处理器发出的虚拟地址转换为主内存中的物理地址。MMU 使用虚拟地址中最重要的位来索引转换表中的条目,并确定正在访问哪个块。MMU 将代码和数据的虚拟地址转换为实际系统中的物理地址。该转换将在硬件中自动执行,并且对应用程序是透明的。除了地址转换之外,MMU 还可以控制每个内存区域的内存访问权限、内存顺序和缓存策略。

MMU 对执行的任务或应用程序可以不了解系统的物理内存映射,也可以不了解同时运行的其他程序。每个程序可以使用相同的虚拟内存地址空间。即使物理内存是碎片化的,还可以使用一个连续的虚拟内存映射。此虚拟地址空间与系统中内存的实际物理映射分开的。应用程序被编写、编译和链接,以在虚拟内存空间中运行。

2.3 TLB:加速地址转换

TLB(Translation Lookaside Buffer),即地址转换后备缓冲器,是 MMU 加速地址转换的秘密武器 。它的工作原理基于一个简单而高效的理念:缓存最近使用的地址转换信息 。TLB 本质上是一种高速缓存,就像是一个小型的、快速访问的地址映射表,存储了近期最常访问的页表项 。

当 CPU 产生一个虚拟地址并需要进行地址转换时,MMU 会首先在 TLB 中查找 。如果在 TLB 中找到了与该虚拟地址对应的有效页表项,即发生了 TLB 命中(TLB Hit),那么 MMU 可以直接从 TLB 中获取物理页号,然后与虚拟地址中的页内偏移组合得到物理地址,整个过程非常迅速,几乎可以忽略不计 。这就好比你在一个常用物品存放处(TLB)中快速找到了你需要的物品(地址转换信息),无需再去大型仓库(内存中的页表)中慢慢查找 。

然而,如果在 TLB 中没有找到对应的页表项,即发生了 TLB 未命中(TLB Miss),情况就会稍微复杂一些 。此时,MMU 不得不按照常规流程,去内存中的页表中查找相应的页表项 。这个过程需要多次内存访问,因为现代操作系统通常采用多级页表结构,查找过程较为耗时 。找到页表项后,MMU 不仅会完成地址转换,还会将这个新的页表项缓存到 TLB 中,以便下次访问相同虚拟地址时能够快速命中 。就好像你在大型仓库中找到了物品后,顺手将它放在了常用物品存放处,方便下次快速取用 。

通过 TLB 的高速缓存机制,地址转换的速度得到了极大提升 。在实际运行中,由于程序的局部性原理,即程序在一段时间内往往会集中访问某些特定的内存区域,TLB 的命中率通常较高,这使得大多数地址转换操作都能快速完成,有效减少了内存访问延迟,提高了系统整体性能 。

如上图所示,TLB 是 MMU 中最近访问的页面翻译的缓存。对于处理器执行的每个内存访问,MMU 将检查转换是否缓存在 TLB 中。如果所请求的地址转换在 TLB 中导致命中,则该地址的翻译立即可用。TLB 本质是一块高速缓存。数据 cache 缓存地址(虚拟地址或者物理地址)和数据。TLB 缓存虚拟地址和其映射的物理地址。TLB 根据虚拟地址查找 cache,它没得选,只能根据虚拟地址查找。所以 TLB 是一个虚拟高速缓存。

每个 TLB entry 通常不仅包含物理地址和虚拟地址,还包含诸如内存类型、缓存策略、访问权限、地址空间 ID(ASID)和虚拟机 ID(VMID)等属性。如果 TLB 不包含处理器发出的虚拟地址的有效转换,称为 TLB Miss,则将执行外部转换页表查找。MMU 内的专用硬件使它能够读取内存中的转换表。

然后,如果翻译页表没有导致页面故障,则可以将新加载的翻译缓存在 TLB 中,以便进行后续的重用。简单概括一下就是:硬件存在 TLB 后,虚拟地址到物理地址的转换过程发生了变化。虚拟地址首先发往 TLB 确认是否命中 cache,如果 cache hit 直接可以得到物理地址。否则,一级一级查找页表获取物理地址。并将虚拟地址和物理地址的映射关系缓存到 TLB 中。

如果操作系统修改了可能已经缓存在 TLB 中的转换的 entry,那么操作系统就有责任使这些未更新的 TLB entry invaild。当执行 A64 代码时,有一个 TLBI,它是一个 TLB 无效的指令:

TLBI <type><level>{IS} {, <Xt>}

TLB 可以保存固定数量的 entry。可以通过由转换页表遍历引起的外部内存访问次数和获得高 TLB 命中率来获得最佳性能。ARMv8-A 体系结构提供了一个被称为连续块 entry 的特性,以有效地利用 TLB 空间。转换表每个 entry 都包含一个连续的位。当设置时,这个位向 TLB 发出信号,表明它可以缓存一个覆盖多个块转换的单个 entry。查找可以索引到连续块所覆盖的地址范围中的任何位置。因此,TLB 可以为已定义的地址范围缓存一个 entry 从而可以在 TLB 中存储更大范围的虚拟地址。

三、Linux 内核中 MMU 的实际应用

3.1 Linux 内存管理架构回顾

为了更深入理解 MMU 在 Linux 内核中的作用,先来看看 Linux 内存管理架构的整体布局。下面是一个简化的 Linux 内存管理架构图:

从用户空间进程开始,应用程序通过系统调用(如 malloc、free 等)向内核请求内存分配 。当进程需要内存时,它会调用 malloc 函数,这实际上会触发一个系统调用进入内核空间 。在内核空间中,内存管理子系统负责处理这些请求 。内存管理子系统是一个复杂的模块,它包含了多个组件,如内存分配器、页表管理模块等 。

内存管理子系统与 MMU 密切协作 。当内核需要为进程分配内存时,它会与 MMU 交互,更新页表等数据结构,以建立虚拟地址到物理地址的映射关系 。例如,内核在为进程分配新的内存页时,会在页表中添加相应的表项,将虚拟页号与物理页框号关联起来 。MMU 则根据这些映射关系,在进程访问内存时进行地址转换 。当进程访问某个虚拟地址时,MMU 会查找页表,将虚拟地址转换为物理地址,然后访问物理内存 。

3.2 关键数据结构分析

在 Linux 内核内存管理中,有两个关键的数据结构起着至关重要的作用,它们分别是内存描述符(mm_struct)和虚拟内存区域(VMA,vm_area_struct) 。

内存描述符(mm_struct)是一个非常重要的数据结构,它描述了一个进程的整个虚拟地址空间 。每个进程都有一个对应的 mm_struct 结构体,就像是每个进程拥有一本关于自己内存空间的 “详细账本” 。在这个结构体中,包含了进程的页目录指针 pgd,它就像是这本 “账本” 的目录,通过它可以快速定位到进程的页表 。页表是记录虚拟地址到物理地址映射关系的关键数据结构,而 pgd 则是访问页表的入口 。

mm_struct 还包含了指向虚拟内存区域(VMA)链表的指针 mmap 和指向红黑树的指针 mm_rb 。当进程的虚拟内存区域较少时,内核使用链表来管理这些区域,链表的优势在于插入和删除操作较为简单 。而当虚拟内存区域较多时,红黑树则发挥出其高效的查找性能,能够快速定位到特定的虚拟内存区域 。例如,当进程需要访问某个虚拟地址时,内核可以通过 mm_struct 中的指针,快速找到该地址所在的虚拟内存区域,进而获取相关的内存属性和映射信息 。

虚拟内存区域(VMA,vm_area_struct)则描述了虚拟地址空间的一个区间 。每个 VMA 代表进程虚拟地址空间中的一段连续区域,它包含了该区域的起始地址、结束地址、访问权限等重要信息 。比如,一个进程的代码段、数据段、堆、栈等,都各自对应着不同的 VMA 。代码段的 VMA 可能设置为只读和可执行权限,数据段的 VMA 则根据是否可写设置相应的权限 。

VMA 还包含一个指向 vm_operations_struct 结构的指针,这个结构中定义了一系列函数指针,用于描述在该虚拟内存区域上的操作 。例如,当进程访问一个不在物理内存中的虚拟页面时,会触发缺页异常,此时内核会调用 VMA 中的 nopage 函数来处理这个异常,为新的虚拟内存分配实际的物理内存 。通过这些数据结构和相关的操作函数,Linux 内核能够高效地管理进程的虚拟地址空间,实现内存的分配、释放和保护等功能 。

3.3 实战案例:手动创建内存映射

接下来,通过一个实战案例来更直观地了解 MMU 在内存管理中的应用,这里我们使用 mmap 函数手动创建内存映射 。下面是一个使用 mmap 函数创建匿名内存映射的 C 语言代码示例:

#include <stdio.h>#include <stdlib.h>#include <sys/mman.h>#include <fcntl.h>#include <unistd.h>#include <string.h>#include <sys/types.h>#include <sys/stat.h>#include <errno.h>#define MAP_SIZE 4096 // 映射区大小,这里设置为 4KB,通常是一个页的大小intmain(){    char *map_start;    int prot = PROT_READ | PROT_WRITE; // 映射区域的权限:可读可写    int flags = MAP_PRIVATE | MAP_ANONYMOUS; // 映射选项:私有映射且匿名映射    int fd = -1// 匿名映射时,文件描述符设为-1    off_t offset = 0// 映射偏移量,通常设为 0    // 使用 mmap 函数创建内存映射    map_start = (char *)mmap(0, MAP_SIZE, prot, flags, fd, offset);    if (map_start == MAP_FAILED) {        perror("mmap failed");        exit(EXIT_FAILURE);    }    // 向映射区域写入数据    strcpy(map_start, "Hello, MMU!");    // 从映射区域读取数据并输出    printf("Data from mapped memory: %s\n", map_start);    // 解除内存映射    if (munmap(map_start, MAP_SIZE) == -1) {        perror("munmap failed");        exit(EXIT_FAILURE);    }    return 0;}

在这段代码中,我们使用 mmap 函数创建了一个大小为 4096 字节(通常是一个内存页的大小)的匿名内存映射 。mmap 函数的第一个参数为 0,表示由系统自动选择映射区的起始地址 。第二个参数 MAP_SIZE 指定了映射区的长度为 4096 字节 。第三个参数 prot 设置了映射区域的权限为可读可写 。第四个参数 flags 指定了映射类型为私有映射(MAP_PRIVATE)和匿名映射(MAP_ANONYMOUS),私有映射意味着对映射区域的修改不会影响到其他进程,匿名映射则表示该映射不与任何文件关联 。第五个参数 fd 在匿名映射时设为 - 1 。第六个参数 offset 指定了映射的偏移量为 0,即从文件开头开始映射(因为是匿名映射,这里的文件概念只是为了函数接口的一致性) 。

创建映射后,我们通过 strcpy 函数向映射区域写入了字符串 “Hello, MMU!”,然后再从映射区域读取数据并输出 。最后,使用 munmap 函数解除内存映射,释放映射的内存区域 。通过这个简单的例子,可以看到 MMU 在内存映射过程中的作用,它负责将虚拟地址空间中的映射区域与物理内存进行关联,使得我们能够通过虚拟地址对物理内存进行读写操作 。

四、高级 MMU 技术实战

4.1 大页(HugePages)优化

大页,简单来说,就是比普通内存页更大的内存页 。在传统的内存管理中,Linux 系统通常使用 4KB 大小的页,而大页的大小可以是 2MB 甚至 1GB 。大页的主要作用之一是解决 TLB 压力问题 。由于 TLB 的容量有限,当系统中存在大量的小内存页时,TLB 很容易被填满,导致 TLB 命中率下降 。而使用大页可以显著减少页表项的数量,因为一个大页可以容纳多个小页的数据 。例如,一个 2MB 的大页相当于 512 个 4KB 的小页,这意味着使用大页时,TLB 中需要存储的页表项数量大幅减少,从而提高了 TLB 的命中率,加速了地址转换过程,提升了系统性能 。

在 Linux 系统中,配置大页可以通过修改内核参数来实现 。首先,需要确定系统支持的大页大小,可以通过查看/proc/meminfo 文件中的 Hugepagesize 字段来获取 。然后,通过修改/etc/sysctl.conf 文件,添加或修改 vm.nr_hugepages 参数来设置系统预留的大页数量 。例如,如果要预留 100 个 2MB 的大页,可以添加如下配置:

vm.nr_hugepages = 100

修改完成后,执行 sudo sysctl -p 使配置生效 。

在 C 语言中使用大页,可以通过 mmap 函数并结合 MAP_HUGETLB 标志来实现 。下面是一个使用大页分配内存的 C 语言示例代码:

#include <stdio.h>#include <stdlib.h>#include <sys/mman.h>#include <fcntl.h>#include <unistd.h>#include <string.h>#include <sys/types.h>#include <sys/stat.h>#include <errno.h>#define HUGE_PAGE_SIZE (2 * 1024 * 1024// 大页大小为 2MBintmain(){    char *map_start;    int prot = PROT_READ | PROT_WRITE; // 映射区域的权限:可读可写    int flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB; // 映射选项:私有映射、匿名映射且使用大页    int fd = -1// 匿名映射时,文件描述符设为-1    off_t offset = 0// 映射偏移量,通常设为 0    // 使用 mmap 函数分配大页内存    map_start = (char *)mmap(0, HUGE_PAGE_SIZE, prot, flags, fd, offset);    if (map_start == MAP_FAILED) {        perror("mmap failed");        exit(EXIT_FAILURE);    }    // 向大页内存区域写入数据    strcpy(map_start, "Hello, HugePages!");    // 从大页内存区域读取数据并输出    printf("Data from hugepage mapped memory: %s\n", map_start);    // 解除内存映射    if (munmap(map_start, HUGE_PAGE_SIZE) == -1) {        perror("munmap failed");        exit(EXIT_FAILURE);    }    return 0;}

在这段代码中,通过 mmap 函数分配了一个 2MB 的大页内存区域 。MAP_HUGETLB 标志表明使用大页进行内存映射 。后续操作与普通内存映射类似,先向映射区域写入数据,再读取并输出数据,最后使用 munmap 函数解除内存映射 。

4.2 DMA 与 MMU 协同

DMA(Direct Memory Access),即直接内存访问,它允许设备(如网卡、硬盘控制器等)直接访问内存,而无需 CPU 的频繁干预 。在 DMA 与 MMU 协同工作的过程中,当设备需要访问内存时,它会生成一个 DMA 请求 。这个请求中包含了要访问的内存地址和数据长度等信息 。MMU 会对这个请求进行处理,将设备提供的虚拟地址转换为物理地址 。因为设备通常使用的是虚拟地址(与进程的虚拟地址空间相对应),而实际的内存访问需要物理地址 。

以网卡接收数据为例,当网卡接收到网络数据包时,它会通过 DMA 将数据直接写入内存 。假设网卡驱动程序为这个操作分配了一段虚拟内存区域,当 DMA 请求到达时,MMU 会根据页表将虚拟地址转换为物理地址,然后网卡就可以将数据写入对应的物理内存位置 。在这个过程中,MMU 确保了设备对内存的访问是安全和正确的,同时也提高了数据传输的效率,因为避免了 CPU 在数据传输过程中的频繁参与 。

下面是一个简单的代码示例,展示了在 Linux 内核中如何进行 DMA 内存分配和映射(这里仅为简化示例,实际应用中可能需要更多的错误处理和细节处理):

#include <linux/module.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/dma-mapping.h>#include <linux/mm.h>MODULE_LICENSE("GPL");staticint __init dma_mmu_init(void){    struct device *dev = NULL// 这里假设设备指针已正确初始化    size_t size = 4096// 分配的内存大小,这里设为 4KB    dma_addr_t dma_handle;    void *dma_buf;    // 分配 DMA 内存    dma_buf = dma_alloc_coherent(dev, size, &dma_handle, GFP_KERNEL);    if (!dma_buf) {        printk(KERN_ERR "DMA allocation failed\n");        return -ENOMEM;    }    // 这里可以进行对 dma_buf 的操作,例如写入数据    // 释放 DMA 内存    dma_free_coherent(dev, size, dma_buf, dma_handle);    return 0;}staticvoid __exit dma_mmu_exit(void){    printk(KERN_INFO "DMA MMU module removed\n");}module_init(dma_mmu_init);module_exit(dma_mmu_exit);

在这个示例中,dma_alloc_coherent 函数用于分配一段 DMA 可访问的内存,它返回一个指向分配内存的指针 dma_buf 和一个 DMA 句柄 dma_handle 。GFP_KERNEL 标志表示从内核内存中分配 。在实际应用中,分配的内存可以用于设备与内存之间的数据传输 。最后,通过 dma_free_coherent 函数释放分配的内存 。这个过程展示了在 Linux 内核中如何实现设备通过 DMA 直接访问内存,以及 MMU 在其中的地址转换作用 。

4.3 KVM 虚拟化中的 MMU

在 KVM(Kernel-based Virtual Machine)虚拟化中,MMU 扮演着至关重要的角色 。KVM 是基于 Linux 内核的虚拟化解决方案,它利用硬件辅助虚拟化技术(如 Intel VT-x 或 AMD-V)来实现高效的虚拟化 。在 KVM 虚拟化环境中,每个虚拟机(Guest)都有自己独立的地址空间,就像在物理机上一样 。然而,这些虚拟机的虚拟地址最终需要映射到宿主机(Host)的物理地址上,这就是 MMU 的工作 。

KVM 虚拟化中的 MMU 实现了二级地址转换 。传统的 MMU 在非虚拟化环境中,只需要进行一次虚拟地址到物理地址的转换 。而在虚拟化环境中,情况变得更加复杂 。虚拟机中的操作系统(Guest OS)认为自己运行在真实的物理机上,它使用的是自己的虚拟地址空间(Guest Virtual Address,GVA) 。但是,这个 GVA 不能直接映射到宿主机的物理地址(Host Physical Address,HPA),因为虚拟机的 “物理内存” 实际上是宿主机分配给它的一段内存区域,这个区域在宿主机的物理内存中可能是不连续的 。

为了解决这个问题,KVM 引入了二级地址转换 。首先,Guest OS 使用的 GVA 会被转换为 Guest Physical Address(GPA),这个过程就像在非虚拟化环境中一样,由 Guest OS 自己的 MMU(通过页表等机制)来完成 。然后,GPA 会被进一步转换为 HPA,这个过程由 KVM 的 MMU(借助硬件的扩展页表 EPT 或嵌套页表 NPT 技术)来实现 。通过这种二级地址转换机制,KVM 能够高效地管理虚拟机的内存,实现虚拟机之间以及虚拟机与宿主机之间的内存隔离和安全访问 。

以一个运行在 KVM 虚拟机上的Web服务器为例,当 Web 服务器进程在虚拟机中访问内存(如读取网页文件)时,它产生的GVA首先会被 Guest OS 的 MMU 转换为GPA 。然后,KVM 的 MMU 会将 GPA 转换为 HPA,最终访问到宿主机上实际存储网页文件的物理内存位置 。这个过程对于 Web 服务器进程来说是透明的,它感觉自己就像在真实的物理机上运行一样 。而 KVM 的 MMU 则在背后默默地完成了复杂的地址转换工作,确保了Web服务器在虚拟化环境中的高效运行 。

五、MMU 性能优化实战

5.1 TLB 优化策略

TLB 作为加速地址转换的关键组件,其性能直接影响系统整体效率。大页(HugePages)使用是优化 TLB 的重要策略之一。在常规的内存管理中,内存以较小的页为单位进行管理,例如常见的 4KB 页。然而,当系统中存在大量小页时,TLB 需要存储大量的页表项来维护虚拟地址到物理地址的映射,这容易导致 TLB 被填满,进而降低 TLB 命中率 。

大页则通过增大单页内存容量,如 2MB 或 1GB,使得单个 TLB 条目能够覆盖更大的内存区域。以 128 条目 TLB 为例,使用 4KB 页时,其覆盖范围仅为 512KB;而使用 2MB 页时,覆盖范围可提升至 256MB,提升了 512 倍;使用 1GB 页时,覆盖范围更是达到 128GB,提升了 262,144 倍 。这意味着在相同的 TLB 容量下,大页可以显著减少页表项的数量,从而提高 TLB 命中率,减少地址转换时间,提升系统性能。在数据库系统中,使用大页可以有效减少 TLB 未命中次数,进而提升数据库的查询和写入速度。

页面着色(Page Coloring)也是一种有效的 TLB 优化策略,它主要用于降低 TLB 冲突。在现代计算机系统中,缓存通常采用组相联映射方式,这意味着不同的虚拟页可能会映射到同一个 TLB 组中。当多个频繁访问的虚拟页映射到同一个 TLB 组时,就会发生 TLB 冲突,导致 TLB 命中率下降 。

页面着色通过将物理页按照一定规则进行分组(着色),使得不同的虚拟页在映射到 TLB 时能够均匀分布,减少冲突的发生 。具体来说,页面着色利用了虚拟地址和物理地址之间的映射关系,通过对物理页的分组,使得不同的虚拟页在映射到 TLB 时,尽可能地分散到不同的组中,从而提高 TLB 的利用率 。假设一个 TLB 有 8 个组,通过合理的页面着色,就可以避免多个频繁访问的虚拟页集中映射到少数几个组中,从而降低 TLB 冲突,提高 TLB 命中率 。

预取优化(Prefetch Optimization)则是利用预取技术提前加载可能被访问的页表项到 TLB 中,从而减少 TLB 未命中的情况 。在程序运行过程中,根据程序的局部性原理,未来可能访问的数据往往与当前访问的数据在内存位置上相近 。预取优化就是基于这一原理,在数据被实际访问之前,通过硬件或软件机制提前将相关的页表项加载到 TLB 中 。

例如,在一些高性能处理器中,硬件会自动检测内存访问模式,当检测到顺序访问模式时,会自动预取后续可能访问的页表项到 TLB 中 。在软件层面,也可以通过编写代码来实现预取操作,如使用_mm_prefetch 指令(在支持 SSE 指令集的处理器中),提前将指定内存位置的页表项预取到 TLB 中 。这样,当程序实际访问这些数据时,就可以直接从 TLB 中获取物理地址,大大减少了 TLB 未命中的概率,提高了内存访问速度 。

5.2 页表遍历加速

在现代处理器架构中,页表遍历是地址转换过程中的重要环节,但也是一个相对耗时的操作,特别是在多级页表结构下。为了提高页表遍历的效率,ARMv8.1 引入了 TT(Translation Table Walk)指令 。传统的页表遍历需要多次内存访问,因为多级页表结构使得处理器需要依次访问各级页表来获取最终的物理地址 。例如,在一个 4 级页表结构中,处理器可能需要访问 4 次内存才能完成一次页表遍历 。而 ARMv8.1 的 TT 指令通过硬件加速的方式,简化了页表遍历过程 。

TT 指令能够直接根据虚拟地址,快速定位到对应的物理地址,而无需像传统方式那样逐级访问页表 。具体来说,TT 指令利用了处理器内部的硬件逻辑,将虚拟地址解析为各级页表索引,并直接访问相应的页表项,从而快速获取物理地址 。以 TTL T0, [X1]指令为例,它可以直接将寄存器 X1 中的虚拟地址进行转换,并将结果存储在寄存器 T0 中 。

这种硬件加速的页表遍历方式,大大减少了页表遍历所需的时间,提高了地址转换的效率 。对于一些对内存访问速度要求极高的应用,如数据库管理系统、实时操作系统等,TT 指令能够显著提升系统性能,减少内存访问延迟,使得这些应用能够更高效地运行 。

5.3 内存访问模式优化

内存访问模式对 MMU 性能有着重要影响,不合理的内存访问模式可能导致页表抖动,从而降低系统性能。页表抖动是指由于频繁的内存访问,导致页表项在内存中频繁地换入换出,增加了系统的开销 。为了避免页表抖动,需要基于局部性原理优化内存访问模式 。

局部性原理包括时间局部性和空间局部性。时间局部性是指如果一个数据项被访问,那么它在未来可能会被再次访问;空间局部性是指如果一个数据项被访问,那么与它相邻的数据项在未来也可能会被访问 。通过遵循局部性原理,可以提高缓存命中率,减少页表抖动 。以矩阵运算为例,假设有一个二维矩阵,在进行矩阵乘法运算时,如果按照列优先的方式访问矩阵元素,就会导致内存访问不连续,频繁地跨页访问,从而增加页表抖动的可能性 。以下是列优先访问的代码示例:

#include <stdio.h>#define N 1000voidmatrix_multiply_column_major(int A[N][N], int B[N][N], int C[N][N]){    for (int j = 0; j < N; j++) {        for (int i = 0; i < N; i++) {            int sum = 0;            for (int k = 0; k < N; k++) {                sum += A[i][k] * B[k][j];            }            C[i][j] = sum;        }    }}

在这段代码中,内层循环按列访问矩阵 B,由于矩阵通常是按行存储的,这种访问方式会导致每次访问 B[k][j]时都可能跨页,增加了页表抖动 。

而如果按照行优先的方式访问矩阵元素,就可以充分利用空间局部性,减少页表抖动 。以下是行优先访问的优化代码示例:

#include <stdio.h>#define N 1000voidmatrix_multiply_row_major(int A[N][N], int B[N][N], int C[N][N]){    for (int i = 0; i < N; i++) {        for (int j = 0; j < N; j++) {            int sum = 0;            for (int k = 0; k < N; k++) {                sum += A[i][k] * B[k][j];            }            C[i][j] = sum;        }    }}

在这个优化后的代码中,内层循环按行访问矩阵 和 B,使得内存访问更加连续,充分利用了空间局部性,减少了页表抖动的可能性 。通过实验测试,优化后的行优先访问模式在执行速度上通常会比列优先访问模式快很多,这充分说明了优化内存访问模式对于提高系统性能的重要性 。

六、调试与问题排查

6.1 常见 MMU 相关错误

在 Linux 系统中,与 MMU 相关的错误时有发生,这些错误往往会导致程序异常甚至系统崩溃 。其中,段错误(Segmentation Fault)是最为常见的错误之一 。段错误通常是由于程序试图访问一个非法的内存地址引起的 。例如,当程序使用一个未初始化的指针,或者指针指向了一个已经释放的内存区域时,就可能触发段错误 。假设我们有以下 C 语言代码:

#include <stdio.h>intmain() {    int *ptr;    *ptr = 10// 这里 ptr 未初始化,是一个野指针,会导致段错误    return 0;}

在这段代码中,ptr 是一个未初始化的指针,当试图通过它来修改内存时,就会引发段错误 。从 MMU 的角度来看,当 CPU 根据这个非法的虚拟地址进行地址转换时,MMU 无法找到对应的有效页表项,从而触发异常 。

权限违规(Permission Violation)也是常见的 MMU 错误 。当程序试图以超出权限的方式访问内存时,就会发生权限违规 。比如,一个内存区域被标记为只读,但程序却尝试对其进行写入操作 。在 Linux 系统中,每个虚拟内存区域(VMA)都有特定的访问权限,如可读(PROT_READ)、可写(PROT_WRITE)、可执行(PROT_EXEC)等 。当 MMU 进行地址转换时,会检查访问权限是否匹配 。如果不匹配,就会触发权限违规错误 。例如,假设一个进程的代码段被标记为只读,当代码中出现如下错误操作时:

#include <stdio.h>intmain() {    char *str = "Hello";    str[0] = 'h'// 试图修改只读的代码段,会导致权限违规    return 0;}

这段代码试图修改只读的字符串常量所在的内存区域,从而引发权限违规错误 。

总线错误(Bus Error)通常与硬件层面的内存访问问题相关 。当 MMU 在进行地址转换时,可能会遇到一些硬件层面的错误,如内存控制器故障、内存芯片损坏等,这些问题会导致总线错误 。例如,当内存中的数据出现奇偶校验错误时,就可能引发总线错误 。从现象上看,总线错误可能导致程序突然崩溃,并且在系统日志中会记录相关的硬件错误信息 。与段错误不同,总线错误更多地涉及到硬件层面的问题,排查和解决起来相对复杂 。

6.2 调试工具集

在排查 MMU 相关问题时,有一系列强大的调试工具可供使用 。Valgrind 是其中一款功能强大的调试工具,它主要用于检测内存错误,包括内存泄漏、越界访问等 。在使用 Valgrind 时,首先需要使用-g 参数编译程序,以保留调试信息 。例如,使用 g++编译 C++ 程序时,可以这样操作:

g++ -g -o myapp myapp.cpp

--tool=memcheck 指定使用内存检测工具,--leak-check=full 表示显示详细的泄漏信息,包括每个泄漏块的位置 。Valgrind 会对程序的内存访问进行全面监测,当检测到内存错误时,会输出详细的错误信息,包括错误类型、发生错误的代码行等 。比如,当检测到内存泄漏时,它会列出所有未释放的内存块,并给出对应的调用栈信息,帮助开发者快速定位到内存泄漏的源头 。

/proc/pid/smaps 文件也是一个非常有用的调试工具 。在 Linux 系统中,/proc 目录下存储了关于系统和进程的各种信息 。对于每个运行的进程,都有一个对应的/proc/pid 目录,其中 pid 是进程的 ID 。smaps 文件记录了该进程的内存映射信息,包括每个虚拟内存区域的起始地址、结束地址、权限、映射的文件等详细信息 。通过查看/proc/pid/smaps 文件,可以了解进程的内存使用情况,判断是否存在异常的内存映射 。

例如,当怀疑某个进程存在内存泄漏时,可以查看该文件中各个内存区域的大小和使用情况,如果发现某个区域的内存不断增长且没有释放,就可能存在内存泄漏问题 。同时,还可以通过分析权限信息,检查是否存在权限违规的内存访问 。比如,如果发现一个区域被标记为只读,但实际却有写入操作的迹象,就可能存在权限违规问题 。

6.3 实战:调试内存泄漏

接下来,通过一个实际案例来展示如何使用 Valgrind 调试内存泄漏问题 。假设我们有如下一段存在内存泄漏的 C++ 代码:

#include <iostream>#include <cstdlib>voidleak_memory() {    int *ptr = new int[100];    // 这里没有释放 ptr,导致内存泄漏}intmain() {    for (int i = 0; i < 10; i++) {        leak_memory();    }    return 0;}

在这段代码中,leak_memory 函数分配了一个包含 100 个整数的数组,但没有释放该内存,从而导致内存泄漏 。并且在 main 函数中,多次调用 leak_memory 函数,加剧了内存泄漏的问题 。

首先,使用-g 参数编译该程序:

g++ -g -o leaky_program leaky_program.cpp

然后,使用 Valgrind 运行程序:

valgrind --tool=memcheck --leak-check=full --show-leak-kinds=all --track-origins=yes ./leaky_program

--show-leak-kinds=all 表示显示所有类型的泄漏,--track-origins=yes 用于追踪未初始化值的来源,有助于更全面地排查问题 。

运行后,Valgrind 会输出大量信息,其中与内存泄漏相关的部分如下:

==12345== 400 bytes in 1 blocks are definitely lost in loss record 1 of 1==12345==    at 0x4C2DB8Foperator new[](unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)==12345==    by 0x40068Eleak_memory() (leaky_program.cpp:5)==12345==    by 0x4006B7main (leaky_program.cpp:10)

从输出中可以看出,Valgrind 明确指出有 400 字节的内存被确定为泄漏(因为每个整数占 4 字节,100 个整数共 400 字节) 。并且给出了内存分配的位置,即在 leaky_program.cpp 文件的第 5 行,通过 operator new[]分配的 。同时,还显示了调用栈信息,表明是在 main 函数中调用 leak_memory 函数时发生的内存泄漏 。

根据这些信息,我们可以很容易地定位到问题代码,并进行修复 。修改后的代码如下:

#include <iostream>#include <cstdlib>voidleak_memory(){    int *ptr = new int[100];    delete[] ptr; // 释放内存,避免泄漏}intmain(){    for (int i = 0; i < 10; i++) {        leak_memory();    }    return 0;}

再次使用 Valgrind 运行修改后的程序,就不会再检测到内存泄漏了 。通过这个实战案例,可以看到 Valgrind 在调试内存泄漏问题时的强大作用,它能够准确地定位问题代码,帮助开发者快速解决内存问题 。

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-02-10 15:30:14 HTTP/2.0 GET : https://f.mffb.com.cn/a/474720.html
  2. 运行时间 : 0.231261s [ 吞吐率:4.32req/s ] 内存消耗:5,028.71kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=9f3375321cb8a46d739203087e676624
  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.000984s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.001662s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.004756s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.002693s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.001710s ]
  6. SELECT * FROM `set` [ RunTime:0.004177s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.001416s ]
  8. SELECT * FROM `article` WHERE `id` = 474720 LIMIT 1 [ RunTime:0.001410s ]
  9. UPDATE `article` SET `lasttime` = 1770708614 WHERE `id` = 474720 [ RunTime:0.014221s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 67 LIMIT 1 [ RunTime:0.001369s ]
  11. SELECT * FROM `article` WHERE `id` < 474720 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.001710s ]
  12. SELECT * FROM `article` WHERE `id` > 474720 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.009877s ]
  13. SELECT * FROM `article` WHERE `id` < 474720 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.013875s ]
  14. SELECT * FROM `article` WHERE `id` < 474720 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.023689s ]
  15. SELECT * FROM `article` WHERE `id` < 474720 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.058640s ]
0.234870s