init
[TOC] Linux 内存模型之基石:FLATMEM 深度解析FLATMEM 是 Linux 内核中最基础、最常用,也是最高效的内存模型。正如其名,“平坦内存”模型假定系统的物理内存是一个单一、连续、不存在大间隙的地址空间。 一、 核心原理:mem_map 数组FLATMEM 模型的全部精髓都围绕着一个核心数据结构:一个巨大的、全局的 struct page 数组,通常被称为 mem_map。 什么是 struct page? 在 Linux 内核中,物理内存不是按字节管理的,而是按固定大小的块来管理的,这个块被称为页帧 (Page Frame)(通常是 4KB)。 内核为每一个物理页帧都分配了一个 struct page 描述符。这个结构体包含了该物理页帧的所有元数据,例如:它的引用计数、它是否是脏页、是否被锁定、属于哪个地址空间映射等。 mem_map 的作用 FLATMEM 模型在内核启动的极早期,会分配一个足够大的连续内存区域,用来存放一个 struct page 数组。这个数组的大小足以覆盖从物理地址 0(或某个起始偏移)到系统最大物理内存地址之间的所有页...
percpu
[TOC] mm/percpu.c Per-CPU Variables Management Per-CPU数据管理的核心实现历史与背景这项技术是为了解决什么特定问题而诞生的?这项技术是为了从根本上解决在多核(SMP)系统中并发访问共享数据所带来的性能瓶颈而诞生的。 锁争用(Lock Contention):在多核系统中,如果多个CPU核心频繁地更新同一个全局变量(例如,一个网络数据包统计计数器),它们必须使用锁(如自旋锁)来保护这个变量,以避免数据竞争。当核心数量增加时,对这个锁的争夺会变得非常激烈,导致CPU花费大量时间在等待锁上,而不是执行实际工作,从而严重限制了系统的扩展性。 缓存一致性开销(Cache Coherency Overhead):即使不使用锁(例如使用原子操作),也会有性能问题。当一个CPU核心修改了共享变量,它所在的缓存行(Cache Line)会被标记为“脏”(Modified)。根据缓存一致性协议(如MESI),其他CPU核心上该缓存行的副本必须被置为“无效”(Invalidated)。当其他CPU也想访问这个变量时,就必须从修改过的那个核...
page-writeback
[TOC] mm/page-writeback.c 页面回写(Page Writeback) 数据持久化的核心机制历史与背景这项技术是为了解决什么特定问题而诞生的?这项技术是为了解决一个计算机体系结构中的根本性矛盾:内存(RAM)与持久性存储设备(如硬盘、SSD)之间巨大的性能差距。 性能缓冲:应用程序向文件写入数据时,如果每次写入都必须等待慢速的存储设备完成,那么整个系统的性能和响应速度将变得无法接受。页面回写机制利用高速的内存作为写回缓存(Write-back Cache),允许write()系统调用将数据写入内存(即 Page Cache)后立即返回,从而极大地提升了应用程序的写入性能和系统吞吐量。 数据持久化:内存中的数据是易失的,断电后会丢失。因此,必须有一个可靠的机制,将在内存中被修改过的数据(称为“脏页”,Dirty Page)在合适的时机写回到持久性存储设备上,确保数据的最终安全性。 I/O效率优化:该机制可以将多次小的、离散的写入操作在内存中合并成一次大的、连续的写入操作,然后再提交给存储设备。这种**I/O合并(I/O...
page_alloc
[TOC] mm/page_alloc.c 伙伴系统内存分配器(Buddy System Memory Allocator) 内核物理内存管理的核心历史与背景这项技术是为了解决什么特定问题而诞生的?这项技术是为了解决操作系统内核中最根本的问题之一:如何高效、可靠地管理整个系统的物理内存(RAM)。内核自身及其服务的子系统(如进程管理、文件系统缓存、网络协议栈)都需要动态地申请和释放内存。mm/page_alloc.c 中实现的伙伴系统(Buddy System)旨在满足以下核心需求: 管理基本单位:将物理内存划分为固定大小的“页”(通常是4KB),并以此为基本单位进行管理。 提供连续内存:许多硬件设备(特别是进行直接内存访问DMA的设备)和某些内核数据结构要求得到的内存块在物理上是连续的。伙伴系统的设计目标之一就是能够分配不同大小的、物理连续的内存块。 对抗外部碎片:在长时间运行的系统中,频繁地分配和释放不同大小的内存块会导致物理内存中产生许多不连续的小空闲块。这种现象称为“外部碎片”,它会导致即使总的空闲内存很多,也无法满足一个较大连续内存的申请。伙伴系统通过其独特...
list
[TOC] include/linux/list.h 内核双向循环链表(Kernel Doubly-Linked List) 内核数据结构的基础构建块历史与背景这项技术是为了解决什么特定问题而诞生的?include/linux/list.h 提供了一套宏和内联函数,用于实现侵入式(Intrusive)的双向循环链表(Doubly-Linked Circular List)。它的诞生是为了解决在C语言编写的操作系统内核中一个极其普遍和基础的问题:如何以一种高效、类型安全且代码复用度高的方式,将任意类型的数据结构组织成一个链表。 在没有这样一个通用实现的情况下,每个需要使用链表的子系统都可能会: 重复造轮子:自己定义一套链表节点和操作函数,导致代码冗余和不一致。 使用非侵入式链表:创建一个通用的链表节点结构,其中包含一个void *指针来指向实际数据。这种方式的缺点是: 额外的内存开销:每次向链表中添加一个元素,都需要额外分配一个链表节点。 性能开销:访问实际数据需要两次指针解引用(list_node->data),这会降低缓存效率。 类型不安全:从voi...
shmem
[TOC] mm/shmem.c 共享内存文件系统(Shared Memory Filesystem) tmpfs与POSIX共享内存的基石历史与背景这项技术是为了解决什么特定问题而诞生的?这项技术以及由它实现的tmpfs文件系统,主要是为了解决两大类问题:高性能的临时文件存储和高效的进程间通信(IPC)。 高速临时存储:传统的磁盘文件系统读写速度受限于物理硬件,速度较慢。很多程序在运行过程中需要创建临时文件(例如编译器中间文件、Web服务器的会话文件等),这些文件不需要持久化存储,在系统重启后即可丢弃。shmem通过在内存中实现一个完整的文件系统,提供了比磁盘快几个数量级的读写速度,极大地提升了这类应用的性能。 进程间通信(IPC):在shmem出现之前,Linux主要支持System V IPC标准的共享内存。POSIX标准则提出了一套基于文件系统的共享内存API(shm_open, mmap)。为了在内核中实现一个统一、高效的后端来支持这两种共享内存机制,shmem被开发出来。它通过将共享内存区域抽象成内存中的“文件”,完美地融入了Linux“一切皆文件”的设计...
shrinker
[TOC] mm/shrinker.c 内核缓存收缩器(Kernel Cache Shrinker) 响应内存压力的回调机制历史与背景这项技术是为了解决什么特定问题而诞生的?这项技术以及其实现的“收缩器”(Shrinker)框架,是为了解决Linux内核中一个根本性的资源管理问题:如何在一个统一的框架下,回收由各种不同内核子系统所占用的内存缓存。 内核缓存的多样性:Linux内核不仅仅有用于缓存文件数据的页面缓存(Page Cache)。还有许多其他重要的缓存,例如用于加速路径查找的dcache、用于缓存文件元数据的inode缓存,以及Slab/Slub分配器自身为内核对象维护的缓存等。 缺乏统一回收接口:当系统物理内存(RAM)不足时,内存管理(MM)子系统需要释放一些内存。对于页面缓存,它有自己复杂的LRU(最近最少使用)算法来回收。但对于dcache、inode cache等“非页面缓存”的内存,MM子系统本身并不知道它们的内部结构,也不知道哪些对象是“可回收的”(例如,一个未被使用的dentry)。 解耦的需求:在没有Shrinker框架的情况...
backing-dev
[toc] mm/backing-dev.c 回写管理(Writeback Management) 脏页回写的调速器与执行者历史与背景这项技术是为了解决什么特定问题而诞生的?这项技术是为了解决Linux内核中一个核心的性能与数据一致性难题:如何智能、高效地将内存中被修改过的数据(“脏页”,Dirty Pages)写回到持久化存储设备(“后备设备”,Backing Device)中。 在mm/backing-dev.c所代表的现代回写框架出现之前,Linux的脏页回写机制比较原始,存在诸多问题: 全局瓶颈:早期的pdflush机制使用一个全局的线程池来处理所有设备的回写任务。这意味着一个慢速设备(如USB 1.0 U盘)的回写任务,可能会长时间占用一个flusher线程,从而阻塞一个高速设备(如NVMe SSD)的回写,造成**队头阻塞(Head-of-line blocking)**问题。 缺乏精细控制:无法对单个设备设置不同的回写策略。所有设备共享一套全局的回写参数,这对于性能差异巨大的异构存储环境是极其低效的。 写操作延迟风暴(Latency Spikes):当系...
kfifo
[TOC] lib/kfifo.c 内核FIFO实现(Kernel FIFO Implementation) 高效的无锁字节流缓冲区历史与背景这项技术是为了解决什么特定问题而诞生的?lib/kfifo.c 中的kfifo(Kernel First-In-First-Out)是为了在Linux内核中提供一个通用、高效、线程安全的先进先出字节流缓冲区(FIFO)而诞生的。在它出现之前,许多内核子系统和驱动程序都需要在生产者(写入数据方)和消费者(读取数据方)之间传递数据,并且它们都各自实现了自己的环形缓冲区(Ring Buffer)逻辑。 这导致了几个突出问题: 代码重复:大量驱动中存在功能相似但实现各异的环形缓冲区代码,造成了代码冗余和维护困难。 容易出错:环形缓冲区的边界条件处理,特别是索引的回绕(wrap-around)逻辑,是常见的bug来源。手写实现很容易在多线程并发场景下出现问题。 缺乏标准化:没有一个标准的接口,使得代码的可读性和可重用性都很差。 性能不佳:一些简单的实现可能使用了不必要的锁,或者没有充分利用CPU架构的特性来优化性能。 kfifo的诞生就...
swap
[TOC] mm/swap.c 交换机制(Swap Mechanism) 物理内存的虚拟扩展历史与背景这项技术是为了解决什么特定问题而诞生的?这项技术以及其实现的交换(Swap)机制,是为了解决一个自计算机诞生以来就存在的根本性限制:物理内存(RAM)的大小是有限的。 运行大于物理内存的程序:在早期,如果一个程序需要比机器拥有的RAM更多的内存,它根本无法运行。交换机制通过将内存中不常用的部分临时存放到磁盘上,从而“欺骗”程序,让它以为自己拥有一个远大于实际物理内存的地址空间。这使得运行大型应用程序成为可能。 同时运行更多的程序:即使每个程序都能装入内存,但同时运行多个程序可能会迅速耗尽RAM。交换机制允许内核将处于空闲或等待状态的进程的内存换出到磁盘,从而为当前活动的进程腾出宝贵的物理内存,提高系统的并发能力和整体吞吐量。 系统休眠(Hibernation):为了实现休眠到磁盘(Suspend-to-Disk)功能,内核需要一个地方来存储整个系统内存的快照。交换空间(Swap Space)被自然地用作这个存储区域。 它的发展经历了哪些重要的里程碑或版本迭代?...