CPU是怎样读取内存和显存信息的?显存的信息通不通过内存?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CPU是怎样读取内存和显存信息的?显存的信息通不通过内存?相关的知识,希望对你有一定的参考价值。

谢了

现在读取内存是 集成内存控制器于CPU里所以就是直接 CPU-内存-CPU-内存(英特额1156之前的不一样 AMDK8之前得不一样 )之前得为 CPU-北桥-内存-北桥-CPU。然后是显存(说实话 这个我也不是很懂)我猜的。。首先由CPU发出指令 从硬盘内存调出需要渲染处理得数据 然后经过北桥传送到显卡 经过显卡GPU处理 储存在显存内 然后再经过RAMDxx(忘了怎么打了)转换成数字图像 输出到屏幕。 参考技术A cpu有%80是直接读取一级缓存里的指令的,几乎不会直接读取内存里的指令,显存更是如此,而且显存还有专门的处理器叫gpu,这些设备都是通过南北桥芯片来管理的,北桥芯片管高速设备,通常在靠近cpu的地方,南桥芯片管低速设备,包括什么硬盘啊什么的,通常在远离cpu的地方 参考技术B 北桥负责读内存和显存信息,CPU从北桥读取内存和显存信息.只有在集成显卡的时候才通过占用内存来作为显存 参考技术C 我认为我认为我认为我认为我认为我认为我认为我认为我认为我认为

NanoPi NEO Air使用十四:FrameBuffer的理解和使用

FrameBuffer的介绍

应用程序直接通过操作显存来操作 LCD,实现在 LCD 上显示字符、图片等信息。在 Linux 中应用程序最终也是通过操作 RGB LCD 的显存来实现在 LCD 上显示字符、图片等信息。在裸机中我们可以随意的分配显存,但是在 Linux 系统中内存的管理很严格,显存是需要申请的,不是你想用就能用的。而且因为虚拟内存的存在,驱动程序设置的显存和应用程序访问的显存要是同一片物理内存。

为了解决上述问题, Framebuffer 诞生了, Framebuffer 翻译过来就是帧缓冲,简称 fb,因此大家在以后的 Linux 学习中见到“Framebuffer”或者“fb”的话第一反应应该想到 RGBLCD或者显示设备。 fb 是一种机制,将系统中所有跟显示有关的硬件以及软件集合起来,虚拟出一个 fb 设备,当我们编写好 LCD 驱动以后会生成一个名为/dev/fbX(X=0~n)的设备,应用程序通过访问/dev/fbX 这个设备就可以访问 LCD。 /dev/fb0 是个字符设备,因此肯定有file_operations 操作集, fb 的 file_operations 操作集定义在 drivers/video/fbdev/core/fbmem.c 文件中,如下所示:

static const struct file_operations fb_fops = 
	.owner =	THIS_MODULE,
	.read =		fb_read,
	.write =	fb_write,
	.unlocked_ioctl = fb_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl = fb_compat_ioctl,
#endif
	.mmap =		fb_mmap,
	.open =		fb_open,
	.release =	fb_release,
#if defined(HAVE_ARCH_FB_UNMAPPED_AREA) || \\
	(defined(CONFIG_FB_PROVIDE_GET_FB_UNMAPPED_AREA) && \\
	 !defined(CONFIG_MMU))
	.get_unmapped_area = get_fb_unmapped_area,
#endif
#ifdef CONFIG_FB_DEFERRED_IO
	.fsync =	fb_deferred_io_fsync,
#endif
	.llseek =	default_llseek,
;

Linux抽象出FrameBuffer这个设备来供用户态进程实现直接写屏。Framebuffer机制模仿显卡的功能,将显卡硬件结构抽象掉,可以通过Framebuffer的读写直接对显存进行操作。用户可以将Framebuffer看成是显示内存的一个映像,将其映射到进程地址空间之后,就可以直接进行读写操作,而写操作可以立即反应在屏幕上。这种操作是抽象的,统一的。用户不必关心物理显存的位置、换页机制等等具体细节。这些都是由Framebuffer设备驱动来完成的。

FrameBuffer在Linux中的实现和机制

Framebuffer设备驱动程序架构如下图,用户空间应用程序主要通过read、write、mmap、ioctl这四个接口与Framebuffer设备打交道。但是一般都是采用mmap方式直接操作内存,所以下文也主要介绍mmap方式。

Framebuffer主要结构体

用户空间可见的FrameBuffer设备的主要结构体几乎都是在include/uapi/linux/fb.h这个中文件定义的。这些结构包括:fb_var_screeninfo和fb_fix_screeninfon

fb_var_screeninfo
主要是记录用户可以修改的控制器可变参数:

