2. 帧缓冲核心概念:设备节点、固定信息、可变信息与 mmap 映射

帧缓冲(FrameBuffer)是 Linux 内核为显示子系统提供的最基础、最直接的抽象层。它屏蔽了底层显示控制器的硬件差异,通过统一的文件接口(/dev/fb*)向用户空间暴露显存。理解帧缓冲的核心概念,是掌握后续 DRM 框架和实际驱动开发的基石。

2.1 帧缓冲设备节点:/dev/fb*

在 Linux 系统中,每一个注册成功的帧缓冲设备都会对应一个字符设备节点,主设备号为 29,次设备号从 0 开始递增。用户空间程序通过标准的文件操作(open、close、ioctl、mmap)与帧缓冲驱动交互。

设备节点 主设备号 次设备号 典型用途
/dev/fb0 29 0 主显示输出(如 LCD 屏)
/dev/fb1 29 1 第二显示输出(如 HDMI 扩展)
/dev/fb2 29 2 虚拟终端或测试帧缓冲
开发提示: 在嵌入式开发板上,如果 ls /dev/fb* 没有任何输出,说明帧缓冲驱动未加载或设备树配置错误。此时应检查内核配置是否启用了 CONFIG_FB 以及对应的 LCD 控制器驱动。

2.2 固定信息:struct fb_fix_screeninfo

固定信息描述了帧缓冲设备在硬件层面不可变或极少变化的属性。这些信息通常在驱动初始化时确定,并在设备生命周期内保持不变。用户空间通过 FBIOGET_FSCREENINFO ioctl 命令获取。

struct fb_fix_screeninfo {
    char id[16];            /* 设备标识字符串,如 "s3c-fb" */
    unsigned long smem_start; /* 物理显存起始地址 */
    __u32 smem_len;         /* 物理显存总长度(字节) */
    __u32 type;             /* 显示类型:FB_TYPE_PACKED_PIXELS 等 */
    __u32 type_aux;         /* 辅助类型(通常为 0) */
    __u32 visual;           /* 颜色模式:真彩色、伪彩色、直接彩色等 */
    __u16 xpanstep;         /* 硬件支持的 x 方向 pan 步长 */
    __u16 ypanstep;         /* 硬件支持的 y 方向 pan 步长 */
    __u16 ywrapstep;        /* 硬件支持的 y 方向 wrap 步长 */
    __u32 line_length;      /* 一行像素占用的字节数(包含对齐填充) */
    unsigned long mmio_start;/* 内存映射 IO 起始地址 */
    __u32 mmio_len;         /* 内存映射 IO 长度 */
    __u32 accel;            /* 硬件加速类型(0 表示无加速) */
    __u16 capabilities;     /* 硬件能力标志位 */
    __u16 reserved[2];      /* 保留字段 */
};
关键字段解读:
  • smem_start / smem_len: 这是 mmap 映射的物理基础。驱动必须保证 smem_start 是页对齐的。
  • line_length: 不等于 width * bytes_per_pixel,因为硬件可能要求每行起始地址对齐到 4、8 或 16 字节。
  • visual: 决定了颜色查找表(LUT)的使用方式。真彩色(FB_VISUAL_TRUECOLOR)是当前主流。

2.3 可变信息:struct fb_var_screeninfo

可变信息描述了当前显示模式的可配置参数,包括分辨率、位深、时序等。用户空间可以通过 FBIOGET_VSCREENINFO 读取,通过 FBIOPUT_VSCREENINFO 修改(如果硬件支持)。

struct fb_var_screeninfo {
    __u32 xres;             /* 可见分辨率:宽度(像素) */
    __u32 yres;             /* 可见分辨率:高度(像素) */
    __u32 xres_virtual;     /* 虚拟分辨率:宽度(像素) */
    __u32 yres_virtual;     /* 虚拟分辨率:高度(像素) */
    __u32 xoffset;          /* 虚拟到可见的 x 偏移 */
    __u32 yoffset;          /* 虚拟到可见的 y 偏移 */
    __u32 bits_per_pixel;   /* 每像素位数:16、24、32 等 */
    __u32 grayscale;        /* 灰度标志(非 0 表示灰度) */
    struct fb_bitfield red;   /* 红色通道位域 */
    struct fb_bitfield green; /* 绿色通道位域 */
    struct fb_bitfield blue;  /* 蓝色通道位域 */
    struct fb_bitfield transp;/* 透明度通道位域 */
    __u32 nonstd;           /* 非标准格式标志 */
    __u32 activate;         /* 模式激活方式 */
    __u32 height;           /* 显示器高度(mm) */
    __u32 width;            /* 显示器宽度(mm) */
    __u32 accel_flags;      /* 加速标志(已废弃) */
    __u32 pixclock;         /* 像素时钟(皮秒) */
    __u32 left_margin;      /* 行同步:左边距 */
    __u32 right_margin;     /* 行同步:右边距 */
    __u32 upper_margin;     /* 帧同步:上边距 */
    __u32 lower_margin;    /* 帧同步:下边距 */
    __u32 hsync_len;        /* 行同步脉冲长度 */
    __u32 vsync_len;        /* 帧同步脉冲长度 */
    __u32 sync;             /* 同步极性标志 */
    __u32 vmode;            /* 显示模式:非交错/交错 */
    __u32 rotate;           /* 旋转角度 */
    __u32 reserved[5];      /* 保留字段 */
};
常见陷阱: 设置 var 时,驱动可能不会完全接受你的参数。例如,某些 LCD 控制器只支持 16bpp 和 32bpp,你传入 24bpp 会被向上取整为 32bpp。务必在 FBIOPUT_VSCREENINFO 后再次读取 var 确认实际生效值。

