当前位置:首页>Linux>内存管理-第1章-Linux 内核内存管理概述

内存管理-第1章-Linux 内核内存管理概述

  • 2026-04-16 03:41:58
内存管理-第1章-Linux 内核内存管理概述

本章将带你从宏观视角理解 Linux 内存管理子系统的设计哲学、整体架构和核心组件。


1.1 为什么内存管理如此重要?

内存管理是操作系统内核最核心、最复杂的子系统之一。它直接影响着:

  • 系统性能:内存分配效率决定了程序运行速度
  • 系统稳定性:错误的内存管理会导致系统崩溃
  • 资源利用率:高效的内存管理能让有限的物理内存发挥最大价值
  • 安全性:内存隔离是进程保护的基础
┌─────────────────────────────────────────────────────────────────────┐│                         应用程序 (Application)                        │├─────────────────────────────────────────────────────────────────────┤│                    用户空间内存管理 (User Space)                       ││         malloc/free, mmap, brk, 共享内存, 内存映射文件                  │├─────────────────────────────────────────────────────────────────────┤│                    系统调用接口 (System Call)                          ││              mmap, munmap, mprotect, brk, madvise...                 │├─────────────────────────────────────────────────────────────────────┤│                                                                     ││                   Linux 内核内存管理子系统                             ││   ┌──────────────────────────────────────────────────────────────┐  ││   │  虚拟内存管理 │ 物理内存管理 │ 页面回收 │ 内存映射 │ 交换系统  │  ││   └──────────────────────────────────────────────────────────────┘  ││                                                                     │├─────────────────────────────────────────────────────────────────────┤│                         硬件抽象层 (HAL)                              ││                    MMU, TLB, Cache, 物理内存                          │└─────────────────────────────────────────────────────────────────────┘

1.2 Linux 内存管理的设计哲学

💡 核心设计原则

1. 虚拟内存抽象

每个进程都拥有独立的、连续的虚拟地址空间,与物理内存解耦。

进程A的视角                    进程B的视角┌──────────────┐              ┌──────────────┐│ 0xFFFFFFFF   │              │ 0xFFFFFFFF   ││    内核空间   │              │    内核空间   │├──────────────┤              ├──────────────┤│              │              │              ││    栈 Stack  │              │    栈 Stack  ││      ↓       │              │      ↓       ││              │              │              ││      ↑       │              │      ↑       ││ 堆 Heap/mmap │              │ 堆 Heap/mmap ││              │              │              │├──────────────┤              ├──────────────┤│   数据段     │              │   数据段      │├──────────────┤              ├──────────────┤│   代码段     │              │   代码段      │├──────────────┤              ├──────────────┤│ 0x00000000   │              │ 0x00000000   │└──────────────┘              └──────────────┘        │                            │        └────────────┬───────────────┘                     ↓            ┌────────────────┐            │   物理内存      │            │ (被多个进程共享) │            └────────────────┘

2. 按需分配 (Demand Paging)

不立即分配物理内存,而是在真正访问时才分配,节省内存资源。

3. 写时复制 (Copy-on-Write)

fork 时不复制页面,只在写入时才真正复制,提高 fork 效率。

4. 页面共享

相同内容(如共享库)只在物理内存中保存一份。

5. 内存回收与交换

当内存紧张时,可以回收不常用的页面或将其交换到磁盘。


1.3 物理内存 vs 虚拟内存

物理内存 (Physical Memory)

物理内存是计算机实际的 RAM 硬件,由 页框 (Page Frame) 组成。

// 源码位置: arch/arm64/include/asm/page-def.h#define PAGE_SHIFT		CONFIG_ARM64_PAGE_SHIFT#define PAGE_SIZE		(_AC(1, UL) << PAGE_SHIFT)    // 通常为 4KB#define PAGE_MASK		(~(PAGE_SIZE-1))

页框大小 (ARM64 架构支持):

  • 4KB (最常用)
  • 16KB
  • 64KB

虚拟内存 (Virtual Memory)

虚拟内存是操作系统提供的抽象,每个进程看到的都是独立的地址空间。

                虚拟地址                          物理地址            ┌────────────┐                    ┌────────────┐            │ 0x7fff0000 │ ──── MMU 转换 ───→ │ 0x12340000 │            ├────────────┤      (页表)        ├────────────┤            │ 0x7fff1000 │ ──────────────────→│ 0x56780000 │            ├────────────┤                    ├────────────┤            │ 0x7fff2000 │ ──────────────────→│ 0x9abc0000 │            └────────────┘                    └────────────┘

地址转换的核心:页表

┌─────────────────────────────────────────────────────────────────────────┐│                        虚拟地址 (64位)                                    │├─────────┬─────────┬─────────┬─────────┬─────────┬───────────────────────┤│ 未使用   │  PGD    │  PUD    │  PMD    │  PTE    │    页内偏移            ││ [63:48] │ [47:39] │ [38:30] │ [29:21] │ [20:12] │    [11:0]             │└─────────┴────┬────┴────┬────┴────┬────┴────┬────┴───────────┬───────────┘               │         │         │         │                │               ↓         ↓         ↓         ↓                │           ┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐            │           │  PGD  │→│  PUD  │→│  PMD  │→│  PTE  │→ 物理页框 ──┘           │ Table │ │ Table │ │ Table │ │ Table │   基址 + 偏移           └───────┘ └───────┘ └───────┘ └───────┘

1.4 内存管理子系统架构全景图

Linux 内存管理子系统是一个复杂的层次化结构:

┌─────────────────────────────────────────────────────────────────────────┐│                              用户空间                                    ││                     malloc/free, mmap, brk 等                           │└────────────────────────────────┬────────────────────────────────────────┘                                 │ 系统调用                                 ↓┌─────────────────────────────────────────────────────────────────────────┐│                         进程地址空间管理                                  ││  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐    ││  │ mm_struct   │  │vm_area_struct│  │   mmap()   │  │  缺页处理    │    ││  │ (进程内存    │  │ (VMA 虚拟   │  │   munmap() │  │ Page Fault  │    ││  │  描述符)    │  │  内存区域)   │  │   mprotect │  │  Handler    │    ││  └─────────────┘  └─────────────┘  └─────────────┘  └─────────────┘    ││                      mm/mmap.c, mm/memory.c                             │└────────────────────────────────┬────────────────────────────────────────┘                                 │                                 ↓┌─────────────────────────────────────────────────────────────────────────┐│                           页表管理                                       ││  ┌─────────────────────────────────────────────────────────────────┐   ││  │   PGD → P4D → PUD → PMD → PTE  (多级页表)                        │   ││  │   页表分配、释放、遍历、TLB 刷新                                    │   ││  └─────────────────────────────────────────────────────────────────┘   ││                 arch/*/mm/, mm/pgtable-generic.c                        │└────────────────────────────────┬────────────────────────────────────────┘                                 │                                 ↓┌─────────────────────────────────────────────────────────────────────────┐│                         物理内存分配                                     ││  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐    ││  │  伙伴系统   │  │ Slab/Slub  │  │   vmalloc   │  │  Per-CPU    │    ││  │   Buddy     │  │  分配器     │  │   分配器    │  │   分配器    │    ││  │  System     │  │ kmalloc()  │  │ vmalloc()   │  │ alloc_percpu│    ││  │alloc_pages()│  │ kfree()    │  │ vfree()     │  │             │    ││  └─────────────┘  └─────────────┘  └─────────────┘  └─────────────┘    ││        mm/page_alloc.c    mm/slub.c      mm/vmalloc.c    mm/percpu.c   │└────────────────────────────────┬────────────────────────────────────────┘                                 │                                 ↓┌─────────────────────────────────────────────────────────────────────────┐│                         物理内存组织                                     ││  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐    ││  │    Node     │  │    Zone     │  │    Page     │  │  Memblock   │    ││  │  (NUMA节点) │  │  (内存区域)  │  │  (页描述符) │  │ (早期内存)   │    ││  │ pg_data_t   │  │ struct zone │  │ struct page │  │             │    ││  └─────────────┘  └─────────────┘  └─────────────┘  └─────────────┘    ││                     include/linux/mmzone.h, mm_types.h                  │└────────────────────────────────┬────────────────────────────────────────┘                                 │                                 ↓┌─────────────────────────────────────────────────────────────────────────┐│                          内存回收与交换                                   ││  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐    ││  │ 页面回收    │  │   交换      │  │  内存压缩   │  │ OOM Killer  │    ││  │  kswapd    │  │   Swap      │  │ Compaction  │  │             │    ││  │ LRU 链表   │  │ 换入/换出   │  │  碎片整理   │  │ 内存耗尽处理 │    ││  └─────────────┘  └─────────────┘  └─────────────┘  └─────────────┘    ││       mm/vmscan.c    mm/swapfile.c   mm/compaction.c   mm/oom_kill.c   │└─────────────────────────────────────────────────────────────────────────┘

1.5 核心数据结构概览

内存管理涉及几个关键数据结构,它们构成了整个子系统的骨架:

🔷 struct page - 页描述符

每个物理页框都有一个 struct page 结构来描述。

// 源码位置: include/linux/mm_types.hstructpage {unsignedlong flags;        /* 原子标志位,如 PG_locked, PG_dirty 等 */union {struct {/* Page cache 和匿名页 */structlist_headlru;/* LRU 链表 */structaddress_space *mapping;/* 映射的地址空间 */pgoff_t index;                  /* 在映射中的偏移 */unsignedlongprivate;          /* 私有数据 */        };struct {/* Slab 分配器使用 */structlist_headslab_list;structkmem_cache *slab_cache;void *freelist;/* ... */        };struct {/* 复合页 (Compound Page) */unsignedlong compound_head;unsignedchar compound_dtor;unsignedchar compound_order;/* ... */        };/* ... 其他用途 ... */    };atomic_t _refcount;         /* 引用计数 */atomic_t _mapcount;         /* 映射计数 *//* ... */};