struct fb_var_screeninfo    
    /*可见解析度*/  
    __u32 xres;            
    __u32 yres;   
    /*虚拟解析度*/  
    __u32 xres_virtual;        
    __u32 yres_virtual;   
    /*虚拟到可见之间的偏移*/  
    __u32 xoffset;             
    __u32 yoffset;             
    __u32 bits_per_pixel;       /*每像素的位数,BPP*/  
    __u32 grayscale;        /*非0时指灰度*/  
    /*fb缓存的R\\G\\B位域*/  
    struct fb_bitfield red;        
    struct fb_bitfield green;      
    struct fb_bitfield blue;   
    struct fb_bitfield transp;  /*透明度*/    
    __u32 nonstd;           /*!=0非标准像素格式*/  
    __u32 activate;            
    __u32 height;           /*高度*/  
    __u32 width;            /*宽度*/  
    __u32 accel_flags;         
    /*除pixclock本身外,其他都以像素时钟为单位*/  
    __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];      /*保留*/  
;  

fb_fix_screeninfon
这个结构在显卡被设定模式后创建,它描述显示卡的属性,并且系统运行时不能被修改;比如FrameBuffer内存的起始地址。它依赖于被设定的模式,当一个模式被设定后,内存信息由显示卡硬件给出,内存的位置等信息就不可以修改。

struct fb_fix_screeninfo    
    char id[16];            /*字符串形式的标识符*/  
    unsigned long smem_start;   /*fb缓冲内存的开始位置(物理地址)*/  
    __u32 smem_len;         /*fb缓冲的长度*/  
    __u32 type;              /*FB_TYPE_* */  
    __u32 type_aux;         /*Interleave*/  
    __u32 visual;           /*FB_VISUAL_* */    
    __u16 xpanstep;         /*如果没有硬件panning,赋0*/  
    __u16 ypanstep;            
    __u16 ywrapstep;           
    __u32 line_length;      /*一行的字节数*/  
    unsigned long mmio_start;   /*内存映射I/O的开始位置*/  
    __u32 mmio_len;         /*内存映射I/O的长度*/  
    __u32 accel;               
    __u16 reserved[3];      /*保留*/  
;  

fb_var_screeninfo与fb_fix_screeninfo的关系见下图:

在framebuffer驱动框架中相关的重要数据结构:

struct fb_ops 
    struct module *owner;
    //检查可变参数并进行设置
    int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info);
    //根据设置的值进行更新,使之有效
    int (*fb_set_par)(struct fb_info *info);
    //设置颜色寄存器
    int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green,
                unsigned blue, unsigned transp, struct fb_info *info);
    //显示空白
    int (*fb_blank)(int blank, struct fb_info *info);
    //矩形填充
    void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect);
    //复制数据
    void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region);
    //图形填充
    void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image);
;

从设备驱动程序结构看,这个驱动主要跟fb_info结构体有相应的关系,这个结构体记录了这个设备驱动的全部相关的信息,
其中就包括设备的设置参数,状态,还有对应底层硬件操作的回调函数。在Linux中,每一个帧缓冲设备都必须对应一个fb_info,fb_info在/linux/fb.h中。

上面的fp_ops这个结构体就是常用的非常重要的一个。和其它的内核代码中的字符驱动类似,同样,如果你要使用这个驱动,你必须去注册这个设备驱动。打开linux3.5的内核代码,显示驱动的分析都是由 drivers/video/fbmem.c开始 。我们可以查阅到fbmem.c里定义 register_framebuffer这个函数。通过 查看这个函数,我们可以发现其实这个函数在drivers/video/s3c-fb.c中的probe函数中调用了 register_framebuffer,在s3c-fb.c这个文件注册了一个平台总线设备的驱动程序。这里要注意了,所谓的平台总线驱动其实是由linux内核本身去实现的,是一种总线驱动的机制。
以下是fb_info结构体:

struct fb_info 
 int node;
 struct fb_var_screeninfo var; /* Current var */
 struct fb_fix_screeninfo fix;  /* Current fix */
 struct fb_videomode *mode; /* current mode */

 struct fb_ops *fbops;
 struct device *device;   /* This is the parent */
 struct device *dev;   /* This is this fb device */

 char __iomem *screen_base; /* Virtual address */
 unsigned long screen_size; /* Amount of ioremapped VRAM or 0 */ 
 …………
;

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:相当于读/写屏幕缓冲区。
例如用 cp /dev/fb0 tmp命令可将当前屏幕的内容拷贝到一个文件中,而命令cp tmp > /dev/fb0 则将图形文件tmp显示在屏幕上。

2、映射(map)操作:由于Linux工作在保护模式,每个应用程序都有自己的虚拟地址空间,在应用程序中是不能直接访问物理缓冲区地址的。为此, Linux在文件操作 file_operations结构中提供了mmap函数,可将文件的内容映射到用户空间。对于帧缓冲设备,则可通过映射操作,可将屏幕缓冲区的物理地址 映射到用户空间的一段虚拟地址中,之后用户就可以通过读写这段虚拟地址访问屏幕缓冲区,在屏幕上绘图了。实际上,使用帧缓冲设备的应用程序都是通过映射操 作来显示图形的。由于映射操作都是由内核来完成,下面我们将看到,帧缓冲驱动留给开发人员的工作并不多。

