前言:缓存管理——Linux内核的"隐形骨架"

inux内核的缓存管理技术——内核的"隐形骨架",支撑着文件系统、块设备、DMA传输、内存分配等各个子系统的高效运转。

本文将基于Linux内核主线源码(v6.x),系统性地梳理所有缓存管理技术,构建一个完整的技术图谱。每个技术都将包含:设计思想、核心数据结构、初始化流程、申请/回收机制、适用场景。


第一部分:缓存管理技术全景树形图

┌─────────────────────────────────────────────────────────────────────────────┐
│                         Linux内核缓存管理技术全景图                          │
│                              (基于内核v6.x)                                   │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  ┌──────────────────────────────────────────────────────────────────────┐  │
│  │ 1. 通用内存分配器层 (基础依赖)                                        │  │
│  │    ├── SLAB/SLUB/SLOB - 通用对象缓存                                  │  │
│  │    ├── kmalloc - 通用内存分配                                         │  │
│  │    ├── vmalloc - 虚拟连续内存                                         │  │
│  │    └── CMA (Contiguous Memory Allocator) - 连续内存分配器            │  │
│  └──────────────────────────────────────────────────────────────────────┘  │
│                                    │                                        │
│                                    ▼                                        │
│  ┌──────────────────────────────────────────────────────────────────────┐  │
│  │ 2. 网络子系统专用缓存                                                 │  │
│  │    ├── sk_buff - 网络数据包缓冲区 (已分析)                            │  │
│  │    ├── sock_alloc_send_skb - socket发送缓存                           │  │
│  │    ├── TCP recv_buf - TCP接收缓冲区                                   │  │
│  │    ├── netdev_alloc_skb - 设备层接收缓存                              │  │
│  │    ├── napi_alloc_skb - NAPI高性能缓存                                │  │
│  │    └── page_frag_cache - 页分片缓存                                   │  │
│  └──────────────────────────────────────────────────────────────────────┘  │
│                                    │                                        │
│                                    ▼                                        │
│  ┌──────────────────────────────────────────────────────────────────────┐  │
│  │ 3. 存储/文件系统专用缓存                                              │  │
│  │    ├── buffer_head - 块缓冲区头                                       │  │
│  │    ├── bio (Block I/O) - 块I/O请求                                    │  │
│  │    ├── page cache - 页高速缓存 (核心)                                 │  │
│  │    ├── inode/dentry cache - 文件系统元数据缓存                        │  │
│  │    ├── fscache - 文件系统缓存层                                       │  │
│  │    ├── bcache - 块设备缓存                                            │  │
│  │    └── dm-cache - 设备映射器缓存                                      │  │
│  └──────────────────────────────────────────────────────────────────────┘  │
│                                    │                                        │
│                                    ▼                                        │
│  ┌──────────────────────────────────────────────────────────────────────┐  │
│  │ 4. DMA/IO专用缓存                                                     │  │
│  │    ├── dma_pool / dma_pool_alloc - DMA池                              │  │
│  │    ├── dma_buf - DMA缓冲区共享                                        │  │
│  │    ├── ion - 内存分配器(Android)                                      │  │
│  │    ├── dmabuf_heap - DMA-BUF堆                                       │  │
│  │    ├── videobuf2 - 视频缓冲区 (已分析)                                │  │
│  │    └── dmabuf_sync - DMA缓存同步                                      │  │
│  └──────────────────────────────────────────────────────────────────────┘  │
│                                    │                                        │
│                                    ▼                                        │
│  ┌──────────────────────────────────────────────────────────────────────┐  │
│  │ 5. 多媒体专用缓存                                                     │  │
│  │    ├── vb2_v4l2_buffer - V4L2缓冲区                                  │  │
│  │    ├── alsa_buffer - 音频环形缓冲区                                   │  │
│  │    ├── snd_pcm_ringbuf - PCM环形缓冲                                 │  │
│  │    ├── drm_gem_object - DRM GEM对象                                  │  │
│  │    ├── dma_buf_imp - DMA-BUF导入/导出                                │  │
│  │    └── cma_alloc - 连续内存分配                                      │  │
│  └──────────────────────────────────────────────────────────────────────┘  │
│                                    │                                        │
│                                    ▼                                        │
│  ┌──────────────────────────────────────────────────────────────────────┐  │
│  │ 6. 高性能计算/加速器专用缓存                                          │  │
│  │    ├── dmabuf_system_heap - 系统堆                                    │  │
│  │    ├── dmabuf_cma_heap - CMA堆                                        │  │
│  │    ├── tee_shm - TEE共享内存                                          │  │
│  │    ├── vfio_dma - VFIO DMA区域                                        │  │
│  │    └── uio_dma - UIO DMA缓冲区                                        │  │
│  └──────────────────────────────────────────────────────────────────────┘  │
│                                    │                                        │
│                                    ▼                                        │
│  ┌──────────────────────────────────────────────────────────────────────┐  │
│  │ 7. 内核内部专用缓存                                                   │  │
│  │    ├── kmem_cache - SLAB缓存 (通用)                                   │  │
│  │    ├── percpu_alloc - per-CPU分配器                                   │  │
│  │    ├── mempool - 内存池 (紧急备用)                                    │  │
│  │    ├── kfifo - 无锁环形队列                                           │  │
│  │    ├── ring_buffer - 跟踪环形缓冲区                                   │  │
│  │    ├── relayfs - 中继文件系统                                         │  │
│  │    └── debugfs_buffer - 调试缓冲区                                    │  │
│  └──────────────────────────────────────────────────────────────────────┘  │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

第二部分:通用内存分配器层(基础依赖)

2.1 SLAB/SLUB分配器

/**
 * struct kmem_cache - SLAB缓存的核心结构
 * @brief 管理特定大小对象的缓存池
 * 
 * @note 源码位置:include/linux/slab.h
 * @see mm/slub.c
 */
struct kmem_cache {
    /* per-CPU数据(无锁访问) */
    struct kmem_cache_cpu __percpu *cpu_slab;
    
    /* 节点数据(NUMA感知) */
    struct kmem_cache_node *node[MAX_NUMNODES];
    
    unsigned long size;           /*!< 对象大小(含对齐)*/
    unsigned long object_size;    /*!< 原始对象大小 */
    int offset;                    /*!< 空闲指针偏移 */
    
    /* 分配标志 */
    slab_flags_t flags;
    unsigned int useroffset;       /*!< 用户空间可访问偏移 */
    unsigned int usersize;         /*!< 用户空间可访问大小 */
    
    /* 构造函数 */
    void (*ctor)(void *);
    
    /* 统计信息 */
    int refcount;                  /*!< 引用计数 */
    int objs_per_slab;             /*!< 每个slab的对象数 */
};
​
/**
 * @brief 创建SLAB缓存
 * @param name 缓存名称(在/proc/slabinfo中可见)
 * @param size 对象大小
 * @param align 对齐要求
 * @param flags 标志
 * @param ctor 构造函数
 */
