初次接触 framebuffer 这个概念是在去年的 9 月,那个时候安装 Arch Linux,进入 X 后觉得 console 好丑,quark 一来,在 grub 启动的 menu.lst
上加了一句 vga=792
,瞬间 console 就变成 1024*768 的分辨率了。先是感到惊喜,再叹 quark 的神通,三来意识到自己和别人的差距。关于怎样开启 framebuffer,grub 的配置文件注释里面有很好的说明,这里是我的部分配置 menu.lst
:
# FRAMEBUFFER RESOLUTION SETTINGS
# +-------------------------------------------------+
# | 640x480 800x600 1024x768 1280x1024
# ----+--------------------------------------------
# 256 | 0x301=769 0x303=771 0x305=773 0x307=775
# 32K | 0x310=784 0x313=787 0x316=790 0x319=793
# 64K | 0x311=785 0x314=788 0x317=791 0x31A=794
# 16M | 0x312=786 0x315=789 0x318=792 0x31B=795
# +-------------------------------------------------+
# for more details and different resolutions see
# http://wiki.archlinux.org/index.php/GRUB#Framebuffer_Resolution
# general configuration:
default 0
timeout 3
color light-blue/black light-cyan/blue
# boot sections follow
# each is implicitly numbered from 0 in the order of appearance below
#
# TIP: If you want a 1024x768 framebuffer, add "vga=773" to your kernel line.
#
#-*
# (0) Gentoo Linux
title Gentoo Linux
root (hd0,8)
kernel /vmlinuz-2.6.34-gentoo root=/dev/sda5 rootfstype=ext4 quiet vga=792
initrd /initrd
恰好今天下午做嵌入式系统的实验,LCD 显示缓冲区体验,要求编程序载入一张图片到 LCD 显示缓冲区,我回到实验室琢磨琢磨,看到了 Gentoo 开机启动时的那只小企鹅图片, tux,想到开机的时候没有启动 X,屏幕上显示出了 tux 图片,那么可以肯定的是,这张图片一定是通过某种驱动直接“写到显存”里面。而在 grub 的启动是否采用 framebuffer,则和这只 tux 的“生死”有着直接的联系,因此我敢肯定,framebuffer 肯定是这次实验的突破口之一。说干就干,回到实验室查了很多资料,对 framebuffer 的相关知识做一次小总结。
FrameBuffer 是出现在 2.2.xx 内核当中的一种驱动程序接口。这种接口将显示设备抽象为帧缓冲区。用户可以将它看成是显示内存的一个映像,将其映射到进程地址空间之后,就可以直接进行读写操作,而写操作可以立即反应在屏幕上。该驱动程序的设备文件一般是 /dev/fb0
、 /dev/fb1
等等(具体内容请参见:基于 Linux 和 MiniGUI 的嵌入式系统软件开发指南 )。
Framebuffer 需要内核的支持,如果没有 /dev/fb0
类似的文件,恐怕要重新编译内核了。我们可以做两个简单的小实验,看看 frambuffer 究竟有怎样的魔力:
仔细观察你的屏幕,然后:
是不是有点中病毒的感觉?其实,最简单的理解, /dev/fb0
是屏幕显示区域内存的一个抽象,前面先把这部分“内存”保存到 screenshot 里面,然后再将 screenshot 里面的”内存“拷贝回去,就实现了屏幕的还原。不过,由于我们使用的 GUI 和 CLI 的自动刷新,整个屏幕并不能实现 100% 的“还原”。
一切皆文件,the great Unix Philosophy,通过读写 /dev/fb0
,我们可以得到对显示屏 100% 的控制!进一步的讨论可以参考 对 FrameBuffer的一夜 hack。由此,我的嵌入式实验算是基本达到了第二个要求:写入 LCD 的缓冲区。载入图片是个复杂问题,毕竟图片的格式五花八门,有损压缩、无损压缩;位图、矢量图等等,就连最简单的 bmp 位图,想要载入,恐怕也要了解 bmp 图形文件格式,颇费一番周折。此时我又想起来去年的图形学程序,最后一个 project 就是载入 bmp 文件作为纹理贴到一个 teapot 上面,整个程序下来有 600 行,不便贴出,给出我当时做的作业,其中载入 bmp 文件的代码可以作为参考。
至于 copy 两个文件的 c 语言程序,我也写了两个,采用的是 C 语言的标准 IO 和 Linux 系统调用 IO 两种方式:
ANSI IO:
#include <stdio.h>
#include <stdlib.h>
#define BUFSIZE 1024
int copyfile(const char * infile, const char * outfile) {
FILE *fp1, *fp2;
char buf[BUFSIZE];
int n;
if((fp1 = fopen(infile, "r")) == NULL) {
perror("open file error");
exit(1);
}
if((fp2 = fopen(outfile, "w+")) == NULL) {
perror("open file error");
exit(1);
}
while((n = fread(buf, sizeof(char), BUFSIZE, fp1)) > 0) {
if((fwrite(buf, sizeof(char), n, fp2)) == -1) {
perror("fail to write");
exit(1);
}
}
if(n == -1) {
perror("fail to read");
exit(1);
}
fclose(fp1);
fclose(fp2);
return 0;
}
int main(int argc, char *argv[]) {
if (argc != 3) {
printf ("Usage: cp from to\n");
}
char * file1 = argv[1];
char * file2 = argv[2];
copyfile(file1, file2);
return 0;
}
Linux IO:
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#define BUFSIZE 512
#define PERM 0644
int copyfile(const char *name1, const char *name2) {
int infile, outfile;
ssize_t nread;
char buffer[BUFSIZE];
if ((infile = open(name1, O_RDONLY)) == -1) {
perror("open file error");
return (-1);
}
if ((outfile = open(name2, O_WRONLY | O_CREAT | O_TRUNC, PERM)) == -1) {
perror("create file error");
close(infile);
return (-2);
}
while ((nread = read(infile, buffer, BUFSIZE)) > 0 ) {
if (write(outfile, buffer, nread) < nread) {
perror("write file error");
close(infile);
close(outfile);
return (-3);
}
}
close(infile);
close(outfile);
if (nread == -1) {
return (-4);
}
else return (0);
}
int main(int argc, char *argv[]) {
if (argc != 3) {
printf ("Usage: cp from to\n");
}
char * file1 = argv[1];
char * file2 = argv[2];
copyfile(file1, file2);
return 0;
}
至于两种 IO 有什么样的区别和联系,我写了一份文档,可以作为入门参考。更进一步的了解已经远远跑题,可以参看 jserv 的 HackingHelloWorld 系列。
OK,本次实验超额完成!120分!^_^
言归正传,事实上,有了 framebuffer,Linux的 console 可以变得无所不能!
先上一张图:
在 Gentoo 下,这种效果需要 fbsplash 的支持,参照 Gentoo Wiki(其实当初装 Gentoo 的时候,自己也折腾过这个,但是一直没有启动起来,不明原因,大概是内核版本的问题,可能同样是内核版本的问题,我的 bootchart 也无法启动)。还有一个类似的项目叫 splashy,不过貌似发展比较缓慢,而且应用也不多。
可能你觉得这就是极限了吧……非也非也!其实有 Framebuffer,再加上 MPlayer,我们甚至可以在这样的终端下看 视频!!
MPlayer 本身是支持多种 VIDEO OUTPUT OPTIONS,其中的一种就是 framebuffer:
fbdev (Linux only)
Uses the kernel framebuffer to play video.
<device>
Explicitly choose the fbdev device name to use (e.g. /dev/fb0) or the
name of the VIDIX subdevice if the device name starts with 'vidix'
(e.g. 'vidixsis_vid' for the sis driver).
fbdev2 (Linux only)
Uses the kernel framebuffer to play video, alternative implementation.
<device>
Explicitly choose the fbdev device name to use (default: /dev/fb0).
终端截图需要 FBGrab 的支持。我们在一个终端以 super user 权限运行:
在另外一个终端,同样以超级用户权限运行:
注意:
mplayer -vo
选项需要为fbdev2
,fbdev
不行- fbgrab 生成的 png 格式图像需要转换成 jpeg 格式才能得到满意的效果,否则视频区域一片透明,这可能与图像压缩算法有关(具体我就不知道了)。
差不多了,All about framebuffer。下一次,写一写如何打造高效快捷的终端环境,敬请期待!