🔷 struct zone - 内存区域

物理内存被划分为不同的区域(Zone),以满足不同类型的内存分配需求。

// 源码位置: include/linux/mmzone.henum zone_type {    ZONE_DMA,       /* DMA 内存区域,用于 ISA 设备 */    ZONE_DMA32,     /* 32位 DMA 设备可访问 */    ZONE_NORMAL,    /* 常规内存,内核可直接映射 */    ZONE_HIGHMEM,   /* 高端内存 (仅32位系统) */    ZONE_MOVABLE,   /* 可移动内存,用于内存热插拔 */    ZONE_DEVICE,    /* 设备内存 */    __MAX_NR_ZONES};structzone {unsignedlong watermark[NR_WMARK];  /* 水位线: min, low, high */unsignedlong nr_reserved_highatomic;structpglist_data *zone_pgdat;/* 所属节点 */structper_cpu_pageset __percpu *pageset;/* Per-CPU 页面缓存 *//* 空闲区域,伙伴系统的核心 */structfree_areafree_area[MAX_ORDER];unsignedlong zone_start_pfn;       /* 起始页框号 */unsignedlong spanned_pages;        /* 跨越的页数 */unsignedlong present_pages;        /* 实际存在的页数 */constchar *name;                   /* 区域名称 *//* ... */};

🔷 struct pglist_data (pg_data_t) - NUMA 节点

在 NUMA 系统中,每个节点有一个 pg_data_t 结构。

// 源码位置: include/linux/mmzone.htypedefstructpglist_data {structzonenode_zones[MAX_NR_ZONES];/* 该节点的所有 zone */structzonelistnode_zonelists[MAX_ZONELISTS];/* 分配回退列表 */int nr_zones;                           /* zone 数量 */unsignedlong node_start_pfn;           /* 节点起始页框号 */unsignedlong node_present_pages;       /* 节点中的页数 */unsignedlong node_spanned_pages;       /* 跨越的页数 */int node_id;                            /* 节点 ID *//* 页面回收相关 */wait_queue_head_t kswapd_wait;structtask_struct *kswapd;/* kswapd 守护进程 *//* ... */pg_data_t;

🔷 struct mm_struct - 进程内存描述符

描述一个进程的整个地址空间。

// 源码位置: include/linux/mm_types.hstructmm_struct {structvm_area_struct *mmap;/* VMA 链表 */structrb_rootmm_rb;/* VMA 红黑树 */pgd_t *pgd;                         /* 页全局目录 */atomic_t mm_users;                  /* 用户计数 */atomic_t mm_count;                  /* 引用计数 */unsignedlong start_code, end_code; /* 代码段 */unsignedlong start_data, end_data; /* 数据段 */unsignedlong start_brk, brk;       /* 堆 */unsignedlong start_stack;          /* 栈起始 */unsignedlong total_vm;             /* 总页数 */unsignedlong locked_vm;            /* 锁定页数 *//* ... */};

🔷 struct vm_area_struct - 虚拟内存区域

描述进程地址空间中的一个连续区域。

// 源码位置: include/linux/mm_types.hstructvm_area_struct {unsignedlong vm_start;             /* 起始地址 */unsignedlong vm_end;               /* 结束地址 */structvm_area_struct *vm_next, *vm_prev;/* VMA 链表 */structrb_nodevm_rb;/* 红黑树节点 */structmm_struct *vm_mm;/* 所属的 mm_struct */pgprot_t vm_page_prot;              /* 访问权限 */unsignedlong vm_flags;             /* 标志位 */structfile *vm_file;/* 映射的文件 (如果是文件映射) */void *vm_private_data;              /* 私有数据 */conststructvm_operations_struct *vm_ops;/* VMA 操作函数 *//* ... */};

数据结构关系图

                                    ┌─────────────┐                                    │   系统      │                                    └──────┬──────┘                                           │              ┌────────────────────────────┼────────────────────────────┐              │                            │                            │              ↓                            ↓                            ↓       ┌─────────────┐              ┌─────────────┐              ┌─────────────┐       │ pg_data_t   │              │ pg_data_t   │              │ pg_data_t   │       │  (Node 0)   │              │  (Node 1)   │              │  (Node N)   │       └──────┬──────┘              └──────┬──────┘              └─────────────┘              │                            │    ┌─────────┼─────────┐        ┌─────────┼─────────┐    │         │         │        │         │         │    ↓         ↓         ↓        ↓         ↓         ↓┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐│ZONE_  │ │ZONE_  │ │ZONE_  │ │ZONE_  │ │ZONE_  │ │ZONE_  ││DMA    │ │NORMAL │ │MOVABLE│ │DMA    │ │NORMAL │ │MOVABLE│└───┬───┘ └───┬───┘ └───┬───┘ └───────┘ └───────┘ └───────┘    │         │         │    └─────────┼─────────┘              │              ↓    ┌─────────────────────┐    │   free_area[0..10]  │    │   (伙伴系统空闲链表)  │    └─────────────────────┘              │              ↓    ┌─────────────────────┐    │     struct page     │    │     struct page     │    │        ...          │    │   (每个物理页框)      │    └─────────────────────┘                                                    ===== 进程视角 =====       ┌──────────────┐       │ task_struct  │       │   (进程)      │       └───────┬──────┘               │               ↓       ┌──────────────┐       │  mm_struct   │       │ (地址空间)    │       └───────┬──────┘               │    ┌──────────┼──────────┬──────────┐    │          │          │          │    ↓          ↓          ↓          ↓┌───────┐  ┌───────┐  ┌───────┐  ┌───────┐│ VMA   │  │ VMA   │  │ VMA   │  │ VMA   ││(代码段)│  │(数据段)│  │ (堆)  │  │ (栈)  │└───────┘  └───────┘  └───────┘  └───────┘

1.6 核心源文件导航指南

📁 主要目录结构

