1. Framebuffer 基础概念

Framebuffer(帧缓冲)是 Linux 内核为显示子系统提供的一种抽象接口,它将显存抽象为一个可直接通过文件 I/O 和内存映射(mmap)访问的字符设备。应用程序无需关心底层 GPU 或显示控制器的具体实现,只需向 Framebuffer 设备写入像素数据,即可在屏幕上呈现图像。

核心理解:Framebuffer 本质上是一块内存区域,每个像素点对应内存中的若干字节。修改这些字节就是修改屏幕上的显示内容。

1.1 Framebuffer 设备文件

在 Linux 系统中,Framebuffer 设备以字符设备文件的形式暴露给用户空间,通常位于 /dev/fbX 命名空间下:

设备文件 主设备号 次设备号 典型用途
/dev/fb0 29 0 主显示器(LCD / HDMI)
/dev/fb1 29 1 第二显示器 / 扩展屏
/dev/fb2 29 2 第三显示器 / 叠加层

每个 /dev/fbX 设备对应一个独立的显示缓冲区。系统通过 ioctl 系统调用查询和设置设备参数,包括分辨率、像素格式、行跨度等。

关键操作:使用 open("/dev/fb0", O_RDWR) 打开设备,通过 ioctl(fd, FBIOGET_VSCREENINFO, &vinfo) 获取可变屏幕信息,通过 ioctl(fd, FBIOPUT_VSCREENINFO, &vinfo) 设置屏幕参数。
Framebuffer 设备文件与内核驱动关系 Framebuffer 设备文件与内核驱动关系 用户空间 (User Space) 应用程序 → open("/dev/fb0") → mmap → 直接读写像素 内核空间 (Kernel Space) fb_ops → fbmem.c → 设备驱动层 (DRM / legacy fbdev) 硬件层 (Hardware) LCD 控制器 / HDMI 发送器 / 显示内存 (VRAM / SDRAM) 每个 /dev/fbX 对应一个独立的显示缓冲区实例

1.2 显示缓冲区 (Display Buffer)

显示缓冲区是 Framebuffer 设备在内存中映射的一块连续区域,其大小由屏幕分辨率与像素格式共同决定。缓冲区中的每个字节都对应屏幕上的一个像素点或像素分量。

缓冲区大小的计算公式:

buffer_size = 屏幕宽度 × 屏幕高度 × (颜色深度 / 8)
例如:1024 × 768 × 32bpp → 1024 × 768 × 4 = 3,145,728 字节 ≈ 3 MB
注意:实际缓冲区大小可能大于理论值,因为行跨度(stride)可能因硬件对齐要求而大于屏幕宽度。始终通过 FBIOGET_FSCREENINFO 获取 smem_len 字段来确认实际缓冲区大小。
显示缓冲区与屏幕像素映射 显示缓冲区与屏幕像素映射 显示缓冲区 (内存) 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 ... 连续字节序列 ... smem_len = 3,145,728 字节 物理屏幕 (LCD / HDMI) (0,0) (1,0) (2,0) (3,0) (0,1) (1,1) (2,1) (3,1) 1024 × 768 像素 每个像素 4 字节 (32bpp) 缓冲区偏移 = (y × 屏幕宽度 + x) × (bpp / 8)

1.3 像素格式与颜色深度

像素格式决定了每个像素在缓冲区中的编码方式。颜色深度(bits per pixel, bpp)表示每个像素占用的比特数。常见的像素格式包括:

颜色深度 像素格式 每个像素字节数 颜色通道 典型应用
8 bpp 伪彩色 (索引色) 1 调色板索引 低端嵌入式 / 字符界面
16 bpp RGB565 2 R:5 G:6 B:5 低成本 LCD / 无 GPU 设备
24 bpp RGB888 3 R:8 G:8 B:8 标准桌面 / 高清显示
32 bpp ARGB8888 / XRGB8888 4 A:8 R:8 G:8 B:8 现代嵌入式 / 带透明叠加
RGB565 格式详解:每个像素占用 2 字节。高 5 位为红色,中间 6 位为绿色,低 5 位为蓝色。内存布局为小端序:低字节包含 G 的低 3 位和 B 的 5 位,高字节包含 R 的 5 位和 G 的高 3 位。
常见像素格式内存布局对比 常见像素格式内存布局对比 RGB565 (16 bpp) — 2 字节 / 像素 R[4:0] G[5:0] B[4:0] 字节0 (低) → [G2:G0 | B4:B0] 字节1 (高) → [R4:R0 | G5:G3] ARGB8888 (32 bpp) — 4 字节 / 像素 A[7:0] R[7:0] G[7:0] B[7:0] 字节0 → B, 字节1 → G, 字节2 → R, 字节3 → A (小端序) 通过 struct fb_var_screeninfo 的 red/green/blue/transp 字段 获取每个颜色通道的偏移量和位宽

1.4 获取像素格式信息

应用程序通过 ioctl 获取 struct fb_var_screeninfo 结构体,其中包含完整的像素格式描述:

struct fb_var_screeninfo {
    __u32 xres;           // 可见分辨率宽度
    __u32 yres;           // 可见分辨率高度
    __u32 bits_per_pixel; // 颜色深度 (8/16/24/32)
    __u32 xres_virtual;   // 虚拟分辨率宽度
    __u32 yres_virtual;   // 虚拟分辨率高度
    __u32 xoffset;        // 虚拟到可见的偏移
    __u32 yoffset;
    struct fb_bitfield red;   // 红色通道: offset, length
    struct fb_bitfield green; // 绿色通道: offset, length
    struct fb_bitfield blue;  // 蓝色通道: offset, length
    struct fb_bitfield transp;// 透明度通道: offset, length
    __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 colorspace;
    __u32 reserved[4];
};
实用技巧:使用 fbset 命令可以快速查看当前 Framebuffer 的像素格式和分辨率:fbset -i /dev/fb0。输出中会显示 geometry(分辨率与深度)和 rgba(通道位宽与偏移)。

1.5 多屏同显的硬件基础

多屏同显(克隆模式 / Mirror Mode)要求多个显示控制器共享同一份显示缓冲区数据。在硬件层面,这可以通过以下方式实现:

  • 共享缓冲区指针:多个 Framebuffer 设备指向同一块物理内存地址,写入一个设备的数据同时出现在所有屏幕上。
  • DMA 引擎复制:主屏的显示缓冲区通过 DMA 定期复制到从屏缓冲区,适用于硬件不支持共享的场景。
  • 显示控制器叠加:部分 SoC 支持将同一个图层(Layer)输出到多个显示接口,实现零拷贝同显。
多屏同显架构示意 多屏同显架构示意 (共享缓冲区模式) 共享显示缓冲区 物理内存 (mmap 映射区域) 显示控制器 0 /dev/fb0 → LCD 显示控制器 1 /dev/fb1 → HDMI LCD 屏幕 (1024×768) HDMI 屏幕 (1024×768) 应用程序 mmap → 写入像素