linux__framebuffer__认识

来源:互联网 发布:gltools王者荣耀优化 编辑:程序博客网 时间:2024/06/05 08:15

转自:http://www.linuxsir.org/bbs/thread241071.html
对FrameBuffer的一夜hack。。。

大家都知道Unix/Linux系统是由命令驱动的。那么最基本的系统是命令行的(就是想DOS一样的界面)。X-Window-System是Unix/Linux上的图形系统,它是通过X-Server来控制硬件的。但有一些Linux的发行版在引导的时候就会在屏幕上出现图形,这时的图形是不可能由X来完成的,那是什么机制呢?答案是FrameBuffer。
FrameBuffer不是一个图形系统,更不是窗口系统。它比X要低级,简单来说FrameBuffer就是一种机制的实现。这种机制是把屏幕上的每个点映射成一段线性内存空间,程序可以简单的改变这段内存的值来改变屏幕上某一点的颜色。X的高度可移植性就是来自于这种机制,不管是在那种图形环境下,只要有这种机制的实现就可以运行X。所以在几乎所有的平台上都有相应的X版本的移植。
好了,闲话少说,下面我们来看看可以利用FrameBuffer来干点什么。首先看看你是否有了相应的驱动:找一下在/dev/下是否有fb*这个设备文件,这是个字符类的特殊文件。

Shell代码 
  1. ls -l /dev/fb0  (Enter)                    
  2. rw-rw----  1 root video 290 Jan 27 15:32 /dev/fb0  

如果没有这个文件也可以找找其他的比如:/dev/fb1,/dev/fb2...如果找不到这些文件,那就得重新编译内核了。下面假设存在这个文件/dev/fb0,这就是FrameBuffer的设备文件。
有了这个我们可以play with FrameBuffer了。(一下的操作不一定要在X下,可以在启动了FrameBuffer的虚拟控制台下)
Shell代码 
  1. cat /dev/fb0 > sreensnap   
  2.   
  3. ls -l sreensnap  
  4.   
  5. -rw-r--r--  1 wsw wsw 6291456 Jan 27 21:30 sreensnap  

我们得到了一个恰好6M的文件,再做下面的操作:
Shell代码 
  1. clear                   /*清楚屏幕的输出*/  
  2. cat sreensnap > /dev/fb0  

是不是奇怪的事情发生了?好像是中了病毒一般?屏幕又恢复了以前的状态?不用着急,
Shell代码 
  1. clear  

这样屏幕就正常了。

通过以上的操作,我想你也猜到了。文件/dev/fb0就是控制屏幕上的每一点的颜色的文件。我们可以写程序来改变这个文件的内容,就可以方便的在屏幕上画图了:-)

我下面就来写一个小程序,探测一下屏幕的属性。

Shell代码 
  1. #include <unistd.h>  
  2. #include <stdio.h>  
  3. #include <fcntl.h>  
  4. #include <linux/fb.h>  
  5. #include <sys/mman.h>  
  6.   
  7. int main () {  
  8.     int fp=0;  
  9.     struct fb_var_screeninfo vinfo;  
  10.     struct fb_fix_screeninfo finfo;  
  11.     fp = open ("/dev/fb0",O_RDWR);  
  12.   
  13.     if (fp < 0){  
  14.         printf("Error : Can not open framebuffer device\n");  
  15.         exit(1);  
  16.     }  
  17.   
  18.     if (ioctl(fp,FBIOGET_FSCREENINFO,&finfo)){  
  19.         printf("Error reading fixed information\n");  
  20.         exit(2);  
  21.     }  
  22.       
  23.     if (ioctl(fp,FBIOGET_VSCREENINFO,&vinfo)){  
  24.         printf("Error reading variable information\n");  
  25.         exit(3);  
  26.     }  
  27.   
  28.     printf("The mem is :%d\n",finfo.smem_len);  
  29.     printf("The line_length is :%d\n",finfo.line_length);  
  30.     printf("The xres is :%d\n",vinfo.xres);  
  31.     printf("The yres is :%d\n",vinfo.yres);  
  32.     printf("bits_per_pixel is :%d\n",vinfo.bits_per_pixel);  
  33.     close (fp);  
  34. }  