struct kmem_cache *
kmem_cache_create(const char *name, unsigned int size,
                  unsigned int align, slab_flags_t flags,
                  void (*ctor)(void *))
{
    struct kmem_cache *s;
    int err;
​
    /* 1. 从全局缓存池分配kmem_cache结构 */
    s = kmem_cache_zalloc(kmem_cache, GFP_KERNEL);
    if (!s)
        return ERR_PTR(-ENOMEM);
​
    /* 2. 设置基本属性 */
    s->name = name;
    s->size = size;
    s->align = align;
    s->flags = flags;
    s->ctor = ctor;
​
    /* 3. 计算每个slab的对象数 */
    calculate_slab_order(s);
​
    /* 4. 初始化per-CPU缓存 */
    init_freelist_randomization(s);
    init_tracking(s);
​
    /* 5. 加入全局链表 */
    list_add(&s->list, &slab_caches);
​
    return s;
}
EXPORT_SYMBOL(kmem_cache_create);

适用场景:所有需要频繁分配固定大小对象的子系统,如inode、dentry、sk_buff等。

2.2 CMA(连续内存分配器)

/**
 * struct cma - 连续内存分配器
 * @brief 管理一大块物理连续内存,用于DMA/多媒体
 * 
 * @note 源码位置:include/linux/cma.h
 * @see mm/cma.c
 */
struct cma {
    unsigned long   base_pfn;      /*!< 起始页帧号 */
    unsigned long   count;         /*!< 页面数量 */
    unsigned long   *bitmap;       /*!< 位图(管理分配状态)*/
    unsigned int    order_per_bit; /*!< 每个bit代表的页阶 */
    struct mutex    lock;
    const char      *name;
};
​
/**
 * @brief 预留CMA区域(设备树或内核参数)
 * 
 * 设备树示例:
 * reserved-memory {
 *     #address-cells = <2>;
 *     #size-cells = <2>;
 *     ranges;
 * 
 *     linux,cma {
 *         compatible = "shared-dma-pool";
 *         reusable;
 *         size = <0 0x10000000>;  // 256MB
 *         linux,cma-default;
 *     };
 * };
 */
​
/**
 * @brief 从CMA分配连续内存
 * @param cma CMA区域
 * @param count 页面数量
 * @param align 对齐要求
 * @param no_warn 是否告警
 */
struct page *cma_alloc(struct cma *cma, unsigned long count,
                       unsigned int align, bool no_warn)
{
    unsigned long mask, offset;
    unsigned long pfn = -1;
    unsigned long start = 0;
​
    if (!cma || !cma->count)
        return NULL;
​
    mask = (1 << align) - 1;
​
    mutex_lock(&cma->lock);
​
    /* 1. 在位图中查找连续区域 */
    for (;;) {
        unsigned long bitmap_max, bitmap_bit;
        size_t bitmap_count;
        unsigned long start_bit;
​
        start_bit = ALIGN(start, 1 << cma->order_per_bit);
        bitmap_max = cma->count >> cma->order_per_bit;
        bitmap_bit = find_next_zero_bit(cma->bitmap, bitmap_max, start_bit);
        if (bitmap_bit >= bitmap_max)
            break;
​
        /* 2. 计算物理页帧 */
        pfn = cma->base_pfn + (bitmap_bit << cma->order_per_bit);
        start = bitmap_bit + 1;
​
        /* 3. 标记为已使用 */
        bitmap_set(cma->bitmap, bitmap_bit, bitmap_count);
        break;
    }
​
    mutex_unlock(&cma->lock);
​
    if (pfn == -1)
        return NULL;
​
    return pfn_to_page(pfn);
}
EXPORT_SYMBOL_GPL(cma_alloc);

适用场景:摄像头传感器缓冲区、GPU内存、显示缓冲区等需要物理连续内存的设备。


第三部分:网络子系统专用缓存

3.1 sock_alloc_send_skb - socket发送缓存

/**
 * sock_alloc_send_skb() - 为socket分配发送skb
 * @sk: socket
 * @size: 数据大小
 * @force: 强制分配标志
 * @err: 错误码输出
 * 
 * @note 源码位置:net/core/sock.c
 * 
 * @details 这个函数比普通alloc_skb多了:
 *          1. 遵守socket内存限制(sk_sndbuf)
 *          2. 支持阻塞等待内存可用
 *          3. 自动处理分片
 */
struct sk_buff *sock_alloc_send_skb(struct sock *sk, unsigned long size,
                                     int force, int *err)
{
    struct sk_buff *skb;
    int npages;
    int i;
​
    /* 1. 检查内存限制 */
    if (atomic_read(&sk->sk_wmem_alloc) >= sk->sk_sndbuf) {
        if (force)
            goto alloc;
        
        /* 等待内存可用 */
        set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
        *err = -EAGAIN;
        return NULL;
    }
​
alloc:
    /* 2. 计算所需页数(用于分片)*/
    size = SKB_DATA_ALIGN(size);
    npages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
​
    if (npages <= MAX_SKB_FRAGS) {
        /* 使用分片模式 */
        skb = alloc_skb(0, sk->sk_allocation);
        if (!skb)
            goto failure;
​
        for (i = 0; i < npages; i++) {
            struct page *page = alloc_pages(sk->sk_allocation, 0);
            if (!page)
                goto failure;
​
            skb_fill_page_desc(skb, i, page, 0, PAGE_SIZE);
        }
    } else {
        /* 使用线性区 */
        skb = alloc_skb(size, sk->sk_allocation);
        if (!skb)
            goto failure;
    }
​
    /* 3. 更新socket内存统计 */
    skb_set_owner_w(skb, sk);
​
    return skb;
​
failure:
    *err = -ENOBUFS;
    return NULL;
}
EXPORT_SYMBOL(sock_alloc_send_skb);

3.2 TCP接收缓冲区

/**
 * struct tcp_sock - TCP套接字状态
 * @brief 包含TCP特有的接收缓冲区管理
 * 
 * @note 源码位置:include/linux/tcp.h
 */
struct tcp_sock {
    /* 接收缓冲区 */
    struct sk_buff_head    recv_queue;    /*!< 已接收但未读取的数据 */
    struct sk_buff_head    out_of_order_queue; /*!< 乱序包队列 */
    u32                    rcv_nxt;       /*!< 下一个期望接收的序号 */
    u32                    copied_seq;    /*!< 用户已拷贝的序号 */
    u32                    rcv_wnd;       /*!< 接收窗口大小 */
    u32                    rcv_ssthresh;  /*!< 接收窗口慢启动阈值 */
    u32                    rcv_tstamp;    /*!< 接收时间戳 */
};
​
/**
 * tcp_queue_rcv() - 将skb放入TCP接收队列
 * @sk: socket
 * @skb: 接收的数据包
 * @fragstolen: 是否窃取了分片
 */