mm/                                 # 内存管理核心代码├── page_alloc.c                   # ⭐ 伙伴系统 (物理页分配)├── slub.c                         # ⭐ SLUB 分配器 (小对象分配)├── slab.c                         # SLAB 分配器├── slab_common.c                  # SLAB/SLUB 公共代码├── vmalloc.c                      # ⭐ vmalloc 实现├── mmap.c                         # ⭐ 进程地址空间管理, mmap 实现├── memory.c                       # ⭐ 缺页异常处理├── vmscan.c                       # ⭐ 页面回收 (kswapd)├── rmap.c                         # 反向映射├── compaction.c                   # 内存压缩├── migrate.c                      # 页面迁移├── oom_kill.c                     # OOM Killer├── swapfile.c                     # 交换文件管理├── swap_state.c                   # 交换缓存├── filemap.c                      # 文件映射, Page Cache├── shmem.c                        # 共享内存 (tmpfs)├── hugetlb.c                      # 大页支持├── huge_memory.c                  # 透明大页 (THP)├── khugepaged.c                   # 大页守护进程├── memcontrol.c                   # Memory Cgroup├── mempolicy.c                    # NUMA 内存策略├── percpu.c                       # Per-CPU 分配器├── memblock.c                     # 早期内存分配├── mm_init.c                      # 内存初始化├── internal.h                     # 内部头文件└── kasan/                         # 内存调试工具    ├── kasan.c    └── ...include/linux/                     # 头文件├── mm.h                           # ⭐ 主要内存管理头文件├── mm_types.h                     # ⭐ 核心数据结构定义├── mmzone.h                       # ⭐ Zone 和 Node 定义├── gfp.h                          # GFP 分配标志├── page-flags.h                   # 页面标志定义├── slab.h                         # Slab 接口├── vmalloc.h                      # vmalloc 接口└── swap.h                         # 交换相关arch/arm64/                        # ARM64 架构相关├── include/asm/│   ├── page.h                     # 页面定义│   ├── pgtable.h                  # 页表定义│   ├── memory.h                   # 内存布局│   └── ...└── mm/    ├── fault.c                    # 缺页异常处理    ├── mmu.c                      # MMU 初始化    └── ...

📋 核心文件功能速查表

文件
核心功能
关键函数
mm/page_alloc.c
伙伴系统
__alloc_pages_nodemask()
__free_pages()
mm/slub.c
小对象分配
kmalloc()
kfree()kmem_cache_alloc()
mm/vmalloc.c
非连续内存
vmalloc()
vfree()
mm/mmap.c
地址空间管理
do_mmap()
do_munmap()find_vma()
mm/memory.c
缺页处理
handle_mm_fault()
do_page_fault()
mm/vmscan.c
页面回收
shrink_page_list()
kswapd()
mm/rmap.c
反向映射
page_referenced()
try_to_unmap()
mm/compaction.c
内存压缩
compact_zone()
migrate_pages()

1.7 内存分配 API 层次

Linux 提供了多层次的内存分配接口:

┌─────────────────────────────────────────────────────────────────────────┐│                            用户空间 API                                  ││     malloc/free (glibc)    mmap/munmap    brk/sbrk    posix_memalign   │└─────────────────────────────────────────────────────────────────────────┘                                    │                                    │ 系统调用                                    ↓┌─────────────────────────────────────────────────────────────────────────┐│                           内核高层 API                                   ││                                                                         ││  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐                  ││  │   kmalloc()  │  │  vmalloc()   │  │  get_free    │                  ││  │   kzalloc()  │  │  vzalloc()   │  │  _pages()    │                  ││  │   krealloc() │  │              │  │              │                  ││  │   kfree()    │  │  vfree()     │  │  free_pages()│                  ││  └──────┬───────┘  └──────┬───────┘  └──────┬───────┘                  ││         │                 │                 │                          ││  物理连续,小内存    虚拟连续,大内存    物理连续,按页分配                   │└─────────┼─────────────────┼─────────────────┼──────────────────────────┘          │                 │                 │          ↓                 ↓                 ↓┌─────────────────────────────────────────────────────────────────────────┐│                          内核底层 API                                    ││                                                                         ││  ┌────────────────────┐      ┌────────────────────┐                    ││  │  Slab/Slub 分配器   │      │     伙伴系统        │                    ││  │ kmem_cache_alloc() │      │  alloc_pages()     │                    ││  │ kmem_cache_free()  │      │  __free_pages()    │                    ││  └─────────┬──────────┘      └──────────┬─────────┘                    ││            │                            │                              ││            └────────────────────────────┘                              ││                          │                                             ││                          ↓                                             ││              ┌────────────────────────┐                                ││              │  __alloc_pages_nodemask │                                ││              │   (核心分配函数)         │                                ││              └────────────────────────┘                                │└─────────────────────────────────────────────────────────────────────────┘

API 使用选择指南

场景
推荐 API
说明
小于 PAGE_SIZE 的小对象
kmalloc()
 / kzalloc()
物理连续,效率最高
频繁分配释放的同类对象
kmem_cache_create()
专用缓存,减少碎片
需要大块虚拟连续内存
vmalloc()
物理可不连续
需要整页分配
alloc_pages()
 / __get_free_pages()
直接使用伙伴系统
DMA 缓冲区
dma_alloc_coherent()
满足设备 DMA 要求
Per-CPU 数据
alloc_percpu()
避免缓存行竞争

1.8 GFP 标志:分配行为控制

GFP (Get Free Pages) 标志控制内存分配的行为:

// 源码位置: include/linux/gfp.h/* 基本标志 */#define __GFP_DMA        /* 从 ZONE_DMA 分配 */#define __GFP_HIGHMEM    /* 可以使用高端内存 */#define __GFP_MOVABLE    /* 可移动的页面 *//* 行为修饰符 */#define __GFP_WAIT       /* 可以睡眠等待 */#define __GFP_IO         /* 可以启动 I/O */#define __GFP_FS         /* 可以调用文件系统 */#define __GFP_NOWARN     /* 失败时不打印警告 */#define __GFP_RETRY_MAYFAIL  /* 可以重试,但可能失败 */#define __GFP_NOFAIL     /* 不允许失败,无限重试 */#define __GFP_ZERO       /* 分配后清零 *//* 常用组合 */#define GFP_KERNEL      (__GFP_RECLAIM | __GFP_IO | __GFP_FS)/* 最常用,可睡眠,可回收 */#define GFP_ATOMIC      (__GFP_HIGH | __GFP_ATOMIC)/* 原子上下文,不可睡眠 */#define GFP_USER        (__GFP_RECLAIM | __GFP_IO | __GFP_FS | __GFP_HARDWALL)/* 用户空间分配 */#define GFP_HIGHUSER    (GFP_USER | __GFP_HIGHMEM)/* 用户空间,可用高端内存 */

GFP 使用决策流程

                    ┌─────────────────┐                    │  需要分配内存?   │                    └────────┬────────┘                             │              ┌──────────────┴──────────────┐              │                             │              ↓                             ↓    ┌─────────────────┐          ┌─────────────────┐    │   可以睡眠?      │          │   不可睡眠       │    │ (进程上下文)      │          │ (中断/软中断)    │    └────────┬────────┘          └────────┬────────┘             │                            │             ↓                            ↓    ┌─────────────────┐          ┌─────────────────┐    │   GFP_KERNEL    │          │   GFP_ATOMIC    │    │   GFP_USER      │          │   GFP_NOWAIT    │    └─────────────────┘          └─────────────────┘

1.9 内存管理初始化流程

系统启动时,内存管理子系统按以下顺序初始化:

启动流程 (start_kernel)        │        ↓┌───────────────────┐│ 1. setup_arch()   │  架构相关初始化│    - 解析内存信息  ││    - memblock 初始化│└─────────┬─────────┘          │          ↓┌───────────────────┐│ 2. mm_init()      │  内存管理初始化│    - mem_init()   │  释放空闲内存到伙伴系统│    - kmem_cache_  │  初始化 Slab 分配器│      init()       │└─────────┬─────────┘          │          ↓┌───────────────────┐│ 3. vmalloc_init() │  vmalloc 初始化└─────────┬─────────┘          │          ↓┌───────────────────┐│ 4. pagecache_init()│ Page Cache 初始化└─────────┬─────────┘          │          ↓┌───────────────────┐│ 5. kswapd 启动     │  页面回收守护进程└───────────────────┘