struct fb_var_screeninfo 和 struct fb_fix_screeninfo 两个数据结构是在/usr/include/linux/fb.h中定义的,里面有些有趣的值:(都是无符号32位的整数)
在fb_fix_screeninfo中有
__u32 smem_len 是这个/dev/fb0的大小,也就是内存大小。
__u32 line_length 是屏幕上一行的点在内存中占有的空间,不是一行上的点数。
在fb_var_screeninfo 中有
__u32 xres ,__u32 yres 是x和y方向的分辨率,就是两个方向上的点数。
__u32 bits_per_pixel 是每一点占有的内存空间。

把上面的程序编译以后运行,在我的机器上的结果如下:
Shell代码 
  1. The mem is :6291456  
  2. The line_length is :4096  
  3. The xres is :1024  
  4. The yres is :768  
  5. bits_per_pixel is :32  

内存长度恰好是6M,每行占有4M的空间,分辨率是1024x768,色彩深度是32位。细心的你可能已经发现有些不对。屏幕上的点有1024x768=786432个,每个点占有32比特。屏幕一共的占有内存数为32x786432=25165824 就是3145728字节,恰好是3M但是上面的程序告诉我们有6M的存储空间。这是因为在现代的图形系统中大多有缓冲技术,显存中存有两页屏幕数据,这是方便快速的改变屏幕内容实现动画之类比较高的要求。关于这种缓冲技术有点复杂,我们目前先不讨论。对于我们来说只有这3M内存来存放这一个屏幕的颜色数据。
好了,现在你应该对FrameBuffer有一个大概的了解了吧。那么接下来你一定会想在屏幕上画一些东西,让我们先从画一个点开始吧。先说说我的想法:在类Unix系统中,一切东西都是文件。我们对屏幕的读写就可以转换成对/dev/fb0的读写。那么就把/dev/fb0用open打开,再用lseek定位要读写的位置,最后调用read或者write来操作。通过这么一大段的操作我们才完成了对一个点的读或者写。这种方法开销太大了。还有一种方法,我们把/dev/fb0映射到程序进程的内存空间中来,然后得到一个指向这段存储空间的指针,这样就可以方便的读写了。但是我们要知道能映射多少和该映射多少,这能很方便的从上面一个程序得出的参数来决定。
下面是程序代码:
Shell代码 
  1. #include <unistd.h>  
  2. #include <stdio.h>  
  3. #include <fcntl.h>  
  4. #include <linux/fb.h>  
  5. #include <sys/mman.h>  
  6.   
  7. int main () {  
  8.     int fp=0;  
  9.     struct fb_var_screeninfo vinfo;  
  10.     struct fb_fix_screeninfo finfo;  
  11.     long screensize=0;  
  12.     char *fbp = 0;  
  13.     int x = 0, y = 0;  
  14.     long location = 0;  
  15.     fp = open ("/dev/fb0",O_RDWR);  
  16.   
  17.     if (fp < 0){  
  18.         printf("Error : Can not open framebuffer device\n");  
  19.         exit(1);  
  20.     }  
  21.   
  22.     if (ioctl(fp,FBIOGET_FSCREENINFO,&finfo)){  
  23.         printf("Error reading fixed information\n");  
  24.         exit(2);  
  25.     }  
  26.       
  27.     if (ioctl(fp,FBIOGET_VSCREENINFO,&vinfo)){  
  28.         printf("Error reading variable information\n");  
  29.         exit(3);  
  30.     }  
  31.   
  32.         screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;  
  33.     /*这就是把fp所指的文件中从开始到screensize大小的内容给映射出来,得到一个指向这块空间的指针*/  
  34.     fbp =(char *) mmap (0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fp,0);  
  35.     if ((int) fbp == -1)  
  36.         {  
  37.             printf ("Error: failed to map framebuffer device to memory.\n");  
  38.             exit (4);  
  39.         }  
  40. /*这是你想画的点的位置坐标,(00)点在屏幕左上角*/  
  41.      x = 100;  
  42.      y = 100;  
  43.     location = x * (vinfo.bits_per_pixel / 8) + y  *  finfo.line_length;  
  44.   
  45.   *(fbp + location) = 100;      /* 蓝色的色深 */     /*直接赋值来改变屏幕上某点的颜色*/  
  46.   *(fbp + location + 1) = 15;   /* 绿色的色深*/         
  47.   *(fbp + location + 2) = 200;  /* 红色的色深*/         
  48.   *(fbp + location + 3) = 0;        /* 是否透明*/         
  49.   munmap (fbp, screensize); /*解除映射*/  
  50.   close (fp);               /*关闭文件*/  
  51.   return 0;  
  52.   
  53. }  