static void tcp_queue_rcv(struct sock *sk, struct sk_buff *skb,
                           int *fragstolen)
{
    struct tcp_sock *tp = tcp_sk(sk);
​
    /* 1. 如果当前接收队列为空,直接添加 */
    if (skb_queue_empty(&sk->sk_receive_queue)) {
        skb_queue_head(&sk->sk_receive_queue, skb);
        return;
    }
​
    /* 2. 按顺序插入(TCP保证有序)*/
    __skb_queue_tail(&sk->sk_receive_queue, skb);
​
    /* 3. 更新接收窗口 */
    tcp_rcv_space_adjust(sk);
}
​
/**
 * tcp_rcv_established() - TCP连接建立状态的数据接收
 */
void tcp_rcv_established(struct sock *sk, struct sk_buff *skb)
{
    struct tcp_sock *tp = tcp_sk(sk);
​
    /* 1. 检查序号是否预期 */
    if (TCP_SKB_CB(skb)->seq != tp->rcv_nxt)
        goto out_of_order;
​
    /* 2. 处理数据 */
    tcp_queue_rcv(sk, skb, &fragstolen);
​
    /* 3. 更新rcv_nxt */
    tp->rcv_nxt += skb->len;
​
    /* 4. 如果有空间,发送ACK */
    if (tcp_ack_scheduled(tp))
        tcp_send_ack(sk);
    
    return;
​
out_of_order:
    /* 乱序包处理 */
    tcp_data_queue_ofo(sk, skb);
}

3.3 page_frag_cache - 页分片缓存

/**
 * struct page_frag_cache - per-CPU页分片缓存
 * @brief 用于高效分配小块内存(从页中切分)
 * 
 * @note 源码位置:include/linux/gfp.h
 */
struct page_frag_cache {
    void *va;                    /*!< 虚拟地址 */
    __u16 offset;                /*!< 当前偏移 */
    __u16 size;                  /*!< 剩余大小 */
    __u8 pfmemalloc;             /*!< 是否使用紧急内存池 */
    struct page *page;           /*!< 当前页 */
};
​
/**
 * page_frag_alloc() - 从页分片缓存分配内存
 * @nc: per-CPU缓存
 * @sz: 需要的大小
 * @gfp: GFP标志
 * 
 * @return: 分配的虚拟地址
 */
void *page_frag_alloc(struct page_frag_cache *nc,
                      unsigned int sz, gfp_t gfp_mask)
{
    unsigned int size = PAGE_SIZE;
    struct page *page;
    int offset;
​
    /* 1. 对齐大小 */
    sz = ALIGN(sz, SMP_CACHE_BYTES);
​
    /* 2. 如果当前页有足够空间 */
    if (nc->page && nc->offset + sz <= size) {
        offset = nc->offset;
        nc->offset += sz;
        return nc->va + offset;
    }
​
    /* 3. 需要分配新页 */
    page = alloc_pages_node(NUMA_NO_NODE, gfp_mask, 0);
    if (!page)
        return NULL;
​
    /* 4. 更新缓存 */
    if (nc->page)
        put_page(nc->page);
​
    nc->page = page;
    nc->va = page_address(page);
    nc->offset = sz;
    nc->size = size;
​
    return nc->va;
}
EXPORT_SYMBOL(page_frag_alloc);

适用场景:NAPI接收路径、小块DMA缓冲区分配。


第四部分:存储/文件系统专用缓存

4.1 buffer_head - 块缓冲区头

/**
 * struct buffer_head - 块I/O缓冲区头
 * @brief 代表磁盘上的一个块在内存中的缓冲区
 * 
 * @note 源码位置:include/linux/buffer_head.h
 */
struct buffer_head {
    unsigned long b_state;          /*!< 缓冲区状态 */
    struct buffer_head *b_this_page; /*!< 同一页上的循环链表 */
    struct page *b_page;             /*!< 所属页 */
​
    sector_t b_blocknr;              /*!< 块号 */
    size_t b_size;                   /*!< 块大小 */
    char *b_data;                    /*!< 数据指针 */
​
    struct block_device *b_bdev;     /*!< 所属块设备 */
    bh_end_io_t *b_end_io;           /*!< I/O完成回调 */
    void *b_private;                  /*!< 回调私有数据 */
​
    atomic_t b_count;                 /*!< 引用计数 */
};
​
/**
 * @brief 块缓冲区状态标志
 */
enum bh_state_bits {
    BH_Uptodate,    /*!< 数据有效 */
    BH_Dirty,       /*!< 数据脏(需要写回)*/
    BH_Lock,        /*!< 正在I/O */
    BH_Req,         /*!< 已有I/O请求 */
    BH_Mapped,      /*!< 已映射到磁盘 */
    BH_New,         /*!< 新分配的块 */
    BH_Async_Read,  /*!< 异步读进行中 */
    BH_Async_Write, /*!< 异步写进行中 */
    BH_Delay,       /*!< 延迟分配 */
    BH_Unwritten,   /*!< 预分配但未写入 */
};
​
/**
 * alloc_buffer_head() - 分配buffer_head
 * @gfp_flags: GFP标志
 * 
 * @note 从bh_cache slab分配
 */
struct buffer_head *alloc_buffer_head(gfp_t gfp_flags)
{
    struct buffer_head *bh;
​
    /* 从slab缓存分配 */
    bh = kmem_cache_zalloc(bh_cachep, gfp_flags);
    if (bh) {
        INIT_LIST_HEAD(&bh->b_assoc_buffers);
        spin_lock_init(&bh->b_uptodate_lock);
        atomic_set(&bh->b_count, 0);
    }
    return bh;
}
EXPORT_SYMBOL(alloc_buffer_head);

适用场景:ext4、XFS等传统文件系统的块I/O操作。

4.2 bio(Block I/O) - 现代块I/O

/**
 * struct bio - 块I/O请求的现代表示
 * @brief 代表一个I/O请求,支持向量化I/O
 * 
 * @note 源码位置:include/linux/blk_types.h
 */
struct bio {
    struct bio      *bi_next;          /*!< 队列中的下一个bio */
    struct block_device *bi_bdev;      /*!< 目标块设备 */
    blk_status_t    bi_status;         /*!< I/O状态 */
    unsigned int    bi_opf;            /*!< 操作码和标志 */
​
    unsigned short  bi_flags;           /*!< 状态标志 */
    unsigned short  bi_ioprio;          /*!< I/O优先级 */
​
    struct bvec_iter bi_iter;           /*!< 当前迭代位置 */
​
    /* 完成回调 */
    bio_end_io_t    *bi_end_io;
    void            *bi_private;
​
    /* I/O统计 */
    atomic_t        __bi_remaining;     /*!< 剩余引用 */
    atomic_t        __bi_cnt;           /*!< 引用计数 */
​
    /* 向量数组(最多BIO_MAX_VECS个)*/
    struct bio_vec  *bi_io_vec;          /*!< 实际向量数组 */
    struct bio_vec  bi_inline_vecs[];    /*!< 内联向量(小请求)*/
};
​
/**
 * struct bio_vec - bio向量(页+偏移+长度)
 */