3、 I/O控制:对于帧缓冲设备,对设备文件的ioctl操作可读取/设置显示设备及屏幕的参数,如分辨率,显示颜色数,屏幕大小等等。ioctl的操作是由底层的驱动程序来完成的。
在应用程序中,操作/dev/fb的一般步骤如下:

  1. 打开/dev/fb设备文件。
  2. 用ioctrl操作取得当前显示屏幕的参数,如屏幕分辨率,每个像素点的比特数。根据屏幕参数可计算屏幕缓冲区的大小。
  3. 将屏幕缓冲区映射到用户空间。
  4. 映射后就可以直接读写屏幕缓冲区,进行绘图和图片显示了。

在屏幕上画一条线:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/mman.h>

int main () 

    int fp=0;
    struct fb_var_screeninfo  vinfo;
    struct fb_fix_screeninfo  finfo;
    long screensize=0;
    char *fbp = 0;
    long location = 0;

    fp = open ("/dev/fb0",O_RDWR);   // 打开/dev/fb设备文件

    if (fp < 0)
    
        printf("Error : Can not open framebuffer device/n");
        exit(1);
    

    if (ioctl(fp,FBIOGET_FSCREENINFO,&finfo))  // fb_fix_screeninfo
    
        printf("Error reading fixed information/n");
        exit(2);
    

    if (ioctl(fp,FBIOGET_VSCREENINFO,&vinfo))
    
        printf("Error reading variable information/n");
        exit(3);
    
    //显示结构体信息
    printf("The mem is :%d\\n",finfo.smem_len);  
    printf("The line_length is :%d\\n",finfo.line_length);  
    printf("The xres is :%d\\n",vinfo.xres);  
    printf("The yres is :%d\\n",vinfo.yres);  
    printf("bits_per_pixel is :%d\\n",vinfo.bits_per_pixel);  

    //计算显存大小
    screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;
    /*这就是把fp所指的文件中从开始到screensize大小的内容给映射出来,得到一个指向这块空间的指针*/
    fbp =(char *) mmap (0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fp,0);
    if ((int) fbp == -1)
    
        printf ("Error: failed to map framebuffer device to memory./n");
        exit (4);
    

    /*从(0,0)到(100,100)画线,(0,0)点在屏幕左上角*/
    int i,j;
    for(i=0,j=0;i<100;i++,j++)
    
        location = i * (vinfo.bits_per_pixel / 8) + j  *  finfo.line_length;

        *(fbp + location) = 0;              /*直接赋值来改变屏幕上某点的颜色*/
        *(fbp + location + 1) = 0xf8;       /* RGB565格式,全红=0xf800 */
    

    munmap (fbp, screensize); /*解除映射*/
    close (fp);    /*关闭文件*/

    return 0;


    //头文件 <sys/mman.h>
    //函数原型:void* mmap(void* start,size_t length,int prot,int flags,int fd,off_t offset);
    //start:映射区的开始地址,设置为0时表示由系统决定映射区的起始地址。
    //length:映射区的长度。//长度单位是 以字节为单位,不足一内存页按一内存页处理
    //prot:期望的内存保护标志,不能与文件的打开模式冲突。是以下的某个值,可以通过or运算合理地组合在一起
    //flags:相关的标志,就跟open函数的标志差不多的,自己百度去查
    //fd:有效的文件描述词。一般是由open()函数返回,其值也可以设置为-1,此时需要指定flags参数中的MAP_ANON,表明进行的是匿名映射。
    //off_toffset:被映射对象内容的起点。

    //PROT_READ //页内容可以被读取
    //PROT_WRITE //页可以被写入
    //MAP_SHARED //与其它所有映射这个对象的进程共享映射空间。对共享区的写入,相当于输出到文件。直到msync()或者munmap()被调用,文件实际上不会被更新。

编译
arm-linux-gcc fb_test.c -o fb_test
编译后上传scp fb_test root@192.168.0.101:/lib/modules/4.14.111/
执行

显示效果:

参考:
2016/1/9:深度剖析安卓Framebuffer设备驱动
【原创】IP摄像头技术纵览(三)—图像数据在帧缓存设备(framebuffer)上的显示

以上是关于CPU是怎样读取内存和显存信息的?显存的信息通不通过内存?的主要内容,如果未能解决你的问题,请参考以下文章

T-Mobile G1 (MSM7200) 显存

6g显存用了5g算爆显存吗

nvidia-smi 关键知识

NanoPi NEO Air使用十四:FrameBuffer的理解和使用

NanoPi NEO Air使用十四:FrameBuffer的理解和使用

WebGL与Canvas的显存与内存使用分析