1. Framebuffer 基础概念

1.1 什么是 Framebuffer

Framebuffer(帧缓冲)是 Linux 内核提供的一种显示输出抽象层,它将显存映射到用户空间,使应用程序可以直接向屏幕写入像素数据。从概念上讲,Framebuffer 是一块连续的物理内存区域,每个内存单元对应屏幕上的一个像素点。

核心定义:Framebuffer 是内核中用于存储屏幕图像数据的缓冲区,通过 /dev/fb0 设备文件暴露给用户态程序,实现“写内存即画图”的显示模式。

Framebuffer 的关键特性包括:

  • 内存映射:通过 mmap() 系统调用将内核缓冲区映射到用户进程的虚拟地址空间。
  • 像素格式:支持 RGB565、RGB888、ARGB8888 等多种颜色编码格式。
  • 固定参数:分辨率、位深、行跨度等参数通过 struct fb_fix_screeninfo 获取。
  • 可变参数:颜色格式、显示时序等可通过 struct fb_var_screeninfo 调整。
Framebuffer 核心数据结构
结构体 作用 关键字段
fb_fix_screeninfo 固定硬件参数 smem_start(物理地址)、smem_len(缓冲区大小)、line_length(行字节数)
fb_var_screeninfo 可变显示参数 xres/yres(分辨率)、bits_per_pixel(位深)、red/green/blue(颜色通道偏移)
fb_cmap 颜色查找表(伪彩色模式) red/green/blue(16位颜色值数组)、start/len(索引范围)
实战提示:在嵌入式设备中,Framebuffer 的物理内存通常位于连续 DMA 区域,smem_start 字段可用于硬件图层叠加时的物理地址传递。

1.2 Framebuffer 在 Linux 图形栈中的位置

Linux 图形系统从底层到上层分为多个层次,Framebuffer 位于内核空间与用户空间的交界处,是直接操作显示硬件的底层接口。

Linux 图形栈层次结构 用户空间应用程序 Qt / GTK / SDL / 裸Framebuffer应用 / 游戏引擎 图形工具库 / 窗口系统 DirectFB / Wayland / X11 / libdrm 内核 Framebuffer 子系统 fbdev 驱动框架 / fbmem.c / 具体芯片驱动 显示控制器硬件抽象 LCD控制器 / HDMI发送器 / MIPI DSI / LVDS 物理显示硬件 LCD面板 / OLED屏 / 触摸屏 / 背光 ★ Framebuffer 是用户空间与显示硬件之间的桥梁,提供统一的“写内存即显示”接口 无需了解底层显示控制器细节,即可完成屏幕绘制

从图中可以看出,Framebuffer 处于内核空间的核心位置:

  • 向下:屏蔽不同显示控制器的硬件差异,提供统一的 fb_ops 操作接口。
  • 向上:通过 /dev/fb0 字符设备和 ioctl 系统调用,为应用程序提供标准化的显示服务。
注意:在现代 Linux 系统中,DRM(Direct Rendering Manager)逐渐取代传统 fbdev 成为主流显示框架。但在嵌入式领域,Framebuffer 因其简单、轻量、无依赖的特点,仍然是裸机显示和快速原型开发的首选方案。

1.3 Framebuffer 设备文件 (/dev/fb0)

/dev/fb0 是系统默认的 Framebuffer 设备节点,属于字符设备,主设备号为 29,次设备号从 0 开始递增。系统中有多个显示设备时,依次注册为 /dev/fb0、/dev/fb1 等。