struct bio_vec {
    struct page     *bv_page;           /*!< 页 */
    unsigned int    bv_len;             /*!< 长度 */
    unsigned int    bv_offset;          /*!< 页内偏移 */
};
​
/**
 * bio_alloc() - 分配新的bio
 * @gfp_mask: GFP标志
 * @nr_iovecs: 预分配的向量数量
 * @bs: bio集(可选)
 */
struct bio *bio_alloc(gfp_t gfp_mask, unsigned short nr_iovecs,
                      struct bio_set *bs)
{
    struct bio *bio;
    void *p;
​
    /* 1. 如果指定了bio_set,从池分配 */
    if (bs) {
        p = mempool_alloc(&bs->bio_pool, gfp_mask);
        bio = p + bs->front_pad;
        bio->bi_pool = bs;
    } else {
        /* 2. 否则直接分配(小请求使用内联向量)*/
        if (nr_iovecs <= BIO_INLINE_VECS) {
            bio = kmalloc(sizeof(struct bio) +
                          nr_iovecs * sizeof(struct bio_vec),
                          gfp_mask);
        } else {
            bio = kmalloc(sizeof(struct bio), gfp_mask);
        }
    }
​
    if (!bio)
        return NULL;
​
    /* 3. 初始化 */
    bio->bi_flags = 0;
    bio->bi_ioprio = 0;
    atomic_set(&bio->__bi_cnt, 1);
    atomic_set(&bio->__bi_remaining, 1);
​
    if (nr_iovecs) {
        if (nr_iovecs <= BIO_INLINE_VECS) {
            bio->bi_io_vec = bio->bi_inline_vecs;
        } else {
            bio->bi_io_vec = bio_alloc_bvec(nr_iovecs, gfp_mask);
            if (!bio->bi_io_vec)
                goto err_out;
        }
    }
​
    return bio;
}
EXPORT_SYMBOL(bio_alloc);

适用场景:现代存储栈(NVMe、多队列块层)、支持向量I/O的场景。

4.3 page cache - 页高速缓存

/**
 * struct address_space - 页缓存的核心结构
 * @brief 管理一个文件或设备的页缓存
 * 
 * @note 源码位置:include/linux/fs.h
 */
struct address_space {
    struct inode            *host;          /*!< 所属inode */
    struct xarray           i_pages;        /*!< 页缓存树(XArray)*/
    atomic_t                i_mmap_writable; /*!< 可写映射计数 */
    struct rb_root_cached   i_mmap;         /*!< 共享内存映射树 */
    unsigned long           nrpages;        /*!< 总页数 */
    unsigned long           nrexceptional;  /*!< 异常条目数 */
    struct address_space_operations *a_ops; /*!< 操作函数表 */
    unsigned long           flags;          /*!< 标志 */
    spinlock_t              private_lock;   /*!< 私有锁 */
    struct list_head        private_list;   /*!< 私有链表 */
};
​
/**
 * struct address_space_operations - 页缓存操作
 */
struct address_space_operations {
    /* 读取页 */
    int (*readpage)(struct file *, struct page *);
    int (*readpages)(struct file *, struct address_space *,
                     struct list_head *, unsigned int);
    
    /* 写入页 */
    int (*writepage)(struct page *, struct writeback_control *);
    int (*writepages)(struct address_space *, struct writeback_control *);
    
    /* 脏页跟踪 */
    void (*set_page_dirty)(struct page *);
    
    /* 释放页 */
    int (*releasepage)(struct page *, gfp_t);
    void (*freepage)(struct page *);
    
    /* 直接I/O */
    ssize_t (*direct_IO)(struct kiocb *, struct iov_iter *);
};
​
/**
 * find_get_page() - 在页缓存中查找页
 * @mapping: address_space
 * @offset: 页偏移
 * 
 * @note 使用XArray快速查找
 */
struct page *find_get_page(struct address_space *mapping, pgoff_t offset)
{
    struct page *page;
    
    rcu_read_lock();
    page = xa_load(&mapping->i_pages, offset);
    if (page && !page_cache_get_speculative(page))
        page = NULL;
    rcu_read_unlock();
    
    return page;
}
EXPORT_SYMBOL(find_get_page);

适用场景:所有文件系统的数据缓存、内存映射文件、共享库等。

4.4 fscache - 文件系统缓存层

/**
 * struct fscache_cache - 缓存后端
 * @brief 代表一个缓存后端(如cachefiles)
 */
struct fscache_cache {
    const struct fscache_cache_ops *ops;  /*!< 操作函数 */
    struct list_head                cache_link; /*!< 缓存链表 */
    void                            *cache_priv; /*!< 私有数据 */
    refcount_t                      ref;    /*!< 引用计数 */
    unsigned int                    flags;  /*!< 标志 */
    char                            name[16]; /*!< 名称 */
};
​
/**
 * struct fscache_cookie - 缓存对象标识
 */
struct fscache_cookie {
    struct hlist_bl_node        hash_link;  /*!< 哈希链表 */
    struct fscache_cache        *cache;     /*!< 关联的缓存 */
    struct fscache_object       *object;    /*!< 缓存对象 */
    unsigned long               flags;      /*!< 标志 */
    refcount_t                  ref;        /*!< 引用计数 */
};
​
/**
 * fscache_read_or_alloc_page() - 从缓存读取或分配页
 */
int fscache_read_or_alloc_page(struct fscache_cookie *cookie,
                                struct page *page,
                                fscache_rw_complete_t end_io_func,
                                void *context,
                                gfp_t gfp)
{
    struct fscache_cache *cache = cookie->cache;
    
    if (!cache || !cache->ops->read_or_alloc_page)
        return -ENOBUFS;
    
    return cache->ops->read_or_alloc_page(cookie, page,
                                          end_io_func, context, gfp);
}
EXPORT_SYMBOL(fscache_read_or_alloc_page);

适用场景:网络文件系统(NFS、AFS)的本地缓存、离线访问支持。


第五部分:DMA/IO专用缓存

5.1 dma_pool - DMA池

/**
 * struct dma_pool - DMA内存池
 * @brief 管理小尺寸DMA缓冲区的分配
 * 
 * @note 源码位置:include/linux/dmapool.h
 * @see drivers/base/dmapool.c
 */
