namespace
[TOC] Linux Namespaces (kernel/nsproxy.c, kernel/user_namespace.c, etc.) 内核隔离与虚拟化的基石历史与背景这项技术是为了解决什么特定问题而诞生的?Linux Namespaces(命名空间)是为了在单一内核上实现**操作系统级虚拟化(OS-level Virtualization)而诞生的。其核心目标是隔离(Isolate)和虚拟化(Virtualize)**全局的系统资源,使得一个进程组(A group of processes)能看到一套独立的系统资源,仿佛它们运行在一个独立的操作系统上,而实际上它们与其他进程组共享同一个内核。 这项技术解决了以下关键问题: 轻量级隔离:在虚拟机(VM)技术出现之前或并行发展中,业界需要一种比完整虚拟化(模拟整个硬件)开销小得多的隔离方案。Namespaces通过隔离内核数据结构而非模拟硬件,实现了极低的性能开销。 资源划分与视图分离:Unix的传统设计中,许多资源是全局唯一的,例如进程ID(PID)1是init进程,只有一个主机名,一套网络接口等。...
nsfs
[toc] fs/nsfs.c 命名空间文件系统(Namespace Filesystem) 内核命名空间的句柄化接口历史与背景这项技术是为了解决什么特定问题而诞生的?nsfs(Namespace Filesystem)是一个内核中的“伪文件系统”(Pseudo-filesystem),它的诞生是为了解决一个核心问题:如何让用户空间的进程能够安全、稳定地引用(Reference)和操纵内核中的命名空间(Namespace)对象。 在nsfs和其配套的setns(2)系统调用出现之前,进程与命名空间的交互是有限且单向的: 缺乏进入(Entering)机制:一个已经存在的进程,没有一个标准的方法可以“进入”到另一个已经存在的命名空间中。进程只能在创建时(通过clone())进入新的命名-空间,或者通过unshare()将自己与父进程的命名空间分离开来,进入一个新建的命名空间。 缺乏持久化(Persistence)机制:一个命名空间的生命周期通常与其内部的进程绑定。当一个命名空间中最后一个进程退出时,这个命名空间就会被销毁。这使得创建“空的”、可供将来使用的命名空间变得非常...
pidfs
[toc] fs/pidfs.c 进程ID文件系统(Process ID Filesystem) 为进程提供稳定的文件句柄历史与背景这项技术是为了解决什么特定问题而诞生的?pidfs(Process ID Filesystem)及其暴露给用户空间的pidfd(Process ID File Descriptor)是为了从根本上解决一个长期困扰Linux/Unix系统开发者的严重问题:PID复用竞争条件(PID Reuse Race Condition)。 这个问题的经典场景如下: 一个监控进程(例如服务管理器)启动了一个工作进程,并得到了它的PID,比如PID 1234。 监控进程想在稍后向PID 1234发送一个信号(例如 kill(1234, SIGTERM))来优雅地关闭它。 然而,在监控进程发送信号之前,工作进程(PID 1234)可能因为崩溃或正常完成而意外退出了。 操作系统非常快地复用了PID 1234,并将其分配给了一个全新的、完全不相关的进程(例如,一个用户刚刚启动的 rm -rf / 命令)。 监控进程此时执行 kill(1234, SIG...
open
[toc] fs/open.c 文件打开与创建(File Opening and Creation) open/creat系统调用的VFS核心历史与背景这项技术是为了解决什么特定问题而诞生的?这项技术是VFS(虚拟文件系统)最核心的入口之一,它为了解决操作系统中一个最基本、最普遍的需求而诞生:如何定位、验证并实例化一个对文件系统对象的访问会话。 fs/open.c通过实现open(2), creat(2)及其变体系统调用,解决了以下至关重要的问题: 路径解析 (Path Traversal):当用户提供一个路径字符串(如/home/user/file.txt)时,内核需要一个标准的、安全的机制来逐级遍历目录,查找每一级路径组件,并最终定位到目标文件。这个过程必须能正确处理符号链接、挂载点和各种权限问题。 权限与访问控制:在允许访问之前,内核必须根据用户请求的访问模式(读、写、执行)和文件的权限位(mode)、所有者(uid/gid)以及可能的访问控制列表(ACL)和安全模块(LSM)策略,来严格地进行权限检查。 文件实例的创建:成功打开一个文件,意...
namei
[toc] fs/namei.c 路径名查找(Pathname Lookup) VFS将路径字符串解析为内核对象的核心历史与背景这项技术是为了解决什么特定问题而诞生的?fs/namei.c 是Linux虚拟文件系统(VFS)中最核心、最基础的组件之一。它的名字来源于“name to inode”(名称到索引节点),其诞生的目的就是为了解决一个操作系统最基本的问题:如何将一个人类可读的文件路径字符串(如 /home/user/document.txt)转换成内核能够理解和操作的内部对象(即一个 struct inode)。 这个过程被称为路径解析(Path Resolution)或路径查找(Pathname Lookup),它需要解决以下几个关键问题: 分层遍历:如何从一个起点(根目录或当前工作目录)开始,逐级地、安全地遍历目录树? 权限检查:在遍历路径的每一步,如何确保当前进程有权限进入下一级目录(需要执行x权限)? 抽象与统一:如何让这个遍历过程对所有不同类型的文件系统(ext4, XFS, NFS等)都有效,而无需关心它们的具体实现? 处理复杂情况:如何正确地处理...
pipe
[toc] fs/pipe.c 管道(Pipe)与FIFO的VFS层核心实现历史与背景这项技术是为了解决什么特定问题而诞生的?管道(Pipe)是Unix哲学中最具标志性的发明之一,它的诞生是为了解决一个基础而强大的需求:将一个进程的标准输出直接连接到另一个进程的标准输入,从而实现进程间的单向数据流通信(Inter-Process Communication, IPC)。 在管道出现之前,如果想实现 command1 | command2 的效果,可能需要: command1将其输出写入一个临时文件。 command2等待command1执行完毕。 command2再从那个临时文件中读取数据进行处理。 最后还需要清理这个临时文件。 这个过程效率低下(有磁盘I/O)、笨拙且容易出错。管道的出现,就是为了提供一个在内核内存中直接进行的、高效的、同步的数据流传输机制,它优雅地解决了以下问题: 消除临时文件:所有数据都在内核的缓冲区中传递,不涉及任何磁盘I/O。 实现流水线作业(Pipeline):它使得command1和command2可以并发执行。c...
kernfs
[TOC] kernfs 伪文件系统核心框架(Pseudo Filesystem Core Framework) sysfs和cgroupfs的底层基石历史与背景这项技术是为了解决什么特定问题而诞生的?kernfs (Kernel File System) 是一个为了解决在实现伪文件系统(pseudo filesystem)时遇到的内部复杂性和锁竞争问题而被创造出来的内核核心框架。它的诞生主要源于其前身及主要用户——sysfs——所暴露出的设计缺陷: 锁机制复杂且易于死锁:在 kernfs 出现之前,sysfs 的实现与 VFS(虚拟文件系统)层紧密耦合。sysfs 中的目录和文件直接对应于内核的 dentry 和 inode 对象。VFS 自身的锁机制(特别是 d_lock 和 i_mutex)非常复杂,当 sysfs 的属性文件读写操作需要回调到驱动程序,而驱动程序又可能需要获取其他与 VFS 相关的锁时,就极易形成复杂的锁依赖链,导致死锁(deadlock)。这是 sysfs 长期以来最头疼的问题之一。 数据结构臃肿:直接使用 dentry 和 inode 来表示 sys...
proc
[TOC] fs/proc 进程和系统信息伪文件系统历史与背景这项技术是为了解决什么特定问题而诞生的?proc 文件系统(procfs)的诞生是为了提供一个标准的、统一的、基于文件的接口,来取代早期Unix系统中那些不安全、不可移植的进程和内核信息获取方法。在proc出现之前,像ps这样的工具需要通过直接读取内核内存(例如,通过特殊的设备文件/dev/kmem)来获取进程信息。这种方法存在几个严重的问题: 安全风险:允许用户空间程序任意读取内核内存是一个巨大的安全漏洞。 稳定性差:这种方法严重依赖于特定内核版本的数据结构布局。内核的任何微小改动都可能导致用户空间工具失效甚至使系统崩溃。 不可移植:每个硬件架构和操作系统变体都有不同的内存布局,使得编写可移植的监控工具几乎不可能。 procfs通过创建一个**伪文件系统(pseudo-filesystem)**解决了这些问题。它将内核内部的动态数据和状态信息,以普通文件的形式呈现给用户空间,使得任何程序都可以使用标准的open(), read(), write()系统调用来安全、稳定地访问这些信息。 它的发展经历了哪些...
libfs
[toc] fs/libfs.c 文件系统库函数(Filesystem Library Functions) 构建简单伪文件系统的工具集历史与背景这项技术是为了解决什么特定问题而诞生的?这项技术以及fs/libfs.c文件中的代码,是为了解决在Linux内核中重复开发简单的、基于内存的(或称为“伪”)文件系统的问题。 消除样板代码(Boilerplate):在libfs出现之前,如果一个内核开发者想要创建一个简单的文件系统来导出一组调试信息(像debugfs)或配置接口(像configfs),他们通常需要从一个现有的简单文件系统(如ramfs)中复制大量代码。这些代码处理了与虚拟文件系统(VFS)交互的所有通用逻辑,如创建超级块(superblock)、inode、dentry,以及实现基本的目录查找操作。这种复制粘贴的做法导致了大量重复、冗余且难以维护的代码。 降低开发门槛:从头开始编写一个文件系统,即使是简单的,也需要对VFS的复杂内部机制有深入的了解。libfs通过提供一套高级、易于使用的API,将这些复杂的VFS交互细节封装起来,极大地降低了创建新伪文件系统的...
iomap
[toc] fs/iomap 文件块映射框架(File Block Mapping Framework) VFS与文件系统的现代桥梁历史与背景这项技术是为了解决什么特定问题而诞生的?iomap 框架的诞生是为了从根本上现代化和统一Linux内核中逻辑文件偏移量到物理磁盘块地址的转换过程。它旨在取代一个被称为get_block的老旧、低效且复杂的接口。 get_block接口存在以下严重问题: 粒度过细:get_block一次只能查询单个文件系统块的映射。对于一个大文件的大型I/O操作,内核必须在一个循环中反复调用get_block成千上万次,这带来了巨大的函数调用开销和锁争用。 信息贫乏:get_block接口基本上只能返回一个物理块号。它很难有效地表达现代文件系统中的复杂状态,例如“空洞”(Sparse Files)、“未写入的盘区”(Unwritten Extents,也称预分配空间)等。 代码重复:由于get_block接口的局限性,许多通用逻辑(如直接I/O、fiemap等)无法有效地构建在其之上。这导致每个文件系统都需要在自己的代码中重复...