1.10 本章小结

本章介绍了 Linux 内核内存管理的整体架构:

✅ 核心概念回顾

  1. 虚拟内存:为每个进程提供独立、连续的地址空间
  2. 物理内存组织:Node → Zone → Page 的层次结构
  3. 页表:实现虚拟地址到物理地址的转换
  4. 核心数据结构struct pagestruct zonepg_data_tmm_structvm_area_struct

✅ 关键子系统

子系统
功能
核心文件
伙伴系统
物理页分配
mm/page_alloc.c
Slab/Slub
小对象分配
mm/slub.c
进程地址空间
VMA 管理
mm/mmap.c
缺页处理
按需分配
mm/memory.c
页面回收
内存回收
mm/vmscan.c

📚 下一章预告

第二章:物理内存组织 将深入讲解:

  • NUMA 架构详解
  • pg_data_tstruct zonestruct page 的完整解析
  • 内存模型 (FLATMEM/SPARSEMEM)
  • 伙伴系统的数据结构

1.11 思考题

📝 基础概念题

  1. 什么是虚拟内存?Linux 为什么要使用虚拟内存?
💡 参考答案

虚拟内存是一种内存管理技术,为每个进程提供一个独立的、连续的虚拟地址空间,与物理内存解耦。

Linux 使用虚拟内存的原因

  1. 进程隔离:每个进程有独立的地址空间,一个进程的错误不会影响其他进程
  2. 内存保护:通过页表权限位防止非法访问内核空间或其他进程内存
  3. 简化编程:程序员可以假设连续的地址空间,无需关心物理内存分布
  4. 共享机制:多进程可以映射同一物理页(共享库、共享内存)
  5. 按需分配:Demand Paging 延迟分配物理内存,提高利用率
  6. 交换扩展:可将不活跃页换出到磁盘,支持大于物理内存的工作集
  1. Linux 内核内存管理的主要职责有哪些?
💡 参考答案

Linux 内核内存管理的主要职责包括:

  1. 物理内存管理

    • 追踪物理页框的使用状态
    • 伙伴系统管理页面分配/释放
    • 处理 NUMA 节点和内存 Zone
  2. 虚拟内存管理

    • 管理进程地址空间 (mm_struct、VMA)
    • 页表创建、修改和销毁
    • 处理缺页异常
  3. 内存分配器

    • Slab/SLUB 分配小对象
    • vmalloc 分配大块虚拟连续内存
    • Per-CPU 变量管理
  4. 内存回收与交换

    • 页面回收 (kswapd、直接回收)
    • Swap 管理
    • OOM Killer
  5. 内存保护

    • 地址空间隔离
    • 权限控制
    • ASLR 随机化
  1. 用户空间和内核空间的区别是什么?它们如何划分?
💡 参考答案

主要区别

特性
用户空间
内核空间
权限
低特权级 (EL0)
高特权级 (EL1)
访问范围
只能访问自己的地址空间
可访问所有内存
独立性
每进程独立
所有进程共享
硬件访问
不能直接访问
可直接访问
异常处理
被保护,可恢复
可能导致系统崩溃

划分方式(ARM64 为例)

  • 用户空间0x0000_0000_0000_0000 ~ 0x0000_FFFF_FFFF_FFFF (低 256TB)
  • 内核空间0xFFFF_0000_0000_0000 ~ 0xFFFF_FFFF_FFFF_FFFF (高 256TB)

实现机制

  • TTBR0 寄存器指向用户空间页表
  • TTBR1 寄存器指向内核空间页表
  • 地址高位为 0 时使用 TTBR0,高位为全 1 时使用 TTBR1
  1. 什么是页 (Page)?为什么 Linux 选择以页为单位管理内存?
💡 参考答案

**页 (Page)**:内存管理的基本单位,典型大小为 4KB (4096 字节)。

以页为单位管理的原因

  1. 硬件支持:MMU 以页为单位进行地址转换和权限检查
  2. 减少管理开销
    • 按字节:需要 16G 个管理单元
    • 按 4KB 页:只需 4M 个管理单元
    • 管理 16GB 内存:
  3. 页表效率:页表项数量可控,多级页表可行
  4. 内存保护:权限以页为粒度,简化硬件设计
  5. 内外碎片平衡
    • 太小:外部碎片增加,管理复杂
    • 太大:内部碎片增加,浪费内存
    • 4KB 是经过工程实践的合理折中
  6. 磁盘 I/O 对齐:页大小通常是磁盘扇区大小的倍数

struct page 的作用:内核为每个物理页框维护一个 struct page 结构,记录页面状态、引用计数、映射信息等。


🔥 高频面试题

【面试题 1】虚拟内存带来了哪些好处?有什么代价?

💡 参考答案

好处

优势
说明
进程隔离
每个进程有独立的地址空间,互不干扰
内存保护
页表包含权限位,防止非法访问
虚拟地址连续
即使物理内存不连续,虚拟地址也可连续
按需分配
Demand Paging,只在真正访问时才分配物理内存
共享内存
多进程可映射同一物理页(如共享库)
交换空间
可将不活跃页面换出到磁盘,扩展可用内存
写时复制 (COW)
fork() 时共享页面,写入时才复制

代价

代价
说明
地址转换开销
每次内存访问都需要页表查询
TLB Miss 惩罚
页表未缓存时需要多次内存访问
页表内存占用
多级页表本身需要内存存储
Page Fault 开销
缺页异常处理涉及磁盘 I/O
内存碎片
内部碎片(页内浪费)和外部碎片

【面试题 2】为什么 Linux 要使用多级页表而不是单级页表?

💡 参考答案

单级页表的问题

以 32 位系统、4KB 页大小为例:

虚拟地址空间:4GB = 2^32 字节页大小:4KB = 2^12 字节页表项数:2^32 / 2^12 = 2^20 = 1M 个每个 PTE 4 字节 → 页表大小 = 4MB / 进程

问题

  • 每个进程都需要 4MB 连续的页表空间
  • 即使进程只使用很少内存,也需要完整页表
  • 100 个进程 = 400MB 仅用于页表

多级页表的优势

                    单级页表                    多级页表              ┌────────────────┐         ┌─────────┐              │ PTE 0          │         │ PGD     │ 4KB              │ PTE 1          │         └────┬────┘              │ PTE 2          │              │              │ ...            │    ┌─────────┼─────────┐              │ (大量未使用)    │    ↓         ↓         ↓              │ ...            │ ┌─────┐  ┌─────┐   NULL              │ PTE 1M         │ │PMD 0│  │PMD 1│  (节省!)              └────────────────┘ └──┬──┘  └─────┘                   4MB/进程          │                                    ↓                                ┌─────┐                                │ PTE │ 只有实际使用的才分配                                └─────┘

优势

  1. 按需分配:只为实际使用的地址范围分配页表
  2. 内存节省:稀疏地址空间大大减少页表内存
  3. 灵活性:支持更大的地址空间(64位系统)

64 位系统的多级页表

Level    名称       ARM64     x86-64───────────────────────────────────L0       PGD        9 bits    9 bitsL1       PUD        9 bits    9 bitsL2       PMD        9 bits    9 bitsL3       PTE        9 bits    9 bits───────────────────────────────────         Page Offset 12 bits   12 bits

【面试题 3】kmallocvmalloc__get_free_pages 有什么区别?分别在什么场景使用?

💡 参考答案
函数
物理连续
虚拟连续
大小限制
适用场景
kmalloc
✅ 是
✅ 是
≤ 4MB (通常 128KB)
小对象,需要物理连续
vmalloc
❌ 否
✅ 是
很大
大内存块,无需物理连续
__get_free_pages
✅ 是
✅ 是
≤ 4MB
整页分配,物理连续

详细对比