2.4 mmap 映射原理

帧缓冲的核心优势在于允许用户空间直接映射物理显存到进程的虚拟地址空间,从而避免数据拷贝,实现高效的像素渲染。mmap 映射的流程如下:

  1. 打开设备: fd = open("/dev/fb0", O_RDWR);
  2. 获取固定信息: ioctl(fd, FBIOGET_FSCREENINFO, &fix); 得到 smem_len 和 smem_start。
  3. 获取可变信息: ioctl(fd, FBIOGET_VSCREENINFO, &var); 得到当前分辨率与位深。
  4. 执行 mmap: fbp = mmap(NULL, fix.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
  5. 直接操作显存: 通过 fbp 指针按像素格式写入数据。
  6. 解除映射: munmap(fbp, fix.smem_len);
mmap 映射的底层机制:
  • 内核驱动中的 fb_mmap() 回调函数负责将 smem_start 对应的物理页映射到用户虚拟地址空间。
  • MAP_SHARED 标志保证用户空间的写操作能立即被硬件扫描(或通过刷新机制同步)。
  • 映射大小通常为 smem_len,但也可以只映射部分区域(如双缓冲中的后缓冲)。
帧缓冲 mmap 映射架构图 帧缓冲 mmap 映射架构 用户空间 应用程序进程 虚拟地址:0x7f1234560000 通过 fbp 指针直接读写 内核空间 帧缓冲驱动 (fb_ops) fb_mmap() 回调 remap_pfn_range() 物理显存 (VRAM) 物理地址:0x4A000000 映射流程说明 1. open("/dev/fb0") 2. ioctl(FBIOGET_FSCREENINFO) 获取 smem_start / smem_len 3. mmap(fd, smem_len, MAP_SHARED) 4. 内核调用 fb_mmap() 5. remap_pfn_range() 建立页表 6. 返回用户空间虚拟地址 7. 应用程序直接写显存 8. 硬件 LCD 控制器 持续扫描显存刷新屏幕 关键:零拷贝,无系统调用开销 系统调用 物理地址映射

2.5 核心数据结构关系总览

帧缓冲驱动在内核中通过 struct fb_info 管理所有信息,它包含了 fix、var、fbops 以及实际的显存分配。下图展示了这些核心数据结构之间的组织关系:

帧缓冲核心数据结构关系 帧缓冲核心数据结构关系 struct fb_info 帧缓冲核心结构(每个设备一个) fb_fix_screeninfo 固定信息:显存地址、行长 硬件不可变属性 fb_var_screeninfo 可变信息:分辨率、位深 显示模式可配置 fb_ops 操作函数集:open/release ioctl/mmap/fillrect 包含 fix 包含 var 包含 fbops 用户空间通过 ioctl(FBIOGET_FSCREENINFO/FBIOGET_VSCREENINFO) 获取 fix 和 var 信息 通过 mmap 映射显存后,直接操作像素数据,无需经过 fb_ops 的读写函数

2.6 帧缓冲与 DRM 的对比

对比维度 帧缓冲 (FBDEV) DRM (Direct Rendering Manager)
设备节点 /dev/fb0 ~ /dev/fb31 /dev/dri/card0、/dev/dri/controlD64
显存管理 单一固定显存区,mmap 直接映射 GEM 或 TTM 对象管理,支持显存分配、共享、导入导出
多缓冲支持 通过虚拟分辨率 + xoffset/yoffset 实现 原生支持 Flip Chain、Atomic Modeset
硬件加速 有限的 fb_ops 加速(fillrect、copyarea) 完整的 GPU/DPU 加速接口
色彩管理 通过 fb_cmap 设置 LUT GAMMA LUT、CTM、HDR 元数据
适用场景 简单 LCD 显示、无 GPU 的 MCU 类平台 复杂图形系统、桌面、游戏、视频播放
学习路径建议: 虽然 DRM 是当前 Linux 显示的主流框架,但帧缓冲的 fix/var/mmap 概念是理解 DRM 的基础。DRM 中的 drm_framebuffer 和 drm_gem_object 本质上是对帧缓冲概念的扩展和标准化。先吃透本章内容,后续学习 DRM 的 GEM 和 KMS 时会事半功倍。