struct dma_pool {
    struct list_head    page_list;      /*!< DMA页链表 */
    spinlock_t          lock;           /*!< 保护页链表 */
    size_t              size;           /*!< 对象大小 */
    size_t              allocation;     /*!< 每次分配的页大小 */
    size_t              boundary;       /*!< 边界对齐 */
    char                name[32];       /*!< 池名称 */
    struct device       *dev;           /*!< DMA设备 */
    unsigned int        flags;          /*!< 标志 */
};
​
/**
 * struct dma_page - DMA池中的一页
 */
struct dma_page {
    struct list_head    page_list;      /*!< 池中的链表 */
    void                *vaddr;         /*!< 虚拟地址 */
    dma_addr_t          dma;            /*!< DMA地址 */
    unsigned int        in_use;         /*!< 使用计数 */
    unsigned long       bitmap[0];      /*!< 分配位图 */
};
​
/**
 * dma_pool_create() - 创建DMA池
 * @name: 池名称
 * @dev: DMA设备
 * @size: 对象大小
 * @align: 对齐要求
 * @boundary: 边界(0表示无边界)
 */
struct dma_pool *dma_pool_create(const char *name, struct device *dev,
                                  size_t size, size_t align, size_t boundary)
{
    struct dma_pool *retval;
​
    if (align == 0)
        align = 1;
​
    /* 1. 分配池结构 */
    retval = kmalloc(sizeof(*retval), GFP_KERNEL);
    if (!retval)
        return retval;
​
    /* 2. 初始化 */
    strlcpy(retval->name, name, sizeof(retval->name));
    retval->dev = dev;
    retval->size = size;
    retval->allocation = PAGE_SIZE;
    retval->boundary = boundary;
    retval->flags = 0;
    INIT_LIST_HEAD(&retval->page_list);
    spin_lock_init(&retval->lock);
​
    return retval;
}
EXPORT_SYMBOL(dma_pool_create);
​
/**
 * dma_pool_alloc() - 从DMA池分配对象
 * @pool: DMA池
 * @mem_flags: GFP标志
 * @handle: 输出DMA地址
 */
void *dma_pool_alloc(struct dma_pool *pool, gfp_t mem_flags,
                      dma_addr_t *handle)
{
    unsigned long flags;
    struct dma_page *page;
    size_t offset;
    void *retval;
​
    spin_lock_irqsave(&pool->lock, flags);
    
    /* 1. 查找有空闲空间的页 */
    list_for_each_entry(page, &pool->page_list, page_list) {
        int i = find_first_zero_bit(page->bitmap,
                                    pool->allocation / pool->size);
        if (i < pool->allocation / pool->size) {
            /* 2. 找到空闲位置 */
            set_bit(i, page->bitmap);
            page->in_use++;
            offset = i * pool->size;
            *handle = page->dma + offset;
            retval = page->vaddr + offset;
            spin_unlock_irqrestore(&pool->lock, flags);
            return retval;
        }
    }
​
    /* 3. 没有空闲页,分配新页 */
    page = pool_alloc_page(pool, mem_flags);
    if (!page) {
        spin_unlock_irqrestore(&pool->lock, flags);
        return NULL;
    }
​
    /* 4. 使用新页的第一个对象 */
    set_bit(0, page->bitmap);
    page->in_use = 1;
    *handle = page->dma;
    retval = page->vaddr;
    list_add(&page->page_list, &pool->page_list);
​
    spin_unlock_irqrestore(&pool->lock, flags);
    return retval;
}
EXPORT_SYMBOL(dma_pool_alloc);

适用场景:网卡描述符环、USB控制传输缓冲区、小尺寸DMA缓冲区。

5.2 dma_buf - DMA缓冲区共享

/**
 * struct dma_buf - 共享DMA缓冲区
 * @brief 在不同设备驱动之间共享DMA缓冲区
 * 
 * @note 源码位置:include/linux/dma-buf.h
 * @see drivers/dma-buf/dma-buf.c
 */
struct dma_buf {
    size_t                      size;           /*!< 缓冲区大小 */
    struct file                 *file;          /*!< 关联文件(用于fd)*/
    struct list_head            attachments;    /*!< 附件列表 */
    const struct dma_buf_ops    *ops;           /*!< 操作函数表 */
    struct mutex                lock;
    unsigned                    vmapping_counter; /*!< vmap引用计数 */
    void                        *vmap_ptr;       /*!< vmap地址 */
    const char                  *exp_name;       /*!< 导出者名称 */
    struct module               *owner;
    void                        *priv;           /*!< 私有数据 */
};
​
/**
 * struct dma_buf_attachment - DMA缓冲区附件
 * @brief 代表一个设备对dma_buf的使用
 */
struct dma_buf_attachment {
    struct dma_buf          *dmabuf;        /*!< 关联的dma_buf */
    struct device           *dev;           /*!< 附加设备 */
    struct list_head        node;           /*!< 附件链表节点 */
    unsigned int            dma_map_count;  /*!< DMA映射计数 */
    enum dma_data_direction dir;            /*!< DMA方向 */
};
​
/**
 * dma_buf_export() - 导出DMA缓冲区
 * @exp_info: 导出信息
 * @return: dma_buf指针
 */
struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info)
{
    struct dma_buf *dmabuf;
    struct file *file;
    int ret;
​
    /* 1. 分配dma_buf结构 */
    dmabuf = kzalloc(sizeof(*dmabuf), GFP_KERNEL);
    if (!dmabuf)
        return ERR_PTR(-ENOMEM);
​
    /* 2. 初始化 */
    dmabuf->ops = exp_info->ops;
    dmabuf->size = exp_info->size;
    dmabuf->exp_name = exp_info->exp_name;
    dmabuf->owner = exp_info->owner;
    dmabuf->priv = exp_info->priv;
    INIT_LIST_HEAD(&dmabuf->attachments);
    mutex_init(&dmabuf->lock);
​
    /* 3. 创建匿名文件(用于fd传递)*/
    file = anon_inode_getfile("dmabuf", &dma_buf_fops, dmabuf,
                              exp_info->flags);
    if (IS_ERR(file)) {
        kfree(dmabuf);
        return ERR_CAST(file);
    }
​
    dmabuf->file = file;
​
    return dmabuf;
}
EXPORT_SYMBOL_GPL(dma_buf_export);
​
/**
 * dma_buf_attach() - 附加设备到dma_buf
 * @dmabuf: dma_buf
 * @dev: 设备
 */