┌──────────────────────────────────────────────────────────┐│                    kmalloc (Slab 分配器)                  │├──────────────────────────────────────────────────────────┤│ • 物理连续,虚拟连续                                       ││ • 基于伙伴系统 + Slab 缓存                                 ││ • 分配粒度:8, 16, 32, ... 字节                            ││ • 适用:内核数据结构、DMA buffer                           ││ • 示例:kmalloc(sizeof(struct task_struct), GFP_KERNEL)   │└──────────────────────────────────────────────────────────┘┌──────────────────────────────────────────────────────────┐│                    vmalloc (非连续分配)                    │├──────────────────────────────────────────────────────────┤│ • 物理不连续,虚拟连续                                     ││ • 需要修改页表建立映射                                     ││ • 开销较大,但可分配大块内存                               ││ • 适用:模块加载、大数组                                   ││ • 示例:vmalloc(1 * 1024 * 1024)  // 1MB                  │└──────────────────────────────────────────────────────────┘┌──────────────────────────────────────────────────────────┐│              __get_free_pages (直接页分配)                │├──────────────────────────────────────────────────────────┤│ • 直接使用伙伴系统                                        ││ • 分配 2^order 个连续物理页                               ││ • 适用:需要精确控制页数                                  ││ • 示例:__get_free_pages(GFP_KERNEL, 2)  // 4 页          │└──────────────────────────────────────────────────────────┘

调用层次

kmalloc()    │    ├── size <= KMALLOC_MAX_CACHE_SIZE    │           └── Slab/Slub 分配器    │    └── size > KMALLOC_MAX_CACHE_SIZE                └── __get_free_pages()                            │                            └── alloc_pages() → 伙伴系统

【面试题 4】GFP_KERNEL 和 GFP_ATOMIC 的区别是什么?什么情况下必须用 GFP_ATOMIC

💡 参考答案

GFP 标志对比

标志
可睡眠
可回收
使用场景
GFP_KERNEL
✅ 是
✅ 是
进程上下文,普通分配
GFP_ATOMIC
❌ 否
❌ 否
中断上下文,不能睡眠
GFP_NOIO
✅ 是
不发起 I/O
块设备驱动
GFP_NOFS
✅ 是
不进入文件系统
文件系统代码
GFP_USER
✅ 是
✅ 是
为用户空间分配

详细解释

// GFP_KERNEL:最常用的标志// - 可以睡眠等待内存// - 可以触发内存回收(kswapd、直接回收)// - 可以触发交换void *ptr = kmalloc(size, GFP_KERNEL);// GFP_ATOMIC:紧急分配// - 绝对不能睡眠// - 使用紧急预留内存池// - 分配失败概率较高void *ptr = kmalloc(size, GFP_ATOMIC);

必须使用 GFP_ATOMIC 的场景

// 1. 中断处理函数irqreturn_tmy_irq_handler(int irq, void *dev_id){// 中断上下文不能睡眠!void *buf = kmalloc(size, GFP_ATOMIC);// ...}// 2. 持有自旋锁时spin_lock(&my_lock);// 持有自旋锁时禁止睡眠void *ptr = kmalloc(size, GFP_ATOMIC);spin_unlock(&my_lock);// 3. softirq / taskletvoidmy_tasklet_func(unsignedlong data){// 软中断上下文void *buf = kmalloc(size, GFP_ATOMIC);}// 4. 禁用抢占的代码段preempt_disable();void *ptr = kmalloc(size, GFP_ATOMIC);preempt_enable();

判断规则

当前上下文能否睡眠?    │    ├── 能睡眠 → GFP_KERNEL    │   (进程上下文,无自旋锁)    │    └── 不能睡眠 → GFP_ATOMIC        (中断、自旋锁、禁用抢占)

【面试题 5】什么是内存碎片?Linux 如何解决内存碎片问题?

💡 参考答案

内存碎片类型

┌─────────────────────────────────────────────────────────┐│                     内部碎片                             │├─────────────────────────────────────────────────────────┤│ 分配的内存块 > 实际需要的内存                             ││                                                         ││ 示例:申请 100 字节,Slab 分配 128 字节                   ││ ┌────────────────────────────────────┐                  ││ │██████████████████████│░░░░░░░░░░░░│                  ││ │     实际使用 100B    │  浪费 28B   │                  ││ └────────────────────────────────────┘                  │└─────────────────────────────────────────────────────────┘┌─────────────────────────────────────────────────────────┐│                     外部碎片                             │├─────────────────────────────────────────────────────────┤│ 空闲内存总量足够,但不连续                               ││                                                         ││ 空闲总量:12 页,但最大连续只有 2 页                      ││ ┌──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐        ││ │░░│██│░░│░░│██│██│░░│░░│██│░░│░░│░░│██│░░│░░│██│        ││ └──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┘        ││  ░░ = 空闲   ██ = 已用                                  ││                                                         ││ 无法分配连续 4 页的请求!                                │└─────────────────────────────────────────────────────────┘

Linux 的解决方案

方案
原理
对应碎片
Slab 分配器
相同大小对象缓存,减少浪费
内部碎片
伙伴系统
2^n 页分配,相邻合并
外部碎片
内存压缩
迁移可移动页面,整理碎片
外部碎片
迁移类型
UNMOVABLE/MOVABLE/RECLAIMABLE 隔离
外部碎片
大页 (Huge Page)
减少页表项,提高连续性
外部碎片

伙伴系统的合并

释放页 A          检查伙伴 B    │                 │    ▼                 ▼┌───────┐         ┌───────┐│ A (空)│         │ B (空)│  都空闲?└───┬───┘         └───┬───┘    │                 │    └────────┬────────┘             ▼      ┌─────────────┐      │  合并为 2 页  │  继续检查更大的伙伴...      └─────────────┘

**内存规整 (Compaction)**:

压缩只能移动 MOVABLE 页面,UNMOVABLE 页面位置不变:

规整前:┌──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐│M │U │░░│M │░░│░░│M │░░│░░│M │░░│M │░░│░░│░░│░░│└──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┘ 0  1  2  3  4  5  6  7  8  9  10 11 12 13 14 15M = Movable, U = Unmovable, ░░ = Free规整后(M 向左聚集,U 位置不变):┌──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐│M │U │M │M │M │M │░░│░░│░░│░░│░░│░░│░░│░░│░░│░░│└──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┘ 0  1  2  3  4  5  6  7  8  9  10 11 12 13 14 15     ↑                 ↑  U 位置不变      连续 10 页空闲!

注意:正因为 UNMOVABLE 页面无法移动,所以:

  • 内核会尽量将不同迁移类型的页面分开管理
  • UNMOVABLE 页面过多会限制规整效果
  • 这也是为什么内存热插拔需要 MOVABLE zone

【面试题 6】什么是 Copy-On-Write (COW)?它在 fork() 中是如何工作的?

💡 参考答案

COW 原理

  • 写时复制,即只有在写入时才真正复制页面
  • 多个进程可以共享同一个只读物理页
  • 任何进程尝试写入时,触发缺页异常,复制页面

fork() 中的 COW

fork() 调用前:                fork() 调用后(使用 COW):┌─────────────────┐           ┌─────────────────┐│   父进程页表      │           │   父进程页表      ││ ┌───────────┐   │           │ ┌───────────┐   ││ │ VA → PA   │───┼──┐        │ │ VA → PA   │───┼──┐│ │ (R/W)     │   │  │        │ │ (R only)  │   │  │ 改为只读│ └───────────┘   │  │        │ └───────────┘   │  │└─────────────────┘  │        └─────────────────┘  │                     │                              │                     ▼                              │  共享!              ┌──────────┐                          │              │ 物理页面  │ ◄──────────────────────┘              │ Data     │ ◄──────────────────────┐              └──────────┘                         │                                                   │                              ┌─────────────────┐  │                              │   子进程页表     │  │                              │ ┌───────────┐   │  │                              │ │ VA → PA   │───┼──┘                              │ │ (R only)  │   │    只读                              │ └───────────┘   │                              └─────────────────┘

