list
[TOC] include/linux/list.h 内核双向循环链表(Kernel Doubly-Linked List) 内核数据结构的基础构建块历史与背景这项技术是为了解决什么特定问题而诞生的?include/linux/list.h 提供了一套宏和内联函数,用于实现侵入式(Intrusive)的双向循环链表(Doubly-Linked Circular List)。它的诞生是为了解决在C语言编写的操作系统内核中一个极其普遍和基础的问题:如何以一种高效、类型安全且代码复用度高的方式,将任意类型的数据结构组织成一个链表。 在没有这样一个通用实现的情况下,每个需要使用链表的子系统都可能会: 重复造轮子:自己定义一套链表节点和操作函数,导致代码冗余和不一致。 使用非侵入式链表:创建一个通用的链表节点结构,其中包含一个void *指针来指向实际数据。这种方式的缺点是: 额外的内存开销:每次向链表中添加一个元素,都需要额外分配一个链表节点。 性能开销:访问实际数据需要两次指针解引用(list_node->data),这会降低缓存效率。 类型不安全:从...
maple_tree
[TOC] lib/maple_tree.c 高性能B树实现(High-Performance B-Tree Implementation) VMA管理的核心数据结构历史与背景这项技术是为了解决什么特定问题而诞生的?Maple Tree(枫树)是为了解决Linux内核内存管理子系统中的一个核心性能瓶颈而设计的。具体来说,它是为了取代管理虚拟内存区域(Virtual Memory Areas, VMAs)的红黑树(Red-Black Tree)。 在Maple Tree出现之前,每个进程的内存地址空间(struct mm_struct)都使用一棵红黑树来组织其所有的VMA。当进程的VMA数量非常多时(例如大型数据库或科学计算应用),以及在多线程环境下,这棵红-黑树成为了一个严重的争用点: 锁争用(Lock Contention):对VMA树的任何修改(如mmap, munmap)都需要获取一个重量级的读写信号量(mmap_sem,现在是mmap_lock)。在写者持有锁期间,所有其他试图查找或修改VMA的线程都会被阻塞,这严重限制了内存密集型应用的可扩展性。 缓存效率低...
radix-tree
[TOC] lib/radix-tree.c 基数树(Radix Tree) 整数键到指针的高效映射历史与背景这项技术是为了解决什么特定问题而诞生的?lib/radix-tree.c 提供的基数树(Radix Tree)是为了解决一个在内核中非常常见的问题:如何将一个整数(特别是unsigned long类型)作为键,高效地、空间优化地映射到一个指针。 在基数树出现之前,内核中存在多种方式来解决这个问题,但各有缺点: 哈希表:对于稀疏的键(例如,文件偏移量,可能非常大且不连续),哈希表虽然平均查找速度快,但存在哈希冲突问题,且无法高效地进行范围查找或有序遍历。 红黑树:可以处理稀疏的键并支持有序遍历,但它是指针密集型的数据结构,每个节点都有左右子指针、父指针和颜色等额外开销,导致内存占用较高,且缓存效率不佳。 直接映射数组:如果键的范围不大且密集,这是最快的方法。但如果键的范围很大(例如unsigned long的所有可能值),创建一个如此巨大的指针数组是完全不可行的,会消耗天文数字般的内存。 基数树被设计出来,就是为了结合以上方法的优点,同时避免它们的缺点。它特别...
rbtree
[TOC] lib/rbtree.c 红黑树(Red-Black Tree) 内核中通用的有序数据管理历史与背景这项技术是为了解决什么特定问题而诞生的?lib/rbtree.c 提供的红黑树(Red-Black Tree)是为了解决一个在计算机科学和操作系统内核中都非常普遍的需求:如何在一个动态变化的数据集合中,高效地维护元素的有序性,并提供快速的查找、插入和删除操作。 在红黑树出现之前,内核中的有序数据管理主要依赖于: 有序链表:实现简单,但查找操作需要O(n)的时间,当数据量大时性能极差。 简单的二叉搜索树:在理想情况下查找性能是O(log n),但在最坏情况下(例如,按顺序插入元素)会退化成一个链表,性能同样下降到O(n)。 红黑树作为一种自平衡的二叉搜索树(Self-balancing Binary Search Tree),被引入来解决这个问题。它通过一套相对简单的规则(着色和旋转),确保树在任何插入和删除操作后都保持“大致平衡”,从而保证了其查找、插入和删除操作在最坏情况下的时间复杂度都能维持在O(log n)。这为内核中需要高效处理有序数据的子系统提供...
refcount
[TOC] include/linux/refcount.h 安全引用计数器(Safe Reference Counter) 防止Use-After-Free的专用原子计数器历史与背景这项技术是为了解决什么特定问题而诞生的?refcount_t 的诞生是为了解决一个在内核并发编程中长期存在的、非常严重的安全问题:使用通用的原子操作 (atomic_t) 来实现引用计数时存在的固有风险。 在 refcount_t 出现之前,内核中广泛使用 atomic_t 来管理共享对象的生命周期。这种做法虽然可行,但非常脆弱,是导致 use-after-free (UAF) 和 double-free 等严重安全漏洞的常见原因: 整数溢出 (Overflow):如果一个对象的引用计数持续增加,达到 atomic_t 能表示的最大值(UINT_MAX)时,下一次 atomic_inc() 会使其值回绕(wrap around)到 0。这会造成灾难性后果:一个实际上被大量使用的对象,其引用计数突然变为0,从而被错误地释放,导致所有其他引用者都在访问一块已被释放的内存。 零值增加...
string
[TOC] include/linux/ctype.h Linux 内核字符类型判断与转换 (ctype.h)本代码片段是 Linux 内核自定义的 ctype.h 头文件,其核心功能是提供一套高效、独立且内核安全的字符类型判断(如 isalpha, isdigit)与大小写转换(如 tolower, toupper)的宏和内联函数。它通过一个预计算的查找表(Lookup Table)实现了极高的执行效率,并避免了对标准 C 库的依赖,这对于保持内核的自包含性和性能至关重要。 实现原理分析此 ctype 实现的核心是一种经典的、以空间换时间的优化技巧:基于数组查找表的位掩码(bitmask)分类法。 查找表与位掩码: 代码预先定义了一个外部数组 _ctype[]。这个数组共有 256 个元素,精确对应所有可能的 8 位 ASCII 字符。 数组中的每个元素是一个 unsigned char,其本身是一个位掩码。掩码中的每一位(bit)代表一种特定的字符属性,例如 _U (0x01) 代表大写字母,_L (0x02) 代表小写字母,_D (0x04) 代表数...
sort
[TOC] lib/sort.c 通用的排序库(Generic Sorting Library) 为内核提供标准的、高效的排序功能历史与背景这项技术是为了解决什么特定问题而诞生的?lib/sort.c 是为了解决一个在大型软件项目中普遍存在的问题——代码重复——而诞生的。在内核的各个子系统和驱动程序中,经常需要对一组数据进行排序。例如,根据优先级对任务进行排序、根据内存地址对设备资源进行排序等。 在lib/sort.c这个通用库出现之前,各个子系统可能会: 实现自己的、简陋的排序算法(如冒泡排序、插入排序)。 从其他地方复制粘贴一个更高效的排序算法(如快速排序)的实现。 这两种做法都导致了严重的问题:代码库中充满了功能相同但实现各异的排序代码,这不仅增加了内核的体积,也使得bug修复和性能优化变得极其困难。lib/sort.c的诞生,就是为了提供一个单一的、经过充分测试的、高性能的、通用的排序实现,供整个内核使用,从而消除代码重复,并保证排序操作的质量和效率。 它的发展经历了哪些重要的里程碑或版本迭代?lib/sort.c的核心是提供一个与标准C库qsort()函数...
search
[TOC] include/linux/bsearch.h 二分查找bsearch12345678910111213141516171819202122232425262728293031323334353637383940414243/* * bsearch - 对元素数组进行二进制搜索 * @key:指向正在搜索的项目的指针 * @base:指向要搜索的第一个元素的指针 * @num:元件数量 * @size:每个元素的大小 * @cmp:指向比较功能的指针 * * 此函数对给定数组进行二进制搜索。 数组的内容应该已经在提供的比较函数下按升序排序。 * * 请注意,键不必与数组中的元素具有相同的类型,e.g. key可以是字符串,比较函数可以将字符串与结构体的 name 字段进行比较。 但是,如果数组中的键和元素属于同一类型,则可以对 sort() 和 bsearch() 使用相同的比较函数。 */void *bsearch(const void *key, const void *base, size_t num, size_t size, cmp_fu...
timerqueue
[TOC] lib/timerqueue.c Timer Queue Management 高精度定时器的有序数据结构历史与背景这项技术是为了解决什么特定问题而诞生的?timerqueue 的诞生是为了解决在内核中高效管理大量、无序、高精度定时器的问题。在 timerqueue 出现之前,Linux内核主要使用基于“时间轮”(Timer Wheel)的 timer_list 机制来管理定时器。时间轮对于处理大量在同一“节拍”(jiffy)到期的低精度定时器非常高效(O(1)复杂度),但它不适用于以下场景: 高精度需求:时间轮的精度受限于系统节拍(jiffy),通常是毫秒级别。对于需要纳秒级精度的现代应用(如 nanosleep、POSIX interval timers),时间轮无法满足需求。 无序到期时间:高精度定时器的到期时间是稀疏且随机分布在时间轴上的。如果用时间轮来管理,需要一个非常巨大且大部分为空的时间轮,效率极低。 快速查找下一个到期事件:高精度定时器系统需要能够非常快速地找出“下一个最早将要到期的定时器”,以便对硬件时钟进行编程。在时间轮中查找下一个非空...
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):当系...