设备文件操作流程

  1. 打开设备:int fd = open("/dev/fb0", O_RDWR);
  2. 获取固定信息:ioctl(fd, FBIOGET_FSCREENINFO, &fix);
  3. 获取可变信息:ioctl(fd, FBIOGET_VSCREENINFO, &var);
  4. 内存映射:void *fbp = mmap(NULL, fix.smem_len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
  5. 像素操作:直接通过 fbp 指针读写像素数据。
  6. 释放资源:munmap(fbp, fix.smem_len); close(fd);
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <linux/fb.h>

int main() {
    int fd = open("/dev/fb0", O_RDWR);
    if (fd < 0) {
        perror("open fb0 failed");
        return -1;
    }

    struct fb_fix_screeninfo fix;
    struct fb_var_screeninfo var;

    // 获取固定参数
    ioctl(fd, FBIOGET_FSCREENINFO, &fix);
    // 获取可变参数
    ioctl(fd, FBIOGET_VSCREENINFO, &var);

    printf("分辨率: %d x %d\n", var.xres, var.yres);
    printf("位深: %d bpp\n", var.bits_per_pixel);
    printf("缓冲区大小: %d bytes\n", fix.smem_len);
    printf("行跨度: %d bytes\n", fix.line_length);

    // 映射显存到用户空间
    size_t screensize = fix.smem_len;
    char *fbp = (char *)mmap(NULL, screensize,
                             PROT_READ | PROT_WRITE,
                             MAP_SHARED, fd, 0);
    if (fbp == MAP_FAILED) {
        perror("mmap failed");
        close(fd);
        return -1;
    }

    // 将屏幕上半部分填充为红色 (RGB565: 0xF800)
    for (int y = 0; y < var.yres / 2; y++) {
        for (int x = 0; x < var.xres; x++) {
            unsigned short *pixel = (unsigned short *)(fbp + y * fix.line_length + x * 2);
            *pixel = 0xF800;  // 红色
        }
    }

    munmap(fbp, screensize);
    close(fd);
    return 0;
}
关键点:fix.line_length 可能大于 var.xres * (var.bits_per_pixel / 8),因为硬件可能要求行对齐到特定字节边界。遍历像素时必须使用 line_length 作为行步长。

常用 ioctl 命令

Framebuffer 设备 ioctl 命令一览
命令 功能 参数类型
FBIOGET_FSCREENINFO 获取固定屏幕信息 struct fb_fix_screeninfo *
FBIOGET_VSCREENINFO 获取可变屏幕信息 struct fb_var_screeninfo *
FBIOPUT_VSCREENINFO 设置可变屏幕信息 struct fb_var_screeninfo *
FBIOGETCMAP 获取颜色查找表 struct fb_cmap *
FBIOPUTCMAP 设置颜色查找表 struct fb_cmap *
FBIOBLANK 控制屏幕开关 int(0=开启,1=关闭)

像素格式与内存布局

Framebuffer 支持多种像素格式,常见的有:

  • RGB565(16位):R占5位,G占6位,B占5位,每个像素2字节。
  • RGB888(32位):R/G/B各占8位,A占8位(或忽略),每个像素4字节。
  • CLUT8(8位):每个像素1字节,作为颜色查找表索引,支持256种颜色。
Framebuffer 内存布局(RGB565 模式,分辨率 800×600) 物理内存:smem_start → smem_start + smem_len line_length = 1600 bytes(800像素 × 2字节/像素,可能含对齐填充) 像素0 | 像素1 | 像素2 | ... | 像素799 | 像素0 | 像素1 | ...(共600行) 一个像素(2字节):[ R: bits 15-11 ] [ G: bits 10-5 ] [ B: bits 4-0 ]
内存计算公式:
  • 每像素字节数 = bits_per_pixel / 8
  • 每行字节数 = line_length(由硬件决定,可能大于 xres × 每像素字节数)
  • 像素 (x, y) 的偏移地址 = y × line_length + x × (bits_per_pixel / 8)

1.4 本章小结

Framebuffer 是嵌入式 Linux 显示系统的基础设施,通过 /dev/fb0 设备文件和 mmap 内存映射机制,为应用程序提供了直接操作屏幕像素的能力。理解 Framebuffer 的层次位置、设备操作流程和内存布局,是后续实现图层合成与光标叠加的基石。