util
[TOC] mm/util.cinit_user_buckets: 初始化memdup_user的专用内存桶此代码片段的作用是在内核启动的早期阶段, 创建一个专用的、高性能的内存分配器(一个kmem_buckets实例), 并将其赋给全局指针user_buckets。这个专用的分配器被命名为"memdup_user", 意味着它被内核中的memdup_user()函数和相关函数独占使用, 目的是优化从用户空间复制数据到内核空间时的小块内存分配性能。 工作原理: memdup_user()是一个非常常见的内核函数, 用于安全地从用户空间拷贝一块数据到内核新分配的内存中。这个操作在处理系统调用参数、网络数据包等场景中被频繁调用, 且请求分配的内存大小各不相同。 标准的通用内存分配器kmalloc()虽然功能强大, 但对于这种极其频繁、大小多变的小块内存请求, 其内部的查找和管理开销可能会成为性能瓶颈。 kmem_buckets (内存桶) 则是一种更轻量级、更快的专用分配器, 类似一个内存池(memory pool)。 它内部预先维护了一系列”桶”(b...
slub
[TOC] mm/slub.c SLUB内存分配器(The SLUB Allocator) 现代内核对象缓存的核心历史与背景这项技术是为了解决什么特定问题而诞生的?这项技术以及它所实现的SLUB分配器,是为了解决内核中一个基础且关键的性能问题:如何高效地分配和释放大量小的、固定大小的内存对象。 对抗内部碎片(Internal Fragmentation):内核需要频繁创建大量的小对象(如inode, dentry, task_struct等),它们的大小通常远小于一个物理内存页(通常是4KB)。如果直接使用页分配器(Buddy System)来为这些小对象分配整个页,会造成巨大的内存浪费。例如,为一个128字节的对象分配一个4096字节的页,97%的内存就被浪费了。 提升分配性能:通用的内存分配器需要处理任意大小的请求,其算法相对复杂。而对于特定类型的对象,我们可以创建一个专用的“对象缓存池”。SLUB分配器就是这种缓存池的实现者。 利用对象构造/析构:很多内核对象在首次使用时需要进行初始化。SLUB框架允许在创建缓存时指定一个“构造函数”(construc...
vmstat
[TOC] mm\vmstat.cinit_mm_internals 初始化内存管理(MM)子系统中,那些依赖于其他核心子系统(如workqueue、cpuhp)已经建立的、更深层次的内部组件。它并非内存管理的最早期初始化(如物理页帧分配器的建立),而是一个第二阶段的、功能性的初始化 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364/* * 这是一个静态的、仅在初始化阶段调用的函数,负责初始化内存管理(MM)的内部组件。 */void __init init_mm_internals(void){ /* ret: 用于接收函数返回值,__maybe_unused属性告诉编译器,在某些配置下 * (如非SMP),这个变量可能未被使用,不要因此产生警告。*/ int ret __maybe_unused; /* * 为MM子系统分配一个名为"mm_percpu_...
async
[toc] kernel/async.c 内核异步函数调用(Asynchronous Function Calls) 一个简单的“发后即忘”式内核执行框架历史与背景这项技术是为了解决什么特定问题而诞生的?这项技术以及其实现的异步函数调用(async)框架,是为了解决内核中一个特定的并发需求:以最简单的方式,将一个函数的执行推迟到一个独立的上下文中,而调用者无需等待其完成。 优化启动时间:这是async框架诞生的最主要驱动力。在内核启动过程中,有大量的初始化调用(initcalls)。如果严格按照顺序依次执行,会非常耗时。async框架允许内核将那些没有严格依赖关系的、耗时的初始化函数“并行化”,将它们提交到后台异步执行,而主启动流程可以继续进行。这显著缩短了系统的总启动时间。 降低关键路径延迟:在某些性能敏感的代码路径中(例如一个设备驱动的.probe函数),可能会有一些耗时但非必需的初始化步骤。通过async,可以将这些步骤“发后即忘”(fire-and-forget)地交由后台处理,使得关键路径函数可以更快地返回,提高系统响应速度。 提供比工作队列更简单的接...
bounds
[TOC] kernel/bounds.c 内核边界定义(Kernel Bounds Definition) 为汇编代码提供C语言的宏常量历史与背景这项技术是为了解决什么特定问题而诞生的?这项技术以及其背后独特的构建过程,是为了解决一个根本性的、存在于所有操作系统内核开发中的问题:如何在底层汇编代码(Assembly Code)中,安全、准确地访问C语言定义的内核数据结构(structs)的成员偏移量和大小。 汇编代码的局限性:汇编语言是一种低级语言,它没有struct或#define的概念。汇编代码需要知道一个结构体中某个成员的确切字节偏移量才能访问它。例如,为了访问task_struct结构中的state字段,汇编代码需要知道state字段距离task_struct结构体起始地址有多少个字节。 C语言编译器的不确定性:这些偏移量并不是固定的。它们会因为以下原因而改变: 架构差异:同一个结构体在32位和64位系统上的布局可能完全不同。 配置选项:不同的内核配置选项(Kconfig)可能会启用或禁用某些结构体成员,导致其后所有成员的偏移量发生变化。 编译器行为:...
completion
[toc] kernel/sched/completion.c 内核同步原语(Kernel Synchronization Primitive) 简洁的任务完成信号量历史与背景这项技术是为了解决什么特定问题而诞生的?这项技术以及它所实现的“完成量”(Completion),是为了提供一个高度简化的、专门用于解决“一个执行单元等待另一个执行单元完成某项工作”这一特定同步场景的内核原语。 简化样板代码:在completion出现之前,要实现这种“等待-通 知”的模式,开发者必须手动组合更底层的原语。这通常意味着: 1. 定义一个`wait_queue_head_t`(等待队列头)。 2. 定义一个`bool`或`int`类型的标志位来表示任务是否完成。 3. 等待者需要在一个循环中调用`wait_event_interruptible()`,这个宏会自动处理将任务加入等待队列、睡眠、被唤醒后检查标志位、处理伪唤醒等一系列复杂操作。 4. 完成者需要设置标志位,然后调用`wake_up()`来唤醒等待者。 ...
cpu
[toc] include/linux/cpumask.hcpumask_check 验证当前cpu数量是否超过了配置的最大cpu数量,并返回cpu1234567891011121314151617// 验证当前cpu数量是否超过了配置的最大cpu数量static __always_inline void cpu_max_bits_warn(unsigned int cpu, unsigned int bits){#ifdef CONFIG_DEBUG_PER_CPU_MAPS WARN_ON_ONCE(cpu >= bits);#endif /* CONFIG_DEBUG_PER_CPU_MAPS */}/* verify cpu argument to cpumask_* operators *///验证当前cpu数量是否超过了配置的最大cpu数量static __always_inline unsigned int cpumask_check(unsigned int cpu){ //small_cpuma...
cred
[TOC] kernel/cred.c 凭证管理(Credential Management) 内核中任务身份与权限的核心历史与背景这项技术是为了解决什么特定问题而诞生的?这项技术以及其核心数据结构struct cred,是为了解决在现代多用户、多进程操作系统中一个根本性的安全问题:如何安全、高效、无竞争地管理和访问一个任务(进程或线程)的身份和权限集合。 集中化身份信息:一个任务的“身份”是复杂的,它包含了用户ID(UID)、组ID(GID)、补充组列表、安全标签(如SELinux上下文)、权能(Capabilities)等一系列信息。在struct cred出现之前,这些信息分散地存储在task_struct(进程描述符)中。 解决竞态条件与锁争用:直接修改task_struct中的权限字段是一个巨大的安全隐患。例如,如果一个线程正在修改自己的UID,而另一个线程同时在检查这个UID以决定是否允许某个操作,就会产生严重的竞态条件。为了保护这些字段,task_struct需要一个锁,但这在高并发系统(如大型Web服务器)中会成为严重的性能瓶颈,因为权限检查是内...
exit
[TOC] kernel/exit.c 进程终结与资源回收(Process Termination and Resource Reclamation)历史与背景这项技术是为了解决什么特定问题而诞生的?kernel/exit.c 内的代码是为了解决操作系统中最基本和最核心的问题之一:如何安全、彻底地终结一个正在运行的进程,并确保其占用的所有系统资源都被完全回收。 一个进程在运行时会占用各种系统资源,包括: 内存:进程地址空间(代码、数据、堆、栈)、页表。 文件描述符:打开的文件、套接字、管道等。 CPU时间:进程作为被调度的实体。 内核数据结构:如 task_struct、信号处理器、定时器等。 子进程关系:作为其他进程的父进程。 如果没有一个健壮、集中的退出机制,当进程结束时: 资源泄漏:内存、文件句柄等资源将无法被释放,随着时间推移会耗尽系统资源,导致系统崩溃。 产生僵尸进程:父进程需要一种机制来获知其子进程的退出状态。如果子进程直接消失,父进程将无法进行后续处理。 孤儿进程问题:如果一个父进程先于其子进程退出,这些子进程将成为“孤儿”,必须有一个机制来“...
fork
[TOC] kernel/fork.c 进程创建(Process Creation) Unix/Linux的基石历史与背景这项技术是为了解决什么特定问题而诞生的?kernel/fork.c 是Linux内核的心脏之一,它实现了进程创建的机制。这项技术是为了解决多任务操作系统中的一个根本问题:如何动态地创建新的、独立的执行流(即进程)。 在单任务系统中,整个系统只有一个执行上下文。为了实现并发和多用户,系统必须有能力创建新的进程。Unix的设计者为此提出了一个极其优雅且强大的模型:fork()。 克隆与变形(Fork-and-Exec):fork()的核心思想不是从零开始创建一个空进程,而是克隆(Clone)当前进程。子进程在创建的瞬间,几乎是父进程的一个完美副本(拥有相同的内存映像、打开的文件等)。然后,子进程通常会通过exec()系统调用来加载并执行一个新的程序,从而“变形”为一个完全不同的进程。 上下文继承:这种模型的强大之处在于子进程可以继承父进程的上下文(如环境变量、文件描述符),这使得像Shell中的管道(|)和I/O重定向(>...