struct dma_buf_attachment *dma_buf_attach(struct dma_buf *dmabuf,
                                           struct device *dev)
{
    struct dma_buf_attachment *attach;
​
    /* 1. 分配附件结构 */
    attach = kzalloc(sizeof(*attach), GFP_KERNEL);
    if (!attach)
        return ERR_PTR(-ENOMEM);
​
    /* 2. 调用驱动attach回调 */
    if (dmabuf->ops->attach) {
        int ret = dmabuf->ops->attach(dmabuf, dev, attach);
        if (ret) {
            kfree(attach);
            return ERR_PTR(ret);
        }
    }
​
    /* 3. 初始化并加入列表 */
    attach->dev = dev;
    attach->dmabuf = dmabuf;
    attach->dma_map_count = 0;
    mutex_lock(&dmabuf->lock);
    list_add(&attach->node, &dmabuf->attachments);
    mutex_unlock(&dmabuf->lock);
​
    return attach;
}
EXPORT_SYMBOL_GPL(dma_buf_attach);

适用场景:图形缓冲区共享(Wayland、DRM)、相机预览、视频编码器输入、跨设备DMA。

5.3 ION - Android内存分配器

/**
 * struct ion_device - ION核心设备
 * @brief Android平台的内存分配器,现已被dmabuf heap取代
 * 
 * @note 源码位置:drivers/staging/android/ion/ion.h
 */
struct ion_device {
    struct miscdevice       dev;            /*!< 杂项设备 */
    struct rb_root          buffers;        /*!< 缓冲区红黑树 */
    struct mutex            buffer_lock;
    struct idr              heap_idr;       /*!< 堆ID管理器 */
    struct list_head        heaps;          /*!< 堆列表 */
    struct rw_semaphore     heap_sem;
};
​
/**
 * struct ion_heap - ION堆
 */
struct ion_heap {
    enum ion_heap_type      type;           /*!< 堆类型 */
    struct ion_device       *dev;           /*!< 所属设备 */
    void                    *priv;          /*!< 私有数据 */
    struct list_head        node;           /*!< 堆链表节点 */
    const char              *name;          /*!< 堆名称 */
};
​
/**
 * struct ion_buffer - ION缓冲区
 */
struct ion_buffer {
    struct kref             ref;            /*!< 引用计数 */
    struct list_head        attachments;    /*!< 附件列表 */
    struct device           *dev;           /*!< 设备 */
    size_t                  size;           /*!< 大小 */
    unsigned int            flags;          /*!< 标志 */
    struct sg_table         *sg_table;      /*!< 散列表 */
    void                    *vaddr;         /*!< 虚拟地址 */
    dma_addr_t              dma_addr;       /*!< DMA地址 */
};
​
/**
 * ion_alloc() - 分配ION缓冲区
 * @client: ION客户端
 * @len: 长度
 * @align: 对齐
 * @heap_id_mask: 堆ID掩码
 * @flags: 标志
 */
struct ion_buffer *ion_alloc(struct ion_client *client, size_t len,
                              unsigned int align, unsigned int heap_id_mask,
                              unsigned int flags)
{
    struct ion_device *dev = client->dev;
    struct ion_heap *heap;
    struct ion_buffer *buffer = NULL;
    int ret;
​
    /* 1. 选择合适的堆 */
    heap = ion_find_heap(dev, heap_id_mask);
    if (!heap)
        return ERR_PTR(-ENODEV);
​
    /* 2. 从堆分配 */
    buffer = heap->ops->allocate(heap, len, align, flags);
    if (IS_ERR(buffer))
        return buffer;
​
    /* 3. 初始化缓冲区 */
    buffer->dev = dev;
    buffer->size = len;
    buffer->flags = flags;
    INIT_LIST_HEAD(&buffer->attachments);
    kref_init(&buffer->ref);
​
    return buffer;
}

适用场景:Android图形栈、Camera HAL、Codec(现已被dmabuf heap取代)。


第六部分:高性能计算/加速器专用缓存

6.1 dmabuf_heap - DMA-BUF堆

/**
 * struct dma_heap - DMA-BUF堆
 * @brief dmabuf heaps是ION的继任者
 * 
 * @note 源码位置:drivers/dma-buf/dma-heap.c
 */
struct dma_heap {
    const char              *name;          /*!< 堆名称 */
    const struct dma_heap_ops *ops;         /*!< 操作函数 */
    void                    *priv;          /*!< 私有数据 */
    struct device           *dev;           /*!< 设备 */
    struct list_head        list;           /*!< 堆链表 */
    int                     heap_devt;      /*!< 设备号 */
    struct cdev             heap_cdev;      /*!< 字符设备 */
};
​
/**
 * struct dma_heap_ops - 堆操作函数
 */
struct dma_heap_ops {
    /**
     * @brief 分配缓冲区
     * @param heap 堆
     * @param len 长度
     * @param fd_flags 文件描述符标志
     * @param heap_flags 堆标志
     * @return dma_buf指针
     */
    struct dma_buf *(*allocate)(struct dma_heap *heap,
                                 unsigned long len,
                                 unsigned long fd_flags,
                                 unsigned long heap_flags);
};
​
/**
 * struct system_heap_buffer - 系统堆缓冲区
 */
struct system_heap_buffer {
    struct dma_heap         *heap;          /*!< 所属堆 */
    struct list_head        attachments;    /*!< 附件列表 */
    struct mutex            lock;
    struct sg_table         sg_table;       /*!< 散列表 */
    int                     vmap_cnt;       /*!< vmap计数 */
    void                    *vaddr;         /*!< 虚拟地址 */
    unsigned long           len;            /*!< 长度 */
};
​
/**
 * dma_heap_buffer_alloc() - 系统堆分配实现
 */
static struct dma_buf *system_heap_allocate(struct dma_heap *heap,
                                             unsigned long len,
                                             unsigned long fd_flags,
                                             unsigned long heap_flags)
{
    struct system_heap_buffer *buffer;
    struct sg_table *sg_table;
    struct page **pages;
    int nr_pages = PAGE_ALIGN(len) / PAGE_SIZE;
    int i, ret;
​
    /* 1. 分配缓冲区结构 */
    buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
    if (!buffer)
        return ERR_PTR(-ENOMEM);
​
    buffer->heap = heap;
    buffer->len = len;
    mutex_init(&buffer->lock);
    INIT_LIST_HEAD(&buffer->attachments);
​
    /* 2. 分配页面数组 */
    pages = kmalloc_array(nr_pages, sizeof(*pages), GFP_KERNEL);
    if (!pages) {
        kfree(buffer);
        return ERR_PTR(-ENOMEM);
    }
​
    /* 3. 分配物理页 */
    for (i = 0; i < nr_pages; i++) {
        pages[i] = alloc_page(GFP_KERNEL | __GFP_ZERO);
        if (!pages[i]) {
            while (--i >= 0)
                __free_page(pages[i]);
            kfree(pages);
            kfree(buffer);
            return ERR_PTR(-ENOMEM);
        }
    }
​
    /* 4. 创建散列表 */
    sg_table = &buffer->sg_table;
    ret = sg_alloc_table_from_pages(sg_table, pages, nr_pages,
                                     0, len, GFP_KERNEL);
    if (ret) {
        for (i = 0; i < nr_pages; i++)
            __free_page(pages[i]);
        kfree(pages);
        kfree(buffer);
        return ERR_PTR(ret);
    }
​
    kfree(pages);
​
    /* 5. 导出dma_buf */
    return dma_buf_export(buffer, &system_heap_buf_ops, len, fd_flags);
}