因为这是对线性存储空间的读写,所以代码有点不清晰,不易理解。但是有了这个基本的代码实现,我们可以很容易写一些DrawPoint之类的函数去包装一下低层的对线性存储空间的读写。但是有了画点的程序,再写出画线画圆的函数就不是非常困难了。

这些就是我对FrameBuffer的初步研究,匆忙之间写些东西不成文章,以后要写些更高级一点的函数的实现。

sno: 注:qt的嵌入式版qte使用的是framebuffer 也就是使用的是/dev/fb0来读写的。


===========================================另一篇:

zz:http://blog.csdn.net/shenbin1430/article/details/4294399

FrameBuffer 是出现在 2.2.xx 内核当中的一种驱动程序接口。Linux 工作在保护模式下,所以用户态进程是无法象 DOS 那样使用显卡 BIOS 里提供的中断调用来实现直接写屏,Linux 抽象出 FrameBuffer 这个设备来供用户态进程实现直接写屏。Framebuffer 机制模仿显卡的功能,将显卡硬件结构抽象掉,可以通过 Framebuffer 的读写直接对显存进行操作。用户可以将 framebuffer 看成是显示内存的一个映像,将其映射到进程地址空间之后,就可以直接进行读写操作,而写操作可以立即反应在屏幕上。这种操作是抽象的,统一的。用户不必关 心物理显存的位置、换页机制等等具体细节。这些都是由 framebuffer 设备驱动来完成的。

framebuffer 本身不具备任何运算数据的能力,就只好比是一个暂时存放水的水池.CPU 将运算后的结果放到这个水池,水池再将结果流到显示器.中间不会对数据做处理. 应用程序也可以直接读写这个水池的内容.在这种机制下,尽管 framebuffer 需要真正的显卡驱动的支持,但所有显示任务都有 CPU 完成,因此 CPU 负担很重.

帧缓冲驱动应用广泛,在 linux 的桌面系统中,X window 服务器就是利用帧缓 冲进行窗口的绘制。尤其是通过帧缓冲可显示汉字点阵,成为Linux 汉化的唯一可 行方案。

在开发者看来,FrameBuffer 本质上是一块显示缓存,往显示缓存中写入特定格 式的数据就意味着向屏幕输出内容。所以说 FrameBuffer 就是一块白板。例如 对于初始化为 16 位色的 FrameBuffer 来说, FrameBuffer 中的两个字节代表屏幕上一个点,从上到下,从左至右,屏幕位置与内存地址是 顺序的线性关系。

帧缓存可以在系统存储器(内存)的任意位置,视频控制器通过访问帧缓存来刷新 屏幕。 帧缓存也叫刷新缓存 Frame buffer 或 refresh buffer, 这里的帧(frame)是指整个屏幕范围。

