Linux 内核 Page Cache 深度工程实战:从 XArray 到大页文件系统

本文深入剖析 Linux 内核 Page Cache 的核心数据结构、预读算法、缺页中断处理、回写机制以及 O_DIRECT 的工程取舍,结合代码示例和生产环境调优经验,揭示文件 I/O 背后的真实运作逻辑。


一、为什么你需要理解 Page Cache

在 Linux 系统中,"一切皆文件"不仅仅是一句哲学口号,更是一种性能架构的设计根基。Page Cache 位于 VFS 与块设备层之间,是数据库、搜索引擎、日志系统乃至 Web 服务器性能的决定性因素之一。理解它,意味着你能在面对 I/O 瓶颈时有章可循,而非盲目加内存或换 SSD。

Page Cache 解决了两个核心问题:

  1. 磁盘访问延迟:机械硬盘寻道时间约 10ms,NVMe 延迟约 10μs,但 DRAM 访问仅需 50ns —— 三个数量级的差异决定了缓存的必要性。
  2. 数据共享:多个进程读取同一文件时,Page Cache 保证物理内存中只有一份副本。
  3. 当所有应用都通过 read()/write() 系统调用访问文件时,数据并非直接落到磁盘,而是经过 Page Cache 中转。这也解释了为什么 Linux 会"吃掉"你所有空闲内存 —— 这是设计,不是 bug。


    二、Page Cache 的骨骼:XArray

    历史上,Page Cache 使用 Radix Tree 管理页面索引(mapping->page_tree)。自从 Linux 4.20 起,XArray(eXtensible Array)逐步取代了 Radix Tree,成为 address_space 的核心数据结构。

    2.1 为什么是 XArray

    Radix Tree 在并发场景下依赖 rcu_read_lock() + 自旋锁保护,而 XArray 提供了原生的 RCU 支持,并且 API 更简洁。对于 64 位系统上一个拥有数十亿页的文件,XArray 能在 O(log n) 时间内完成页面查找,同时支持并发读、独占写。

    XArray 的核心数据结构:

    
    // include/linux/xarray.h
    struct xa_lock {
        spinlock_t    xa_lock;
    };
    
    struct xarray {
        spinlock_t    xa_lock;
        gfp_t         xa_flags;
        void __rcu    *xa_head;
    };
    

    xa_head 指向树的一级节点,每个节点最多存储 64 个槽位(XA_CHUNK_SIZE = 64)。对于 index 类型为 unsigned long 的系统,树的深度最多为 6 层(BITS_PER_LONG = 64,每个节点分支因子 64)。

    2.2 address_space 中的组织

    每个打开的文件在内存中都有一个 struct address_space 实例,它承载了 Page Cache 的索引:

    
    struct address_space {
        struct inode        *host;          // 宿主 inode
        struct xarray       i_pages;        // XArray: 页缓存索引
        gfp_t               gfp_mask;
        atomic_t            i_mmap_writable; // 可写 mmap 映射数
        struct rb_root_cached i_mmap;       // mmap 的红黑树
        rw_semaphore        i_mmap_rwsem;    // mmap 读写信号锁
        unsigned long       nrpages;        // 缓存页数
        const struct address_space_operations *aops;
        unsigned long long  writeback_index; // 回写起始位置
        // ... 更多字段
    };
    

    nrpages 字段可以直接通过 /proc/meminfo 的 Cached 行观察到。要确认具体哪个文件占用了多少页缓存,可以通过 vmtouch 工具或解析 /proc//pagemap。

    2.3 实战:查看文件的页缓存占比

    
    # 安装 vmtouch
    $ git clone https://github.com/hoytech/vmtouch.git
    $ cd vmtouch && make && sudo cp vmtouch /usr/local/bin/
    
    # 查看 /var/lib/mysql/ibdata1 的缓存情况
    $ vmtouch /var/lib/mysql/ibdata1
               Files: 1
         Directories: 0
      Resident Pages: 1250343/1280000  (97.7%)
             Elapsed: 0.2868 seconds
    
    # 查看多个目录下所有文件的缓存热度
    $ vmtouch -v /var/lib/mysql/
    

    vmtouch 底层使用 mincore() 系统调用查询每页是否在页缓存中。


    三、缺页中断与 mmap 映射

    3.1 do_page_fault 的完整链路

    当进程通过 mmap() 访问一个尚未建立页表映射的虚拟地址时,CPU 触发缺页异常,内核进入 do_page_fault() → handle_mm_fault() → handle_pte_fault() 链路:

    
    // mm/memory.c
    vm_fault_t handle_mm_fault(struct vm_area_struct *vma, unsigned long address,
                               unsigned int flags, struct pt_regs *regs)
    {
        // 1. 找到或创建 PMD 中间页目录条目
        // 2. 调用 handle_pte_fault 处理 PTE 层
        pgd = pgd_offset(mm, address);
        p4d = p4d_alloc(mm, pgd, address);
        pud = pud_alloc(mm, p4d, address);
        pmd = pmd_alloc(mm, pud, address);
        return handle_pte_fault(vmf);
    }
    
    static vm_fault_t handle_pte_fault(struct vm_fault *vmf)
    {
        // PTE 不存在 → do_fault(文件映射的缺页处理)
        if (!vmf->pte)
            return do_fault(vmf);
        // PTE 存在但只读 → do_wp_page(写时复制)
        if (!(vmf->flags & FAULT_FLAG_WRITE))
            return do_read_fault(vmf);
        // PTE 存在且可写 → do_wp_page
        return do_wp_page(vmf);
    }
    

    对于文件映射的 Page Cache 场景(do_fault 分支),内核走以下路径:

    1. do_read_fault:从磁盘读入页面到 Page Cache,建立 PTE 映射
    2. do_shared_fault:写共享映射时的首次写入(同时涉及页面读入和 COW)
    3. do_cow_fault:写时复制(fork 后父子进程私有映射的写入)
    4. 3.2 文件映射缺页的深层次优化

      当缺页发生在 VMA 的文件映射区域时,内核首先在 XArray 中查找对应页面:

      
      // mm/filemap.c
      static vm_fault_t filemap_fault(struct vm_fault *vmf)
      {
          struct file *filp = vmf->vma->vm_file;
          struct address_space *mapping = filp->f_mapping;
          pgoff_t offset = vmf->pgoff;
      
          // 1. 在 XArray 中查找页面(RCU 保护,无锁)
          rcu_read_lock();
          page = xa_load(&mapping->i_pages, offset);
          rcu_read_unlock();
      
          if (page && !fault_flags_allow_retry(vmf->flags))
              goto page_hit;    // 命中缓存,建立 PTE 即可
      
      page_no_read:
          // 2. 未命中,使用预读窗口批量读入
          page = page_cache_alloc(...);
          error = mapping->aops->readpage(filp, page);  // → ext4_readpage
          // 建立 PTE 映射
      }
      
      page_hit:
          // 预读:告诉 readahead 子系统我们即将顺序读取
          if (vmf->flags & FAULT_FLAG_WRITE)
              // 可写缺页特殊处理
          else
              // 检查是否命中预读窗口前沿,若是则触发异步预读
              if (PageReadahead(page))
                  page_cache_async_ra(...);
      

      这里隐藏了一个关键设计:预读窗口的前沿标记(PageReadahead)。内核在预读时,将窗口最后一个页面的 PG_readahead 标志位置位,当进程实际访问到这个页面时,内核才会触发下一轮预读。这种"按需异步预读"避免了无效的 I/O 浪费。

      3.3 实战:用 perf 定位 mmap 应用的缺页热点

      
      # 记录缺页中断事件
      $ perf stat -e page-faults,dTLB-load-misses,dTLB-loads \
        -p $(pidof nginx) sleep 10
      
      # 输出
       Performance counter stats for process id '1234':
      
              125,432      page-fists
              890,234      dTLB-load-misses              #   12.34% of all dTLB accesses
            7,215,678      dTLB-loads
      
      # 进一步用 perf probe 跟踪内核函数
      $ perf probe --add 'filemap_fault file:file* address_space:address_space*'
      $ perf probe --add 'do_page_fault mm:mm_struct* address:ulong'
      

      dTLB 命中率低通常暗示大文件随机访问过多,可考虑使用透明大页(THP)或 MAP_HUGETLB 缓解。


      四、Read-Ahead:从顺序检测到异步流水线

      Linux 的预读子系统(mm/readahead.c)实现了基于窗口的动态预读算法,它的精妙之处在于自适应 —— 既能识别顺序读的典型场景,又不会因偶尔的随机访问而预读过量数据。

      4.1 预读窗口的状态机

      内核为每个 address_space 维护一个预读窗口(struct file_ra_state),其核心字段:

      
      // include/linux/fs.h
      struct file_ra_state {
          pgoff_t start;          // 窗口起始索引
          unsigned int size;      // 窗口大小(页数)
          unsigned int async_size;// 异步触发点(前沿)
          unsigned int ra_pages;  // 最大窗口大小(默认 128KB / PAGE_SIZE)
          unsigned int mmap_miss; // 记录 mmap 缺页不命中的次数
          loff_t prev_pos;        // 上次读取的偏移
      };
      

      状态流转如下:

      1. 初始态:窗口大小为 0,首次读入 1 页
      2. 顺序命中检测:若下次读取紧接上次结束位置,窗口大小翻倍(2→4→8→... 最大到 ra_pages)
      3. 异步触发:当进程读到窗口的 async_size 位置时,触发异步页面的读入
      4. 中断检测:若读取位置与预期不符,窗口大小衰减;连续不命中 2 次,窗口直接归零(标记为随机访问)
      5. 4.2 自定义预读策略

        对于已知访问模式的应用,可以 madvise() 提示内核:

        
        #include <sys/mman.h>
        
        void *data = mmap(NULL, file_size, PROT_READ, MAP_PRIVATE, fd, 0);
        
        // 告诉内核:我将顺序读取这块映射
        madvise(data, file_size, MADV_SEQUENTIAL);
        
        // 告诉内核:我需要这些页面立即加载
        madvise(data + offset, chunk_size, MADV_WILLNEED);
        
        // 内核预读结束后,可以释放不再需要的页面
        madvise(data, first_chunk, MADV_DONTNEED);
        

        4.3 实战案例:MyRocks/RocksDB 中的预读调优

        RocksDB 的 wal_dir 和 SST 文件读取路径对预读高度敏感。在写多读少的场景下,默认的 128KB 预读窗口反而造成缓存污染。解决方案:

        
        // 在 RocksDB 中通过 fallocate + FADV_DONTNEED 控制
        #include <fcntl.h>
        
        // 文件打开时声明随机访问模式
        posix_fadvise(fd, 0, 0, POSIX_FADV_RANDOM);
        
        // 预读控制:通过 Env::RandomAccessFile 接口
        class PosixRandomAccessFile : public RandomAccessFile {
            // RocksDB 内部使用 2MB 预读窗口优化 SST 点查
            size_t GetRequiredBufferAlignment() const override { return 4096; }
            // hint 机制
            void Prefetch(uint64_t offset, size_t n) override {
                posix_fadvise(fd_, offset, n, POSIX_FADV_WILLNEED);
            }
        };
        

        五、回写机制:从脏页到磁盘

        数据写入 Page Cache 后并不会立即刷盘,而是被标记为脏页(Dirty Page),由后台 flusher 线程在特定条件下批量回写。

        5.1 脏页的生命周期

        
        // 标记页面为脏的调用链:
        // write() → ... → aops->write_begin() / write_end()
        //            → set_page_dirty_balance() 或 mark_buffer_dirty()
        
        void account_page_dirty(struct page *page, struct address_space *mapping)
        {
            struct mem_cgroup *memcg;
            memcg = mem_cgroup_begin(page);
            if (TestSetPageDirty(page))
                return; // 已经是脏页,无需重复记账
            __mod_lruvec_page_state(page, NR_FILE_DIRTY, 1);
            __mod_node_page_state(pgdat, NR_FILE_DIRTY, 1);
            // 将 inode 加入 bdi_writeback 的脏链表
            mapping->wb_work->nr_pages++;
            memcg_end(memcg);
        }
        

        5.2 回写触发的三个条件

        由 fs/fs-writeback.c 中的逻辑控制:

        1. 周期性回写:dirty_writeback_centisecs(默认 500,即 5 秒)触发后台 flusher
        2. 比例回写:当脏页占比超过 dirty_ratio(默认 20%)或 dirty_bytes 时,进程自身的 write() 会被阻塞,同步刷盘
        3. 内存压力回写:当系统内存严重短缺时,kswapd 触发紧急回写
        4. 核心参数(/proc/sys/vm/):

          参数 默认值 含义
          dirty_background_ratio 10% 后台回写启动的脏页比例阈值
          dirty_background_bytes 0(禁用) 后台回写的绝对字节阈值
          dirty_ratio 20% 进程阻塞刷盘的脏页比例阈值
          dirty_bytes 0(禁用) 进程阻塞刷盘的绝对字节阈值
          dirty_writeback_centisecs 500 (5s) 后台 flusher 唤醒周期
          dirty_expire_centisecs 3000 (30s) 脏页超过此年龄即被视为过期
          nr_requests 128 块层请求队列深度

          5.3 BDI(Backing Device Info)写回架构

          现代 Linux 中,每个块设备都有独立的 BDI 线程,避免全局锁竞争:

          
          struct bdi_writeback {
              struct backing_dev_info *bdi;       // 指向父设备
              unsigned long last_old_flush;       // 上次刷盘时间
              struct list_head b_dirty;           // 脏 inode 链表
              struct list_head b_io;              // 待回写 inode 链表
              struct list_head b_more_io;         // 积压 inode 链表
              unsigned long nr_dirty, nr_io;      // 链表计数
              struct delayed_work dwork;          // 延迟工作项
              // ...
          };
          

          NFS 网络设备没有块设备,其 BDI 由 register_bdi() 动态创建,保证每设备独立的回写控制。

          5.4 实战:快速刷盘与文件系统同步

          
          #include <unistd.h>
          #include <fcntl.h>
          
          // 异步触发回写(非阻塞,仅标记脏页)
          sync_file_range(fd, offset, nbytes, SYNC_FILE_RANGE_WRITE);
          
          // 等待特定范围的数据到达磁盘
          sync_file_range(fd, offset, nbytes, 
              SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE | SYNC_FILE_RANGE_WAIT_AFTER);
          
          // fdatasync:仅同步数据和必要的元数据(比 fsync 快)
          fdatasync(fd);
          

          sync_file_range 相比 fsync() 的颗粒度更细,适合数据库日志逐条刷盘的场景。


          六、O_DIRECT:绕过 Page Cache 的工程取舍

          O_DIRECT 允许用户态程序直接与 DMA 控制器交互,跳过 Page Cache 实现真正的 Direct I/O。在设计高吞吐 I/O 系统时,是否使用 O_DIRECT 是需要权衡的重要决策。

          6.1 O_DIRECT 的真实工作流程

          开启 O_DIRECT 后,内核走 generic_file_direct_read/write() 路径,而非普通的 filemap_read/write():

          
          // mm/filemap.c
          ssize_t generic_file_direct_read(struct kiocb *iocb, struct iov_iter *to)
          {
              // 1. 检查用户缓冲区对齐(通常为 512 字节或 4096 字节)
              // 2. 使用 get_user_pages_fast() 锁定用户缓冲区内存
              // 3. 在块层准备 BIO 请求,直接 DMA 到用户空间
              // 4. 调用 submit_bio() 排队 I/O 请求
              
              // 如果底层设备不支持直接 I/O,可能退回到 buffer I/O
          }
          

          O_DIRECT 的核心要求:

          • 文件偏移必须是磁盘扇区大小(通常 512B)的整数倍
          • I/O 大小必须是扇区大小的整数倍
          • 用户缓冲区内存必须页对齐(posix_memalign 分配)

          6.2 深度对比:Page Cache I/O vs O_DIRECT

          维度 缓冲 I/O(默认) O_DIRECT
          拷贝次数 磁盘→Page Cache→用户态(2次 DMA) 磁盘→用户态(1次 DMA)
          CPU 消耗 涉及 memcpy 释放 CPU 周期 零拷贝,CPU 占用更低
          内存占用 RAM 自动用作缓存 必须用户态自行管理缓存池
          对小 I/O 的影响 合并相邻小 I/O 机会大 每个 I/O 直达磁盘,延迟不可控
          一致性保证 写入后数据在 Page Cache,掉电可能丢失 数据直达磁盘,持久化语义明确
          文件系统元数据 异步延迟写入 需要手动控制 fsync/fdatasync
          适用场景 通用文件 I/O、Web 服务、NFS 共享 数据库、消息队列、自建缓存系统

          6.3 实战经验:PostgreSQL 和 MySQL 中使用 O_DIRECT 的真实效果

          PostgreSQL 使用 O_DIRECT 的场景主要是 WAL(Write-Ahead Log),避免"双重缓存"问题 -- 既有操作系统的 Page Cache,也有数据库自己的 shared_buffers。这在高负载场景下通常能提升 15-25% 的写入吞吐。

          MySQL/InnoDB 通过 innodb_flush_method=O_DIRECT 同样避免双重缓存,但在 RAID 卡带电池备份缓存(BBU)的场景下,关闭 O_DIRECT 反而更快 -- 因为 RAID 卡自身的缓存层本身充当了 Page Cache 的角色。

          关键经验:使用 O_DIRECT 时,应用层必须实现自己的预读(Read-Ahead)和缓存池,否则随机 I/O 性能会显著下降。这也是为什么 RocksDB、PostgreSQL 等数据库都实现了大块 I/O(8KB-64KB)的缓存管理,而非直接使用单扇区 I/O。

          6.4 Linux 5.0+ 的优化:SPLICE 与 splice 系统调用

          在不满足 O_DIRECT 对齐要求又需要减少拷贝次数的场景下,splice() 和 tee() 系统调用提供了零拷贝管道传输:

          
          // 零拷贝:文件 → 管道
          int pipefd[2];
          pipe(pipefd);
          splice(fd, &offset, pipefd[1], NULL, 65536, SPLICE_F_MOVE);
          splice(pipefd[0], NULL, socket_fd, NULL, 65536, SPLICE_F_MOVE);
          
          // splice 在 Page Cache 和管道之间移动页面引用,不复制数据
          

          Nginx 的 sendfile 就是 splice 的典型应用,执行零拷贝文件输出。


          七、调优实战:生产环境中的 Page Cache 管理

          7.1 关键监控指标

          
          # 查看系统级 Page Cache 状态
          $ cat /proc/meminfo | grep -E "(Cached|Buffers|Dirty|Writeback|AnonPages|Mapped)"
          Buffers:         327980 kB
          Cached:        18394120 kB
          Dirty:           1200940 kB
          Writeback:         20308 kB
          Mapped:         42372112 kB
          
          # 查看具体进程的 Page Cache 使用量
          $ cat /proc/<pid>/smaps | grep -A 15 "/path/to/filename"
          7f8e5c000000-7f8e5d000000 rw-s 00000000 08:01 123456  /data/sst.db
          Size:             16384 kB
          KernelPageSize:        4 kB
          MMUPageSize:           4 kB
          Rss:               12000 kB      # 驻留内存的缓存页
          Pss:               12000 kB
          Shared_Clean:          0 kB
          Shared_Dirty:          0 kB
          Private_Clean:     12000 kB
          Private_Dirty:         0 kB
          Referenced:        12000 kB
          Swap:                  0 kB
          
          # 通过 drop_caches 手动清除 Page Cache(调试用,勿用于生产)
          $ echo 3 > /proc/sys/sysctl.conf  # 清除 Page Cache + dentries + inodes
          

          7.2 生产调优实践

          案例一:高频写入日志服务器的脏页调优

          
          # 问题:日志收集服务器脏页暴涨导致同步刷盘阻塞
          
          # 方案:调快后台回写频次,缩短刷盘延迟
          echo 50   > /proc/sys/vm/dirty_writeback_centisecs    # 0.5s 唤醒一次
          echo 5000 > /proc/sys/vm/dirty_expire_centisecs       # 5s 过期
          echo 15   > /proc/sys/vm/dirty_background_ratio        # 15% 即开始异步刷盘
          echo 30   > /proc/sys/vm/dirty_ratio                    # 30% 开始阻塞刷盘
          
          # 对于 NVMe 设备,可启用更激进的 I/O 调度
          echo 256 > /sys/block/nvme0n1/queue/nr_requests
          echo none > /sys/block/nvme0n1/queue/scheduler          # NVMe 不需要调度器
          

          案例二:大数据索引构建时的缓存隔离

          cgroup v2 提供 memory.max 和 memory.high 等接口,可以限制特定任务组的内存使用,间接限制了 Page Cache 的占用:

          
          # 创建 cgroup 限制索引构建任务
          $ mkdir /sys/fs/cgroup/indexer
          $ echo "8G" > /sys/fs/cgroup/indexer/memory.max
          $ echo "6G" > /sys/fs/cgroup/indexer.memory.high
          
          # 启动索引进程
          $ cgexec -g memory:indexer ./build_index /data/base
          

          另外,通过 posix_fadvise() 的控制:

          
          // 构建索引时主动丢弃已读过的页面,防止污染缓存
          while (read_next_chunk()) {
              process_chunk(chunk);
              // 读完即弃,不占用 Page Cache
              posix_fadvise(fd, chunk_offset, chunk_size, POSIX_FADV_DONTNEED);
          }
          

          7.3 与 io_uring 的深度结合

          Linux 5.1+ 引入的 io_uring 为 Page Cache 管理提供了更高效的异步 I/O 入口。对于需要精细控制缓存的场景,io_uring 支持 CQES_SETUP_SPLICE 等高级特性:

          
          // 使用 io_uring 进行带缓冲的异步 I/O
          struct io_uring ring;
          io_uring_queue_init(256, &ring, IORING_SETUP_SQPOLL);  // 内核轮询模式
          
          // 预注册缓冲区,避免每次 I/O 的 get_user_pages 开销
          struct iovec iovecs[16];
          posix_memalign(&buf, 4096, 65536);
          iovecs[0] = (struct iovec){ .iov_base = buf, .iov_len = 65536 };
          io_uring_register_buffers(&ring, iovecs, 1);
          
          // 发起固定缓冲区读请求(零额外拷贝)
          struct io_uring_sqe *sqe = io_uring_get_sqe(&ring);
          io_uring_prep_read_fixed(sqe, fd, buf, 4096, offset, 0, 0);
          io_uring_submit(&ring);
          

          八、现代演进:大页文件与 iomap

          8.1 Large Folios(大页文件)

          Linux 5.16+ 引入了 large folios 支持,允许 Page Cache 使用 larger-than-page(通常是 2MB 或 1GB)的大页来缓存文件数据。这对于大文件顺序读(如数据库表扫描、科学计算数据加载)场景有显著性能提升,同时减少 TLB 压力:

          
          // 启用 large folio 的代码路径
          // mm/filemap.c: filemap_alloc_folio()
          struct folio *folio = filemap_alloc_folio(mapping->gfp_mask, order);
          // order > 0 表示分配 2^order 个连续页面组成的大页
          

          在 ext4/xfs 文件系统中,可以通过 mkfs.ext4 -O bigalloc 启用 cluster 分配策略,或者在挂载时使用 -o bs=65536 增大块大小来提高 large folio 的命中率。

          8.2 iomap:新一代 I/O 映射层

          随着文件系统的演进(尤其是 XFS 的 DAX 支持),传统的 address_space_operations(readpages/write/pages)被 iomap 框架替代。iomap 通过 extent 级别的映射,避免了逐页处理的粒度损耗:

          
          // fs/iomap/buffered.c
          int iomap_readpage(struct page *page, const struct iomap_ops *ops)
          {
              // 查找文件偏移对应的数据 extent
              // 一次性提交 extent 范围覆盖的所有块的 I/O
              // 对于 NVMe 多队列和 ZNS 设备,批量提交效率更高
          }
          

          iomap 在设计上天然适配 ZNS SSD 和 DAX(直接访问持久内存),代表了 Page Cache 架构的未来方向。


          九、总结与工程建议

          回顾 Page Cache 的完整图景,以下工程建议值得参考:

          1. 通用文件 I/O 优先使用缓冲 I/O,利用内核智能的预读和回写机制;只有在对延迟确定性要求极高(如数据库 WAL)或应用层有独立缓存时才考虑 O_DIRECT。
            1. 脏页比例监控是 I/O 健康度的先行指标。如果 Dirty 持续超过 dirty_ratio 的一半,应及时调整后台回写参数或增加磁盘带宽。
              1. 避免在生产环境使用 drop_caches。除非为了基准测试预热,否则手动清除 Page Cache 会导致系统短暂的性能"悬崖"。
                1. 善用 posix_fadvise 和 madvise。大量随机访问文件前声明 POSIX_FADV_RANDOM,是成本最低的性能调优。
                  1. 关注 large folios 和 iomap。对于新兴的数据库和 AI 推理工作负载,这些新特性将在未来几年内持续释放性能红利。
                  2. Page Cache 是 Linux 最古老也最复杂的子系统之一,其设计体现了操作系统"用空间换时间"的核心哲学。理解它,不仅是为了在日常工作中写出更高效的代码,更是为了建立对系统全局性能的直觉。


                    *参考资料:Linux kernel v6.8 source, Understanding the Linux Kernel 3rd Edition, BPF Performance Tools*

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部