适用场景:现代Android图形栈、多媒体框架、需要跨设备共享大块内存的场景。

6.2 vfio_dma - VFIO DMA区域

/**
 * struct vfio_dma - VFIO DMA映射区域
 * @brief 管理分配给用户的DMA区域
 * 
 * @note 源码位置:drivers/vfio/vfio_iommu_type1.c
 */
struct vfio_dma {
    struct rb_node          node;           /*!< 红黑树节点 */
    dma_addr_t              iova;           /*!< I/O虚拟地址 */
    unsigned long           vaddr;          /*!< 用户虚拟地址 */
    size_t                  size;           /*!< 大小 */
    int                     prot;           /*!< 保护标志 */
    bool                    iommu_mapped;   /*!< 是否已映射到IOMMU */
    struct list_head        vaddr_list;     /*!< 虚拟地址列表 */
    unsigned int            vaddr_max;      /*!< 最大虚拟地址数 */
    struct task_struct      *task;          /*!< 所属任务 */
    struct mm_struct        *mm;            /*!< 内存描述符 */
};
​
/**
 * vfio_dma_alloc() - 分配VFIO DMA区域
 */
static struct vfio_dma *vfio_dma_alloc(struct rb_root *root,
                                         dma_addr_t iova, size_t size)
{
    struct vfio_dma *dma;
​
    /* 1. 分配结构 */
    dma = kzalloc(sizeof(*dma), GFP_KERNEL);
    if (!dma)
        return NULL;
​
    /* 2. 初始化 */
    dma->iova = iova;
    dma->size = size;
    dma->vaddr_max = 1;
    INIT_LIST_HEAD(&dma->vaddr_list);
​
    /* 3. 插入红黑树 */
    vfio_dma_insert(root, dma);
​
    return dma;
}

适用场景:PCIe设备直通、虚拟机设备分配、高性能计算。


第七部分:内核内部专用缓存

7.1 mempool - 内存池(紧急备用)

/**
 * struct mempool_s - 内存池
 * @brief 提供紧急备用内存,防止GFP_ATOMIC分配失败
 * 
 * @note 源码位置:include/linux/mempool.h
 * @see mm/mempool.c
 */
struct mempool_s {
    spinlock_t              lock;           /*!< 保护空闲列表 */
    int                     min_nr;         /*!< 最小元素数量 */
    int                     curr_nr;        /*!< 当前元素数量 */
    void                    **elements;     /*!< 元素数组 */
    void                    *pool_data;     /*!< 池私有数据 */
    mempool_alloc_t         *alloc;         /*!< 分配函数 */
    mempool_free_t          *free;          /*!< 释放函数 */
    wait_queue_head_t       wait;           /*!< 等待队列 */
};
​
/**
 * mempool_alloc() - 从内存池分配
 * @pool: 内存池
 * @gfp_mask: GFP标志
 */
void *mempool_alloc(mempool_t *pool, gfp_t gfp_mask)
{
    void *element;
    unsigned long flags;
​
    /* 1. 尝试从池中获取 */
    spin_lock_irqsave(&pool->lock, flags);
    if (likely(pool->curr_nr)) {
        element = pool->elements[--pool->curr_nr];
        spin_unlock_irqrestore(&pool->lock, flags);
        return element;
    }
    spin_unlock_irqrestore(&pool->lock, flags);
​
    /* 2. 池为空,尝试底层分配 */
    element = pool->alloc(gfp_mask, pool->pool_data);
    if (likely(element))
        return element;
​
    /* 3. 底层分配失败,但这是紧急情况 */
    spin_lock_irqsave(&pool->lock, flags);
    if (likely(pool->curr_nr)) {
        element = pool->elements[--pool->curr_nr];
        spin_unlock_irqrestore(&pool->lock, flags);
        return element;
    }
    spin_unlock_irqrestore(&pool->lock, flags);
​
    /* 4. 如果允许等待,则阻塞 */
    if (!(gfp_mask & __GFP_WAIT))
        return NULL;
​
    /* 等待其他任务释放 */
    wait_event(pool->wait, !pool->curr_nr);
​
    goto retry;
}
EXPORT_SYMBOL(mempool_alloc);

适用场景:块I/O层(bio池)、中断处理程序、任何不允许失败的关键路径。

7.2 kfifo - 无锁环形队列

/**
 * struct kfifo - 无锁FIFO环形缓冲区
 * @brief 多生产者/消费者安全的无锁环形队列
 * 
 * @note 源码位置:include/linux/kfifo.h
 * @see kernel/kfifo.c
 */
struct kfifo {
    unsigned char   *buffer;        /*!< 缓冲区 */
    unsigned int    size;           /*!< 缓冲区大小(2的幂)*/
    unsigned int    in;             /*!< 写指针 */
    unsigned int    out;            /*!< 读指针 */
};
​
/**
 * kfifo_init() - 初始化kfifo
 * @fifo: kfifo结构
 * @buffer: 缓冲区
 * @size: 缓冲区大小(必须为2的幂)
 */
void kfifo_init(struct kfifo *fifo, void *buffer, unsigned int size)
{
    /* size必须是2的幂 */
    if (!is_power_of_2(size))
        size = rounddown_pow_of_two(size);
​
    fifo->buffer = buffer;
    fifo->size = size;
    fifo->in = 0;
    fifo->out = 0;
}
EXPORT_SYMBOL(kfifo_init);
​
/**
 * kfifo_in() - 向kfifo写入数据(无锁版本)
 * @fifo: kfifo
 * @from: 源数据
 * @len: 长度
 * @return: 实际写入字节数
 */
unsigned int kfifo_in(struct kfifo *fifo, const void *from,
                       unsigned int len)
{
    unsigned long flags;
    unsigned int ret;
​
    spin_lock_irqsave(&fifo->lock, flags);
    ret = kfifo_in_locked(fifo, from, len, &fifo->lock);
    spin_unlock_irqrestore(&fifo->lock, flags);
​
    return ret;
}
EXPORT_SYMBOL(kfifo_in);
​
/**
 * __kfifo_put() - 无锁放入(单生产者/消费者安全)
 */