帧缓存有个地址,是在内存里。我们通过不停的向 frame buffer 中写入数据, 显示控制器就自动的从 frame buffer 中取数据并显示出来。全部的图形都共享内 存中同一个帧缓存。

CPU 指定显示控制器工作,则显示控制器根据CPU的控制到指定的地方去取数据 和指令, 目前的数据一般是从显存里取, 如果显存里存不下,则从内存里取, 内存也放不下,则从硬盘里取,当然也不是内存放不下,而是为了节省内存的话, 可以放在硬盘里,然后通过 指令控制显示控制器去取。帧缓存 Frame Buffer 里面存储的东西是一帧一帧的, 显卡会不停的刷新 Frame Buffer, 这每一帧如 果不捕获的话, 则会被丢弃,也就是说是实时的。这每一帧不管是保存在内存 还是显存里, 都是一个显性的信息, 这每一帧假设是 800x600 的分辨率, 则保 存的是 800x600 个像素点,和颜色值。

如何启用 framebuffer

首先确认内核是否支持 framebuffer ,查看 /proc/fb 文件是否存在,存在则 说明支持,否则,说明不支持。其次查看 framebuffer 设备是否已激活,若 /dev/fbx 文件存在,则说明已经 激活;否则说明没有激活。

在系统启动时可通过向 kernel 传送 vga=mode-number 的参数来激活 FrameBuffer 设备,如 vga=0x314,将会启动 800*600*16bpp 模式 要 linux 缺省激活 framebuffer 设备,需要将/etc/grub.conf改成如下形式:

# grub.conf generated by anaconda## Note that you do not have to rerun grub after making changes to this file# NOTICE:   You do not have a /boot partition.   This means that#           all kernel and initrd paths are relative to /, eg.#           root (hd0,0)#           kernel /boot/vmlinuz-version ro root=/dev/sda1#           initrd /boot/initrd-version.img#boot=/dev/sdadefault=0timeout=10splashimage=(hd0,0)/boot/grub/splash.xpm.gztitle Red Hat Linux (2.4.18-14)         root (hd0,0)        kernel /boot/vmlinuz-2.4.18-14 ro root=LABEL=/ hdc=ide-scsi vga=0x314         initrd /boot/initrd-2.4.18-14.img

0x314 表示 800*600*16bpp,其它取值见下表:

color640x400640x480800x6001024x7681280x10241600x12004bits??0x302???8bits0x3000x3010x3030x3050x3070x31C15bits?0x3100x3130x3160x3190x31D16bits?0x3110x3140x3170x31A0x31E24bits?0x3120x3150x3180x31B0x31F32bits??????

framebuffer 设备启用后,在重启系统时屏幕左上方会显示一个小企鹅。

如何编程操作 framebuffer

帧缓冲设备对应的设备文件为/dev/fb*,如果系统有多个显示卡,Linux 下还可 支持多个帧缓冲设备,最多可达 32 个,分别为 /dev/fb0 到 /dev/fb31 ,而 /dev/fb 则为当前缺省的帧缓冲设备,通常指向 /dev/fb0 。当然在嵌入式系统 中支持一个显示设备就够了。帧缓冲设备为标准字符设备,主设备号为 29,次 设备号则从 0 到 31 ,分别对应 /dev/fb0 至 /dev/fb31 。通过 /dev/fb ,应用程序的操作主要有这几种:

  1. 读/写(read/write)/dev/fb 文件:相当于读/写屏幕缓冲区。通过 seek 接口定位读写位置,通过 read/write 接口读写具体数据;
  2. 映射(map)操作:由于 Linux 工作在保护模式,每个应用程序都有自己的 虚拟地址空间,在应用程序中是不能直接访问物理缓冲区地址的。为此, Linux 在文件操作 file_operations 结构中提供了 mmap 函数,可将文件的内容映射到用户空间。对于帧缓冲设备,则可通过映射操作,可将屏幕缓冲区的物理地址映射到用户空间的一段虚拟地址中,之后用户就可以通过 读写这段虚拟地址访问屏幕缓冲区,在屏幕上绘图了。实际上,使用帧缓冲设备的应用程序都是通过映射操作来显示图形的。由于映射操作都是由内核来完成,下面 我们将看到,帧缓冲驱动留给开发人员的工作并不多。
  3. I/O控制:对于帧缓冲设备,对设备文件的ioctl操作可读取/设置显示设备及屏幕的参数,如分辨率,显示颜色数,屏幕大小等等。ioctl的操作是由底层的驱动程序来完成的。