写入触发复制

子进程尝试写入:                              ┌─────────────────┐1. 写入只读页 ─────────────►    │  Page Fault!    │                              └────────┬────────┘                                       │                                       ▼                              ┌─────────────────┐2. 分配新物理页 ──────────►     │  new_page = alloc│                              └────────┬────────┘                                       │                                       ▼                              ┌─────────────────┐3. 复制原页内容 ──────────►     │  copy(new, old) │                              └────────┬────────┘                                       │                                       ▼                              ┌─────────────────┐4. 更新页表(可写)─────────►   │  pte = new | RW │                              └─────────────────┘

结果

              父进程页表                  子进程页表           ┌───────────┐              ┌───────────┐           │ VA → PA1  │              │ VA → PA2  │           │ (R/W)     │              │ (R/W)     │           └─────┬─────┘              └─────┬─────┘                 │                          │                 ▼                          ▼           ┌──────────┐              ┌──────────┐           │ 物理页 1  │              │ 物理页 2  │           │ (原数据)  │              │ (复制)    │           └──────────┘              └──────────┘

COW 的好处

  • fork() 非常快,只复制页表
  • 节省内存,很多页从不被写入
  • exec() 后子进程会替换地址空间,无需复制

【面试题 7】什么是缺页异常 (Page Fault)?有哪些类型?

💡 参考答案

缺页异常定义


缺页异常分类

按处理开销分类

类型
原因
处理方式
开销
Minor Fault
页面在内存,但页表未建立
只需更新页表
微秒级
Major Fault
页面不在内存,需要磁盘 I/O
从磁盘读取 + 更新页表
毫秒级
Invalid Fault
非法访问
发送 SIGSEGV 信号
进程终止

按触发场景分类

场景
描述
Minor/Major
Demand Paging
首次访问 mmap 区域
Minor(匿名)/ Major(文件)
Copy-On-Write
fork 后首次写入共享页
Minor
Swap In
访问被换出的页面
Major
Page Cache Miss
文件映射但不在缓存
Major
Stack Growth
栈自动扩展
Minor
Lazy Allocation
malloc 后首次访问
Minor

详细处理流程

                           访问虚拟地址 VA                                 │                                 ▼                       ┌─────────────────┐                       │ MMU 查找页表 PTE │                       └────────┬────────┘                                │            ┌───────────────────┼───────────────────┐            │                   │                   │            ▼                   ▼                   ▼     PTE 有效 & 权限OK    PTE 无效/不存在      PTE 有效但权限不符            │                   │                   │            ▼                   ▼                   ▼       正常访问完成      ┌──── Page Fault! ────┐                        │                      │                        ▼                      ▼                  进入内核态             写只读页?                  do_page_fault()              │                        │                      ▼                        ▼                 COW 处理                 ┌──────────────┐        do_wp_page()                 │ 查找 VMA      │                 │ find_vma()   │                 └──────┬───────┘                        │        ┌───────────────┼───────────────┐        │               │               │        ▼               ▼               ▼    VA < vma->start  VA 在 VMA 内    无 VMA 覆盖        │               │               │        ▼               ▼               ▼   检查栈扩展?      检查权限         SIGSEGV        │               │            (段错误)        ▼               │  expand_stack()        │        │               │        └───────┬───────┘                │                ▼        ┌───────────────┐        │ 权限检查通过?  │        └───────┬───────┘                │        ┌───────┴───────┐        │               │        ▼               ▼      通过            不通过        │               │        ▼               ▼   handle_mm_fault   SIGSEGV        │        ▼   ┌─────────────────────────────────────────┐   │            根据页面状态分发              │   └─────────────────────────────────────────┘        │        ├─── PTE 不存在 ──────────────────────┐        │                                     │        │    ┌────────────────┬───────────────┤        │    │                │               │        │    ▼                ▼               ▼        │  匿名映射        文件映射        共享映射        │    │                │               │        │    ▼                ▼               ▼        │ do_anonymous_page  do_fault    do_shared_fault        │    │                │               │        │    ▼                ▼               ▼        │ 分配零页        读取文件         检查权限        │                 到 Page Cache     建立映射        │        ├─── PTE 存在但不在内存 (Swap) ───────┐        │                                     │        │    ▼                                │        │ do_swap_page                        │        │    │                                │        │    ▼                                │        │ 从交换空间读入                        │        │                                     │        └─── PTE 存在但只读 (写操作) ─────────┐             │                                │             ▼                                │         do_wp_page (COW)                     │             │                                │             ▼                                │         复制页面,建立可写映射                   │                                              │                                              ▼                                    ┌─────────────────┐                                    │ 更新页表,返回   │                                    │ 用户态重试访问   │                                    └─────────────────┘

常见缺页场景详解

场景 1:首次访问匿名映射 (Demand Paging)

// 用户代码void *ptr = mmap(NULL4096, PROT_READ | PROT_WRITE,                 MAP_PRIVATE | MAP_ANONYMOUS, -10);// 此时:VMA 已创建,但 PTE 未建立,物理页未分配ptr[0] = 'A';  // 触发 Page Fault!

内核处理

Page Fault → do_anonymous_page()    │    ├── 1. alloc_zeroed_user_highpage() 分配零页    │    ├── 2. 设置 PTE: pte = mk_pte(page, vma->vm_page_prot)    │    └── 3. set_pte_at() 写入页表

结果:Minor Fault,微秒级完成


场景 2:Copy-On-Write (fork 后写入)

// 父进程int *data = malloc(4096);*data = 100;pid_t pid = fork();  // 子进程创建,页面标记为只读共享if (pid == 0) {// 子进程    *data = 200;  // 触发 COW Page Fault!}

内核处理

写入只读页 → do_wp_page()    │    ├── 1. 检查是否为 COW 页(page_mapcount > 1 或 PageKsm)    │    ├── 2. alloc_page() 分配新页    │    ├── 3. copy_user_highpage() 复制内容    │    ├── 4. 减少原页引用计数    │    └── 5. 设置新 PTE 为可写

结果:Minor Fault,父子进程各有独立页面


场景 3:访问被换出的页面 (Swap In)

// 内存紧张时,某页面被换出到 swap// 之后程序再次访问该地址int value = *some_swapped_ptr;  // 触发 Major Page Fault!

内核处理

PTE 显示页面在 swap → do_swap_page()    │    ├── 1. 从 PTE 获取 swap entry    │    ├── 2. lookup_swap_cache() 检查 swap cache    │       │    │       ├── 命中:直接使用缓存页    │       │    │       └── 未命中:    │               │    │               ├── alloc_page() 分配页面    │               │    │               └── swap_readpage() 从磁盘读取    │                       │    │                       └── 阻塞等待 I/O 完成    │    ├── 3. 更新 PTE 指向物理页    │    └── 4. swap_free() 释放 swap 槽位(如果不再需要)

结果:Major Fault,毫秒级(涉及磁盘 I/O)


场景 4:文件映射缺页 (mmap 文件)

int fd = open("data.txt", O_RDONLY);char *file = mmap(NULL4096, PROT_READ, MAP_SHARED, fd, 0);char c = file[0];  // 触发 Page Fault!

内核处理

文件映射缺页 → do_fault() → do_read_fault()    │    ├── 1. 查找 Page Cache    │       │    │       ├── 命中:Minor Fault,直接映射    │       │    │       └── 未命中:    │               │    │               ├── alloc_page() 分配页面    │               │    │               ├── add_to_page_cache() 加入缓存    │               │    │               └── readpage() 从文件读取    │                       │    │                       └── 阻塞等待 I/O    │    └── 2. 建立 PTE 映射到 Page Cache 页面

结果:可能是 Minor(缓存命中)或 Major(需要读文件)


场景 5:栈自动扩展

