松江叶榭网站建设,校友网站建设的重要性,百度申诉网站,资阳房产网站建设操作系统为什么需要内存管理#xff1f;这应该是一个很经典的问题#xff0c;内存池 (Memory Pool) 也可以认为是一种内存管理的方式#xff0c;所以关于内存管理四个字有点像谜底就在谜面上#xff0c;更多的只是你如何管理的方式。比如 FreeRTOS 中的好几种分配方式…操作系统为什么需要内存管理这应该是一个很经典的问题内存池 (Memory Pool) 也可以认为是一种内存管理的方式所以关于内存管理四个字有点像谜底就在谜面上更多的只是你如何管理的方式。比如 FreeRTOS 中的好几种分配方式常用的只是 heap_4.c 的方式这种用在 MCU 上的方式可以比较简单而对于现代的 2025 年的 MCU 可能依然还是比较小的内存至少没有上升到 4GB 至少我还没接触到。并且芯片性能可能不强无法负责和管理这么多的内存后面出现了一个东西专门辅助此工作所以操作系统采取了其他的方式来管理。MCU 跑的都是在很小的内存中大部分直接都是访问了物理地址所以简单的说为什么的原因就是为了更好的利用和使用内存 这个相对比较快速的可以存数据的东西才出现了内存管理的各种方式一般在操作系统课程中都会提到在演进中出现的分段机制(Segmentation)分页机制(Paging)两种经典的方式也可能听到 段页式 就是两种合并在一起说的。但 Linux 采取的是分页的机制同时根据书中描述是 四级页表 的形式关于分段、分页的一些说明和概述及原理性这里就不详细说明可以询问 ChatGPT 或是查看其他的文章这里仅添加一些可能重要的名词名词 翻译换入 Swapping In换出 Swapping Out页面 Page物理页面 Physical Page页帧 Page Frame页帧号 Page Frame Number (PFN)虚拟页帧号 Virtual Page Frame Number (VPN)物理页帧号 Physical Page Frame Number (PFN)虚拟页面 Virtual Page页表 Page Table (PT)页表项 Page Table Entry (PTE)内存管理单元 Memory Management Unit (MMU)虚拟地址 Virtual Address物理地址 Physical Address转译后备缓冲器快表 Translation Lookaside Buffer (TLB)页全局目录 Page Global Direcotry (PGD)页上级目录 Page Upper Directory (PUD)页中间目录 Page Middle Direcotry (PMD)页表 Page Table (PTE)上面出现了但含义不一样这里主要指Linux中多级页表的页内偏移 Page offset上面提到了一个专门辅助 Linux 做内存管理的东西就是 MMU 了用来将虚拟地址转换成物理地址现代的芯片一般都是将 MMU 内置在芯片中了这是 Linux 运行的必备条件所以 区别一个芯片能不能运行 Linux 系统就是芯片有没有 MMU 这个模块了。 当然现在也可以不用 MMU 也能运行 Linux 了只不过是功能受限关于 MMU 如何寻址如何管理以及页表的映射和使用的过程这里不多赘述感兴趣的可以找操作系统相关课程学习或者找 408 相关的学习视频参考。另外重点是 Linux 一个页面的大小为 4KB 至于为什么是 4KB 的大小AI 给出的答案是历史原因还有就是想要运行 Linux 就需要 MMU 这个模块Linux 内存架构和模型略过对理解关系不大不太重要一级、二级页表映射过程感觉实在是有必要的画一个一级、二级的页表映射的过程二级页表只是在一级的基础上做了修改添加了一个对一级页表的索引表这样能对应的一级页表项就会更多了而 Linux 用了四级来管理数量就不计算了同时这只是大致的示意不代表就是这样的寻址。对于 Linux 的多级页表管理不准备画图了只不过是更多级更复杂更多控制位更长的地址长度但基本的方式是一样的。简单说明就是首先将一个包含了虚拟页帧号的虚拟地址线性地址通过一系列查表的方式查表的这个动作也可以是 MMU 在执行转换成一个包含了物理页帧号的物理地址这里假设用到了 TLB 然后 MMU 通过查 TLB 快速的知道了物理页帧号与内存的某一块位置的对应关系然后就使用一下偏移量在这一页中的偏移多少就知道了这个虚拟地址的数据内容是多少了。另外在这个过程中可能会产生一个 缺页中断 Page Fault 简单说明即是在代码中使用 malloc 申请内存空间是在虚拟地址空间中此时随便申请实际上物理的内存条上对应映射的空间并不存在或者说并没有数据内容或者当访问的页不在任何一个页表中这个时候出现了缺页中断此时才会从存储中加载到内存中或者是正式的分配内存这时候内存条上就有了空间和数据内容了那么这正好也有页的换入Swaping In和换出Swaping Out两个动作。memblock 物理内存的初始化这部分有比较多的图和代码太麻烦了尝试通过文字来简单的叙述看看memblock 的作用Linux 中通过 Buddy 伙伴系统和 slab 分配器来分配和管理内存的但是在此之前不可用的阶段就由 memblock 来承担了初始化和管理的工作所以自然的就想到这个阶段的 memblock 直接就是访问和管理的物理地址memblock 是唯一能做早期启动阶段管理内存的内存分配器由此出现了 early boot memory 阶段的名称是系统启动中间阶段的内存管理这里涉及的内存模型不多赘述。memblock 的数据结构及代码分析书籍解析了代码的结构只是简单解析了各部分的字段含义详细可以直接让 AI 生成注释内容 Gemini 2.5 Flash 生成include/linux/memblock.hstruct memblock {bool bottom_up; /* 是否是自底向上 */phys_addr_t current_limit; /* 当前限制地址 */struct memblock_type memory; /* 可用内存区域 */struct memblock_type reserved; /* 保留内存区域 */};struct memblock_type {unsigned long cnt; /* 区域计数 */unsigned long max; /* 最大区域数 */phys_addr_t total_size; /* 总大小 */struct memblock_region *regions; /* 区域数组 */char *name; /* 类型名称 */};struct memblock_region {phys_addr_t base; /* 区域基地址 */phys_addr_t size; /* 区域大小 */enum memblock_flags flags; /* 区域标志 */#ifdef CONFIG_NUMAint nid; /* NUMA节点ID */#endif};enum memblock_flags {MEMBLOCK_NONE 0x0, /* 无特殊请求 */MEMBLOCK_HOTPLUG 0x1, /* 可热插拔区域 */MEMBLOCK_MIRROR 0x2, /* 镜像区域 */MEMBLOCK_NOMAP 0x4, /* 不添加到内核直接映射 */MEMBLOCK_DRIVER_MANAGED 0x8, /* 总是通过驱动检测 */MEMBLOCK_RSRV_NOINIT 0x10, /* 不初始化struct pages */};接着从 stark_kernel 入手主要关注了 setup_arch 函数参数是 command_line贴出函数// 只给出相对重要的函数调用arch/arm64/kernel/setup.cvoid __init __no_sanitize_address setup_arch(char **cmdline_p){setup_initial_init_mm(_stext, _etext, _edata, _end);*cmdline_p boot_command_line;... ...early_fixmap_init();early_ioremap_init();setup_machine_fdt(__fdt_pointer);... ...arm64_memblock_init();paging_init();... ...bootmem_init();... ...}Gemini 2.5 Flashsetup_initial_init_mm初始化内核的第一个内存描述符 init_mm。主要用于设置内核代码 (_stext, _etext) 和数据段 (_edata, _end) 的内存范围。*cmdline_p boot_command_line将系统启动时传递的命令行参数 (boot_command_line) 赋值给 cmdline_p 指针。这使得后续的内核组件能够访问和解析启动参数。early_fixmap_init初始化早期固定映射fixmap区域。用于在启动初期为特定的、固定地址的内存区域建立虚拟地址映射通常用于访问一些关键的硬件寄存器或数据结构。early_ioremap_init初始化早期 I/O 内存重映射机制。允许内核在启动初期安全地访问和映射设备 I/O 空间例如外设控制器的寄存器。setup_machine_fdt(__fdt_pointer)根据设备树Flattened Device Tree, FDT设置机器相关的参数和配置。解析由 __fdt_pointer 指向的设备树从中获取硬件信息、设备配置等以初始化系统。arm64_memblock_init初始化 ARM64 架构的内存块memblock管理机制。用于在内核启动早期跟踪和管理物理内存区域包括可用内存和保留内存。paging_init初始化页表和内存分页机制。建立将物理内存映射到虚拟地址空间的页表结构这是现代操作系统内存管理的基础。bootmem_init初始化 bootmem 分配器。这是一个简单的物理内存分配器在内核启动的早期阶段分页机制刚建立但更复杂的内存管理系统尚未完全初始化时用于分配物理内存。接着继续分析了 setup_machine_fdt 函数static void __init setup_machine_fdt(phys_addr_t dt_phys){int size;void *dt_virt fixmap_remap_fdt(dt_phys, size, PAGE_KERNEL);const char *name;if (dt_virt)memblock_reserve(dt_phys, size);if (!early_init_dt_scan(dt_virt, dt_phys)) {pr_crit(\nError: invalid device tree blob at physical address %pa (virtual address 0x%px)\nThe dtb must be 8-byte aligned and must not exceed 2 MB in size\n\nPlease check your bootloader.,dt_phys, dt_virt);while (true)cpu_relax();}/* Early fixups are done, map the FDT as read-only now */fixmap_remap_fdt(dt_phys, size, PAGE_KERNEL_RO);name of_flat_dt_get_machine_name();if (!name)return;pr_info(Machine model: %s\n, name);dump_stack_set_arch_desc(%s (DT), name);}该函数主要功能是拿到 DTB 的物理地址后会通过 fixmap_remap_fdt() 进行映射其中包括 pgd、pud、pte 等映射书中这部分是否漏了 pmd 当映射完成后会返回 dt_virt并通过 memblock_reserve() 添加到 memblock.reserved 中。early_init_dt_scan() 通过解析 DTB 文件的 memory 节点获得可用物理内存的起始地址和大小并通过类 memblock_add 的 API 向 memory.regions 数组添加一个 memblock.region 实例用于管理这个物理内存的区域。接着是 arm64_memblock_init 函数其主要工作是将物理内存进行整理将一些特殊区域添加到 reserved 内存中主要是设备树中的chosen chosen(cma) reserved-memory /memreserve chosen(initrd) 节点。这部分的代码工作大体将物理内存进行了分区和简单的管理后续需要进行重要的 内存页表映射 完成物理地址到虚拟地址的映射书中说系统完成初始化之后所有的工作会移交给 Buddy 系统来进行内存管理。