static inline unsigned int __kfifo_put(struct kfifo *fifo,
                                        const unsigned char *buffer,
                                        unsigned int len)
{
    unsigned int l;
​
    len = min(len, fifo->size - fifo->in + fifo->out);
​
    /* 第一段:直到末尾 */
    l = min(len, fifo->size - (fifo->in & (fifo->size - 1)));
    memcpy(fifo->buffer + (fifo->in & (fifo->size - 1)), buffer, l);
​
    /* 第二段:剩余部分从开头开始 */
    memcpy(fifo->buffer, buffer + l, len - l);
​
    fifo->in += len;
​
    return len;
}

适用场景:中断与进程通信、日志缓冲区、串口驱动、任何需要无锁队列的场景。

7.3 relayfs - 中继文件系统

/**
 * struct rchan_buf - 中继通道缓冲区
 * @brief relayfs的每个CPU缓冲区
 * 
 * @note 源码位置:kernel/relay.c
 */
struct rchan_buf {
    void                    *start;         /*!< 缓冲区起始 */
    void                    *data;          /*!< 当前写指针 */
    size_t                   offset;        /*!< 当前偏移 */
    size_t                   subbuf_size;   /*!< 子缓冲区大小 */
    size_t                   n_subbufs;     /*!< 子缓冲区数量 */
    size_t                   bytes_consumed;/*!< 已消费字节数 */
    struct rchan             *chan;         /*!< 所属通道 */
    wait_queue_head_t        read_wait;     /*!< 读等待队列 */
    struct dentry            *dentry;       /*!< debugfs dentry */
};
​
/**
 * struct rchan - 中继通道
 */
struct rchan {
    struct kref              kref;          /*!< 引用计数 */
    struct rchan_callbacks   *cb;           /*!< 回调函数 */
    struct dentry            *parent;       /*!< 父目录 */
    struct rchan_buf         *buf[NR_CPUS]; /*!< per-CPU缓冲区 */
    size_t                   subbuf_size;   /*!< 子缓冲区大小 */
    size_t                   n_subbufs;     /*!< 子缓冲区数量 */
    int                      is_global;     /*!< 是否全局通道 */
};
​
/**
 * relay_open() - 打开中继通道
 */
struct rchan *relay_open(const char *base_filename,
                         struct dentry *parent,
                         size_t subbuf_size,
                         size_t n_subbufs,
                         struct rchan_callbacks *cb,
                         void *private_data)
{
    struct rchan *chan;
    int i;
​
    /* 1. 分配通道结构 */
    chan = kzalloc(sizeof(struct rchan), GFP_KERNEL);
    if (!chan)
        return NULL;
​
    chan->cb = cb;
    chan->subbuf_size = subbuf_size;
    chan->n_subbufs = n_subbufs;
    kref_init(&chan->kref);
​
    /* 2. 为每个CPU创建缓冲区 */
    for_each_possible_cpu(i) {
        chan->buf[i] = relay_create_buf(chan);
        if (!chan->buf[i])
            goto free_bufs;
    }
​
    return chan;
​
free_bufs:
    relay_destroy_channel(chan);
    return NULL;
}
EXPORT_SYMBOL_GPL(relay_open);

适用场景:内核追踪(trace)、性能监控、调试数据导出。


第八部分:缓存管理技术对比总结

8.1 技术对比表

技术设计模式数据组织内存类型共享机制典型场景
sk_buff组合+享元树形(分片)kmalloc+page克隆/引用网络数据包
videobuf2桥接+策略队列+平面DMA-SG/contig缓冲区池视频帧
buffer_head简单对象线性kmalloc引用计数传统块I/O
bio向量化向量数组page共享现代块I/O
page cache缓存XArray树page共享映射文件缓存
dma_pool内存池固定大小块DMA coherent池分配小DMA缓冲区
dma_buf门面不透明任意fd共享跨设备共享
mempool对象池数组任意紧急备用关键路径
kfifo环形缓冲区环形数组kmalloc无锁中断通信
relayfsper-CPU环形vmalloc文件导出内核追踪

8.2 设计模式分布

设计模式在缓存管理中的分布:
┌─────────────────────────────────────────────────────────────┐
│ 组合模式    │ sk_buff (分片树)                               │
├─────────────┼───────────────────────────────────────────────┤
│ 桥接模式    │ videobuf2 (mem_ops抽象), dma_buf (ops抽象)    │
├─────────────┼───────────────────────────────────────────────┤
│ 策略模式    │ videobuf2_ops, dma_buf_ops, address_space_ops│
├─────────────┼───────────────────────────────────────────────┤
│ 享元模式    │ sk_buff克隆, page cache共享                   │
├─────────────┼───────────────────────────────────────────────┤
│ 工厂模式    │ kmem_cache_create, dma_pool_create            │
├─────────────┼───────────────────────────────────────────────┤
│ 门面模式    │ dma_buf (统一接口屏蔽底层)                     │
├─────────────┼───────────────────────────────────────────────┤
│ 观察者模式  │ buffer_head回调, bio_end_io                   │
├─────────────┼───────────────────────────────────────────────┤
│ 迭代器模式  │ bio_vec迭代, kfifo读写                         │
└─────────────┴───────────────────────────────────────────────┘

8.3 选择指南

根据场景选择缓存技术:
┌─────────────────────────────────────────────────────────────┐
│ 需要处理网络数据包?        → sk_buff                       │
│ 需要处理视频帧?           → videobuf2                      │
│ 需要读写块设备?           → bio (+ buffer_head兼容)        │
│ 需要文件数据缓存?         → page cache                     │
│ 需要小尺寸DMA缓冲区?      → dma_pool                       │
│ 需要跨设备共享内存?       → dma_buf + dma_heap             │
│ 需要紧急备用内存?         → mempool                        │
│ 需要无锁队列?            → kfifo                          │
│ 需要内核态到用户态流数据?  → relayfs                       │
│ 需要NUMA感知的对象缓存?    → kmem_cache (SLUB)             │
│ 需要物理连续大块内存?      → CMA                           │
└─────────────────────────────────────────────────────────────┘

总结:Linux缓存管理的设计哲学

1. 分层抽象

每一层都提供清晰的接口,隐藏底层实现细节,使得上层可以专注于业务逻辑。

2. 专一化优化

不同场景使用不同的缓存技术,没有银弹。网络、存储、DMA各有其优化的专用结构。

3. 零拷贝优先

通过共享内存、引用计数、DMA-BUF等技术,最大限度减少数据拷贝,提升性能。

4. 紧急备用

通过mempool等机制,确保关键路径即使在内存紧张时也能分配成功。

5. 可观测性

通过relayfs、tracepoint等机制,让内核缓存的行为对开发者透明。


参考资料:

  1. Linux内核源码:include/linux/ 和 mm/、fs/、drivers/

  2. Linux内核文档:Documentation/core-api/

  3. Linux内核邮件列表:关于各缓存技术的讨论

  4. 《Linux内核设计与实现》(第3版)- Robert Love

  5. 《Understanding the Linux Kernel》- Daniel P. Bovet

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