voiddeep_recursion(int n){char buffer[4096];  // 栈上分配if (n > 0)        deep_recursion(n - 1);  // 递归,栈不断增长}// 当栈增长超出当前映射范围时,触发 Page Fault

内核处理

访问地址 < vma->vm_start(栈向下增长)    │    ├── 检查是否可以扩展栈    │       │    │       ├── 地址在 vma->vm_start - 栈保护页 范围内?    │       │    │       ├── 未超过 RLIMIT_STACK 限制?    │       │    │       └── 未与其他 VMA 冲突?    │    ├── expand_stack()    │       │    │       └── 更新 vma->vm_start    │    └── 继续正常的缺页处理(分配页面)

结果:Minor Fault(或 SIGSEGV 如果超出限制)


场景 6:空指针访问 (Invalid Fault)

int *ptr = NULL;*ptr = 42;  // 访问地址 0,触发 Page Fault

内核处理

地址 0 (或很低的地址)    │    ├── find_vma() 找不到覆盖此地址的 VMA    │    ├── 检查是否可以栈扩展:不可以(地址太低)    │    └── bad_area() → 发送 SIGSEGV

结果:进程收到 SIGSEGV 信号,默认终止并产生 core dump


代码路径总结

// arch/x86/mm/fault.c 或 arch/arm64/mm/fault.cdo_page_fault()    │    └── handle_mm_fault()           // mm/memory.c            │            └── __handle_mm_fault()                    │                    └── handle_pte_fault()                            │                            ├── do_anonymous_page()  // 匿名页首次访问                            │                            ├── do_fault()           // 文件映射缺页                            │       ├── do_read_fault()                            │       ├── do_cow_fault()                            │       └── do_shared_fault()                            │                            ├── do_swap_page()       // 换入页面                            │                            ├── do_wp_page()         // COW 写保护                            │                            └── do_numa_page()       // NUMA 迁移

性能影响

场景
典型延迟
原因
Minor Fault (匿名)
1-10 μs
分配页面 + 更新页表
Minor Fault (COW)
2-20 μs
复制页面 + 更新页表
Minor Fault (Page Cache 命中)
1-5 μs
只需建立映射
Major Fault (文件读取)
1-10 ms
磁盘 I/O (HDD)
Major Fault (SSD)
50-200 μs
SSD I/O
Major Fault (Swap)
1-10 ms
交换空间 I/O

调试与监控

# 查看进程的缺页统计cat /proc/[pid]/stat | awk '{print "minflt:", $10, "majflt:", $12}'# 使用 time 命令/usr/bin/time -v ./program 2>&1 | grep "page faults"# 使用 perf 分析perf stat -e page-faults,minor-faults,major-faults ./program# 实时监控系统缺页vmstat 1  # 查看 si/so (swap in/out) 和 bi/bo (block I/O)# 详细跟踪(需要 root)perf record -e page-faults ./programperf report

【面试题 8】请解释 Linux 内存管理中的"按需分配 (Demand Paging)"

💡 参考答案

概念

  • 虚拟内存分配时不立即分配物理页
  • 只有在首次访问时才分配物理页
  • 通过缺页异常 (Page Fault) 触发分配

优点

  1. 节省内存:未使用的页面不占用物理内存
  2. 启动快:程序启动时只加载必要页面
  3. 支持超额分配:可以分配超过物理内存的虚拟地址

工作流程

1. mmap() 调用:只创建 VMA,不分配物理页   ┌─────────────────────────────────────┐   │           进程地址空间               │   │  ┌────────────────────────────────┐ │   │  │  VMA: 0x7f000000 - 0x7f100000  │ │ 虚拟地址已分配   │  │  权限: R/W                      │ │   │  │  物理页: 无                     │ │ 物理页未分配!   │  └────────────────────────────────┘ │   └─────────────────────────────────────┘2. 首次访问:触发 Page Fault   ┌────────────────────────────────────────┐   │ CPU: 访问 0x7f000000                   │   │        │                               │   │        ▼                               │   │   页表查找: PTE 无效!                   │   │        │                               │   │        ▼                               │   │   Page Fault → 内核处理                │   └────────────────────────────────────────┘3. 内核处理:分配物理页,建立映射   ┌────────────────────────────────────────┐   │ 1. 检查 VMA:地址合法                   │   │ 2. 分配物理页:alloc_page()            │   │ 3. 初始化页面:清零或从文件读取          │   │ 4. 更新页表:建立 VA → PA 映射          │   │ 5. 返回用户空间:重新执行访问指令        │   └────────────────────────────────────────┘4. 后续访问:正常进行,无 Page Fault

示例

// 分配 1GB 虚拟内存void *ptr = mmap(NULL1GB, PROT_READ | PROT_WRITE,                 MAP_PRIVATE | MAP_ANONYMOUS, -10);// 此时物理内存使用:0 字节// 访问第一个页面ptr[0] = 'A';// Page Fault → 分配 1 页(4KB)物理内存// 访问第 1000 个页面ptr[4096 * 1000] = 'B';// Page Fault → 再分配 1 页物理内存// 实际使用:8KB(而不是 1GB)

🧠 深度思考题

  1. 如果一个进程的虚拟地址空间是 128TB(64 位系统),为什么实际使用的物理内存通常很少?这依赖哪些机制?
💡 参考答案

根本原因:虚拟地址空间只是"潜在可用"的范围,实际物理内存是按需分配的。

依赖的关键机制

  1. 按需分页(Demand Paging)

    • 只有访问到的虚拟页才会分配物理页
    • mmap/malloc 只分配虚拟地址,不分配物理内存
    • 首次访问触发缺页异常才真正分配
  2. 延迟分配(Lazy Allocation)

    // malloc(1GB) 后VmSize: 1GB+   // 虚拟内存增加VmRSS:  ~0// 物理内存几乎不变
  3. **Copy-on-Write (COW)**:

    • fork 后父子共享物理页
    • 只有写入时才复制
  4. 内存回收与交换

    • 不活跃页面可被换出到 swap
    • 物理内存可被重复利用
  5. 内存映射复用

    • 共享库只加载一份到物理内存
    • 多个进程共享同一物理页

具体示例

进程视角:- 代码段: 10MB- 堆: 声明 1GB,实际用 50MB- mmap: 映射 2GB 文件,只访问 100MB- 栈: 预留 8MB,实际用 1MB虚拟内存: ~3GB物理内存: ~160MB (仅访问过的页)
  1. 在中断处理函数中使用 GFP_KERNEL 分配内存会发生什么?内核如何检测这种错误?
💡 参考答案

问题分析

GFP_KERNEL 包含 __GFP_RECLAIM 和 __GFP_IO,允许:

  • 直接内存回收(可能睡眠)
  • 文件系统操作(可能睡眠)
  • 等待 I/O 完成(会睡眠)

中断上下文的限制

  • 不能睡眠/调度
  • 不能持有可睡眠的锁
  • 必须快速执行完毕

可能发生的问题

  1. 内核 BUG/警告:如果启用了调试选项
  2. 死锁:中断中睡眠可能导致系统挂起
  3. 调度器混乱:in_interrupt() 上下文调用 schedule()

内核检测机制

  1. might_sleep() 检测

    voidmight_sleep(void){if (in_atomic() || irqs_disabled()) {// 打印警告和调用栈        WARN_ON(1);    }}
  2. __GFP_DIRECT_RECLAIM 检查

    if (gfp_mask & __GFP_DIRECT_RECLAIM) {    might_sleep_if(true);}
  3. CONFIG_DEBUG_ATOMIC_SLEEP

    • 检测原子上下文中的睡眠
    • 打印警告信息和调用栈

正确做法

// 中断上下文必须使用ptr = kmalloc(size, GFP_ATOMIC);// 或ptr = kmalloc(size, GFP_NOWAIT);
  1. vm_area_struct 和 struct page 分别管理什么?它们之间有什么关系?
💡 参考答案

vm_area_struct(VMA)

  • 管理对象:进程的虚拟地址空间区域
  • 粒度:任意大小的连续虚拟地址范围
  • 归属:每个进程独有(mm_struct)
  • 信息:起止地址、权限、映射文件等

struct page

  • 管理对象:物理页框
  • 粒度:固定 4KB(或大页)
  • 归属:全局唯一,描述所有物理内存
  • 信息:引用计数、标志、LRU 链表等

两者的关系

用户视角 (VMA):                    内核视角 (page):┌─────────────────┐               ┌─────────────────┐│ VMA: 0x1000-0x3000 │             │ page[pfn=0x100]││ 虚拟地址范围     │               │ 物理页框       ││ 权限、映射类型   │               │ 状态、引用计数 │└────────┬────────┘               └────────┬────────┘         │                                  │         │     通过页表 (PTE) 建立关联      │         └──────────────────────────────────┘VMA (多个) ──页表映射──> page (一个)          一对多关系(共享页面)一个 page 可被多个 VMA 映射:- 共享库被多个进程映射- fork 后父子进程共享页面- 共享内存

关键操作中的协作

  • 缺页处理:VMA 提供策略,page 提供物理存储
  • 页面回收:通过反向映射从 page 找到所有 VMA
  • mmap:创建 VMA,延迟分配 page
  1. 为什么 kmalloc 分配的最大内存通常限制在 4MB 左右?如何分配更大的连续物理内存?
💡 参考答案

4MB 限制的原因

  1. 伙伴系统限制

    #define MAX_ORDER 11  // 最大阶数// 最大分配 = 2^10 * 4KB = 4MB
  2. 碎片化问题

    • 系统运行后,高阶连续物理内存稀缺
    • 请求 4MB 连续内存经常失败
  3. 设计权衡

    • 大多数内核分配需求 < 1 页
    • 连续大内存需求应使用其他方案

分配更大连续物理内存的方法

  1. vmalloc(虚拟连续,物理不连续):

    void *p = vmalloc(16 * 1024 * 1024);  // 16MB// 适用于不需要物理连续的场景
  2. CMA(Contiguous Memory Allocator)

    // 设备树或 bootargs 预留cma=256M// 分配page = cma_alloc(cma_dev, count, align, gfp);// 适用于 DMA 设备
  3. 大页内存(HugeTLB)

    // 预留大页echo 100 > /proc/sys/vm/nr_hugepages// 分配mmap(NULL2*1024*1024, ..., MAP_HUGETLB, ...);
  4. 启动时预留(memblock)

    // 在内核启动早期预留memblock_reserve(base, size);// 内存碎片化之前预留
  5. DMA API

    dma_alloc_coherent(dev, size, &dma_handle, GFP_KERNEL);// 自动选择合适的分配方式
  1. Linux 内核是如何实现进程间内存隔离的?一个进程能否访问另一个进程的内存?
💡 参考答案

内存隔离实现机制

  1. 独立的页表

    进程 A:  mm_struct_A -> pgd_A -> 私有页表进程 B:  mm_struct_B -> pgd_B -> 私有页表相同虚拟地址 -> 不同物理页
  2. 地址空间切换

    switch_mm(old_mm, new_mm, next) {// 更新 TTBR0(ARM64)或 CR3(x86)// 切换到新进程的页表    load_new_mm_cr3(next->mm->pgd);}
  3. MMU 硬件保护

    • 进程只能访问自己页表中映射的地址
    • 访问未映射地址触发缺页异常
  4. 用户/内核空间隔离

    • 用户态无法访问内核地址
    • 页表项权限位控制

能否访问另一个进程的内存?

正常情况下不能,但有合法途径:

  1. 共享内存

    // 两个进程都 mmap 同一 shmmmap(NULL, size, ..., MAP_SHARED, shm_fd, 0);
  2. ptrace 系统调用

    // 调试器可以读写目标进程内存ptrace(PTRACE_PEEKDATA, pid, addr, NULL);ptrace(PTRACE_POKEDATA, pid, addr, data);
  3. /proc/[pid]/mem

    # root 可以读取其他进程内存cat /proc/1234/mem
  4. process_vm_readv/writev

    // 跨进程内存读写(需要权限)process_vm_readv(pid, local_iov, ..., remote_iov, ...);
  5. 内核模块

    • 内核代码可以访问任何物理内存
    • 可以临时映射其他进程的页

安全机制

  • 上述方式都需要特权或特定权限
  • SELinux/AppArmor 可进一步限制
  • 地址空间随机化 (ASLR) 增加攻击难度

💻 实践题

  1. 编写一个简单程序,验证 Demand Paging

    #include<stdio.h>#include<stdlib.h>#include<sys/mman.h>#include<unistd.h>intmain(){size_t size = 100 * 1024 * 1024;  // 100MBprintf("Before mmap, press Enter...\n");    getchar();  // 查看 /proc/[pid]/status 的 VmRSSvoid *ptr = mmap(NULL, size, PROT_READ | PROT_WRITE,                     MAP_PRIVATE | MAP_ANONYMOUS, -10);printf("After mmap, before access, press Enter...\n");    getchar();  // VmRSS 几乎不变// 访问每个页面for (size_t i = 0; i < size; i += 4096) {        ((char *)ptr)[i] = 'A';    }printf("After access, press Enter...\n");    getchar();  // VmRSS 增加约 100MB    munmap(ptr, size);return0;}
  2. 使用 /proc/[pid]/maps 分析进程的内存布局

    # 查看某进程的内存区域cat /proc/$(pidof bash)/maps# 分析各区域的用途:# - [heap]:堆# - [stack]:栈# - libc.so:共享库# - [vdso]:虚拟动态共享对象
  3. 观察 Page Fault 统计

    # 方法 1:使用 time 命令/usr/bin/time -v ls# 查看 "Minor page faults" 和 "Major page faults"# 方法 2:使用 perfperf stat -e page-faults ./your_program

📊 综合分析题

  1. 场景分析:一个应用程序调用 malloc(1GB) 后立即 memset() 填充数据,系统变得非常卡顿。请分析可能的原因和优化方案。

    💡 分析思路

    原因分析

    优化方案

    方案
    说明
    MAP_POPULATE
    mmap 时预先分配所有页面
    mlock()
    锁定页面,防止换出
    大页 (Huge Page)
    减少 Page Fault 次数(2MB 页)
    madvise(MADV_WILLNEED)
    提示内核预读
    分块处理
    不要一次性处理 1GB

    代码示例

    // 使用 MAP_POPULATE 预分配void *ptr = mmap(NULL1GB, PROT_READ | PROT_WRITE,                 MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE,-10);// 或使用大页void *ptr = mmap(NULL1GB, PROT_READ | PROT_WRITE,                 MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB,-10);
    1. malloc(1GB) 只分配虚拟地址,不分配物理页
    2. memset() 顺序访问每个页面
    3. 每 4KB 触发一次 Page Fault(约 262,144 次)
    4. 大量 Page Fault 导致 CPU 频繁陷入内核
    5. 如果物理内存不足,还会触发内存回收/交换

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-04-17 00:19:01 HTTP/2.0 GET : https://f.mffb.com.cn/a/485546.html
  2. 运行时间 : 0.214450s [ 吞吐率:4.66req/s ] 内存消耗:5,268.02kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=78044d3f04b20f4531bc8a8ddcdba2aa
  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.001001s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.001636s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000704s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.003037s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.001303s ]
  6. SELECT * FROM `set` [ RunTime:0.002103s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.001311s ]
  8. SELECT * FROM `article` WHERE `id` = 485546 LIMIT 1 [ RunTime:0.005490s ]
  9. UPDATE `article` SET `lasttime` = 1776356341 WHERE `id` = 485546 [ RunTime:0.002509s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 67 LIMIT 1 [ RunTime:0.000599s ]
  11. SELECT * FROM `article` WHERE `id` < 485546 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.001122s ]
  12. SELECT * FROM `article` WHERE `id` > 485546 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.001307s ]
  13. SELECT * FROM `article` WHERE `id` < 485546 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.001948s ]
  14. SELECT * FROM `article` WHERE `id` < 485546 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.003661s ]
  15. SELECT * FROM `article` WHERE `id` < 485546 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.006332s ]
0.219360s