在应用程序中,一般通过将 framebuffer 设备映射到进程地址空间的方式使用, 比如下面的程序就打开 /dev/fb0 设备,并通过 mmap 系统调用进行地址映射, 随后用 memset 将屏幕清空(这里假设显示模式是 1024x768-8 位色模式,线性 内存模式):

int fb;unsigned char* fb_mem;fb = open ("/dev/fb0", O_RDWR);fb_mem = mmap (NULL, 1024*768, PROT_READ|PROT_WRITE,MAP_SHARED,fb,0);memset (fb_mem, 0, 1024*768);

framebuffer 设备还提供了若干 ioctl 命令,通过这些命令,可以获得显示设 备的一些固定信息(比如显示内存大小)、与显示模式相关的可变信息(比如分 辨率、象素结构、每扫描线的字节宽度),以及伪彩色模式下的调色板信息等等。

通过 framebuffer 设备,还可以获得当前内核所支持的加速显示卡的类型(通 过固定信息得到),这种类型通常是和特定显示芯片相关的。比如目前最新的内 核(2.4.9)中,就包含有对 S3、Matrox、nVidia、3Dfx 等等流行显示芯片的 加速支持。在获得了加速芯片类型之后,应用程序就可以将 PCI 设备的内存I/O (memio)映射到进程的地址空间。这些 memio 一般是用来控制显示卡的寄存器, 通过对这些寄存器的操作,应用程序就可以控制特定显卡的加速功能。

PCI 设备可以将自己的控制寄存器映射到物理内存空间,而后,对这些控制寄存 器的访问,给变成了对物理内存的访问。因此,这些寄存器又被称为"memio"。 一旦被映射到物理内存,Linux 的普通进程就可以通过 mmap 将这些内存 I/O 映射到进程地址空间,这样就可以直接访问这些寄存器了。

当然,因为不同的显示芯片具有不同的加速能力,对memio 的使用和定义也各自 不同,这时,就需要针对加速芯片的不同类型来编写实现不同的加速功能。比如 大多数芯片都提供了对矩形填充的硬件加速支持,但不同的芯片实现方式不同, 这时,就需要针对不同的芯片类型编写不同的用来完成填充矩形的函数。

framebuffer 只是一个提供显示内存和显示芯片寄存器从物理内存映射到进程地址空间中的设备。所以,对于应用程序而言,如果希望在 framebuffer 之上进行图形编程,还需要自己动手完成其他许多工作。

工具

cat

/dev/fb 是一个文件,因此我们可以用对其读写。

cat /dev/fb0 > screensnap.txt  /* read current sreen to a file*/cat screensnap.txt > /dev/fb0  /* 将 screensnap.txt 的内容贴到屏幕上 */
dd

可以用如下命令清空屏幕:

dd if=/dev/zero of=/dev/fb

如果显示模式是 1024x768-8 位色,用如下命令清空屏幕:

 dd if=/dev/zero of=/dev/fb0 bs=1024 count=768

用如下命令可以将fb中的内容保存下来和重新写回屏幕:

dd if=/dev/fb of=fbfiledd if=fbfile of=/dev/fb
fbset

fbset是一个可以查看和设置framebuffer的工具。具体使用方法可参看手册。


sno:  android 端的fb0在/dev/graphics/fb0中,而不是/dev/fb0,这点要注意。