精华内容
下载资源
问答
  • 图形内存是进程共享内存,且根据其标志支持不同硬件设备的读与写。 buffer_handle_t 是 *private_handle_t,gralloc模块自定义private_handle_t类型,并实现图形内存的实际申请。 GraphicBuffer跨进程共享的流程是用...

    图形内存的申请与显示

    这一篇回答序言中的第一个问题:
    如何申请可以用来送显的内存,如何将其送往LCD?

    要点

    1. 图形内存是进程共享内存,且根据其标志支持不同硬件设备的读与写。
    2. buffer_handle_t 是 *private_handle_t,gralloc模块自定义private_handle_t类型,并实现图形内存的实际申请。
    3. GraphicBuffer跨进程共享的流程是用binder传输必要信息到另一进程,另一进程调用gralloc模块的registerBuffer方法映射其到自己的内存空间。
    4. GraphicBuffer与SurfaceFlinger 没有 直接关系,图形内存不仅仅是提供给窗口系统用的,也不是非得在SurfaceFlinger进程里申请。
    5. 调用 gralloc 模块的 post 函数指针,并在HAL层发送 FBIOPAN_DISPLAY 指令,是将图形内存送显的一个路径,但不是惟一。

    GraphicBuffer

    图形内存是用来渲染和显示的,它需要被跨进程共享,并支持不同硬件(GPU、DSP等)使用。
    Android中的图形内存包裹类为GraphicBuffer。
    GraphicBuffer
    GraphicBuffer继承于ANativeWindowBuffer。
    ANativeWindowBuffer包含w,h,stride和handle属性,其中handle对应一个private_handle_t的指针。
    GraphicBuffer额外包括 mBufferMapper(映射器,为单例),mId(每块GraphicBuffer的独立id,根据进程号与申请顺序编号),mInitCheck(表示申请状态,用来检查是否实际申请到了内存)。

    GraphicBuffer申请流程

    申请释放图形内存的流程如下:
    申请释放流程

    GraphicBuffer共享流程

    Map流程
    尽管已经将GraphicBuffer映射到了自己的进程空间,在进一步使用时,流程上需要在使用前lock,使用完后unlock,这两个步骤一般用来作cache同步(根据共享内存策略,如果是缓存式,CPU/GPU会先把数据写到缓存,达到一定量才同步到GraphicBuffer中,unlock时可以强制把缓存同步一次)。

    标志flags介绍

    在 GraphicBuffer申请及lock的参数里,有个flags属性值,gralloc模块根据这个值,去判断从什么地方申请内存,按什么方式组织内存,我们来看一下:

    USAGE_SW_READ_NEVER   = GRALLOC_USAGE_SW_READ_NEVER,
    USAGE_SW_READ_RARELY  = GRALLOC_USAGE_SW_READ_RARELY,
    USAGE_SW_READ_OFTEN   = GRALLOC_USAGE_SW_READ_OFTEN,
    USAGE_SW_READ_MASK    = GRALLOC_USAGE_SW_READ_MASK,
    USAGE_SW_WRITE_NEVER  = GRALLOC_USAGE_SW_WRITE_NEVER,
    USAGE_SW_WRITE_RARELY = GRALLOC_USAGE_SW_WRITE_RARELY,
    USAGE_SW_WRITE_OFTEN  = GRALLOC_USAGE_SW_WRITE_OFTEN,
    USAGE_SW_WRITE_MASK   = GRALLOC_USAGE_SW_WRITE_MASK,
    USAGE_SOFTWARE_MASK   = USAGE_SW_READ_MASK|USAGE_SW_WRITE_MASK,
    USAGE_PROTECTED       = GRALLOC_USAGE_PROTECTED,
    USAGE_HW_TEXTURE      = GRALLOC_USAGE_HW_TEXTURE,
    USAGE_HW_RENDER       = GRALLOC_USAGE_HW_RENDER,
    USAGE_HW_2D           = GRALLOC_USAGE_HW_2D,
    USAGE_HW_COMPOSER     = GRALLOC_USAGE_HW_COMPOSER,
    USAGE_HW_VIDEO_ENCODER= GRALLOC_USAGE_HW_VIDEO_ENCODER,
    USAGE_HW_MASK         = GRALLOC_USAGE_HW_MASK,
    USAGE_CURSOR          = GRALLOC_USAGE_CURSOR,

    GRALLOC_USAGE_SW_READ_NEVER,GRALLOC_USAGE_SW_READ_RARELY,GRALLOC_USAGE_SW_READ_OFTEN
    分别表示CPU不需要/很少会/经常会读这块GraphicBuffer,对于READ_OFTEN经常读的情况,gralloc模块应该考虑建读缓存了。
    CPU写的三个标志类似。
    当发觉GraphicBuffer操作起来速度慢时,就得看一下,是不是忘了配CPU的读写标志了。

    GRALLOC_USAGE_HW_TEXTURE
    GRALLOC_USAGE_HW_RENDER
    这两个标志分别表示需要GPU读与写,TEXTURE表示可以映射为一个OpenGL的纹理,RENDER表示可以作为OpenGL的渲染目标。
    一般来说,gralloc分配的内存都是gpu可读写的,也不需要加这两个标志。

    GRALLOC_USAGE_HW_COMPOSER
    这个表示这个GraphicBuffer可以由硬件合成器直接合成。

    GRALLOC_USAGE_HW_VIDEO_ENCODER
    这个表示这个GraphicBuffer可以作为Video硬解码(一般是DSP)的输入输出对象。

    Gralloc

    gralloc模块需要实现如下三个设备及函数指针。

    内存分配

    typedef struct alloc_device_t {  
        struct hw_device_t common;
        int (*alloc)(struct alloc_device_t* dev,  
                int w, int h, int format, int usage,  
                buffer_handle_t* handle, int* stride);  
    
        int (*free)(struct alloc_device_t* dev,  
                buffer_handle_t handle);  
    } alloc_device_t;  

    内存共享

    typedef struct gralloc_module_t {  
        ......  
    
        int (*registerBuffer)(struct gralloc_module_t const* module,  
                buffer_handle_t handle);  
    
        int (*unregisterBuffer)(struct gralloc_module_t const* module,  
                buffer_handle_t handle);  
    
    
        int (*lock)(struct gralloc_module_t const* module,  
                buffer_handle_t handle, int usage,  
                int l, int t, int w, int h,  
                void** vaddr);  
    
        int (*unlock)(struct gralloc_module_t const* module,  
                buffer_handle_t handle);  
        ......  
    }

    显示

    typedef struct framebuffer_device_t {  
        struct hw_device_t common;  
        ......
        int (*setSwapInterval)(struct framebuffer_device_t* window, int interval);//设置刷新频率
        int (*setUpdateRect)(struct framebuffer_device_t* window,int left, int top, int width, int height);//设置更新区域,对于带缓存的LCD屏,可以在只传发生了变化的区域过去,此即局部刷新。
        int (*post)(struct framebuffer_device_t* dev, buffer_handle_t buffer);//送显
        ......
    } framebuffer_device_t;

    关于gralloc模块如何注册如何打开,可看老罗的博客:
    http://blog.csdn.net/luoshengyang/article/details/7747932
    这里面需要校正的是 hardware/libhardware/modules/gralloc下面的代码编译出来的是gralloc.default.so,即Android系统默认提供的。一般来说厂商不会用这个so。
    如下是Arm为Mali系列gpu提供的开源gralloc代码的链接,相对而言更有参考意义。
    http://malideveloper.arm.com/cn/develop-for-mali/drivers/open-source-mali-gpus-android-gralloc-module/
    Arm提供的代码里面包括自家的ump方案和标准的ion方案,一般而言,在Android4.2之后,普遍用的都是ion方案,ion内存共享方案可参考此博客的文章。
    http://blog.csdn.net/qq160816/article/details/38082579
    http://blog.csdn.net/qq160816/article/details/38299251

    我们只看一下这份代码里面送显的部分:

    
    static int fb_post(struct framebuffer_device_t* dev, buffer_handle_t buffer)
    {
        if (private_handle_t::validate(buffer) < 0)
        {
            return -EINVAL;
        }
    
        private_handle_t const* hnd = reinterpret_cast<private_handle_t const*>(buffer);
        private_module_t* m = reinterpret_cast<private_module_t*>(dev->common.module);
    
        if (m->currentBuffer)
        {
            m->base.unlock(&m->base, m->currentBuffer);
            m->currentBuffer = 0;
        }
    
        if (hnd->flags & private_handle_t::PRIV_FLAGS_FRAMEBUFFER)
        {
            m->base.lock(&m->base, buffer, private_module_t::PRIV_USAGE_LOCKED_FOR_POST, 
                    0, 0, m->info.xres, m->info.yres, NULL);
    
            const size_t offset = hnd->base - m->framebuffer->base;
            int interrupt;
            m->info.activate = FB_ACTIVATE_VBL;
            m->info.yoffset = offset / m->finfo.line_length;
    
    #ifdef STANDARD_LINUX_SCREEN
    #define FBIO_WAITFORVSYNC       _IOW('F', 0x20, __u32)
    #define S3CFB_SET_VSYNC_INT _IOW('F', 206, unsigned int)
            if (ioctl(m->framebuffer->fd, FBIOPAN_DISPLAY, &m->info) == -1) 
            {
                AERR( "FBIOPAN_DISPLAY failed for fd: %d", m->framebuffer->fd );
                m->base.unlock(&m->base, buffer); 
                return 0;
            }
    
            {
                // enable VSYNC
                interrupt = 1;
                if(ioctl(m->framebuffer->fd, S3CFB_SET_VSYNC_INT, &interrupt) < 0) 
                {
                    AERR( "S3CFB_SET_VSYNC_INT enable failed for fd: %d", m->framebuffer->fd );
                    return 0;
                }
                // wait for VSYNC
    #ifdef MALI_VSYNC_EVENT_REPORT_ENABLE
                gralloc_mali_vsync_report(MALI_VSYNC_EVENT_BEGIN_WAIT);
    #endif
                int crtc = 0;
                if(ioctl(m->framebuffer->fd, FBIO_WAITFORVSYNC, &crtc) < 0)
                {
                    AERR( "FBIO_WAITFORVSYNC failed for fd: %d", m->framebuffer->fd );
    #ifdef MALI_VSYNC_EVENT_REPORT_ENABLE
                    gralloc_mali_vsync_report(MALI_VSYNC_EVENT_END_WAIT);
    #endif
                    return 0;
                }
    #ifdef MALI_VSYNC_EVENT_REPORT_ENABLE
                gralloc_mali_vsync_report(MALI_VSYNC_EVENT_END_WAIT);
    #endif
                // disable VSYNC
                interrupt = 0;
                if(ioctl(m->framebuffer->fd, S3CFB_SET_VSYNC_INT, &interrupt) < 0) 
                {
                    AERR( "S3CFB_SET_VSYNC_INT disable failed for fd: %d", m->framebuffer->fd );
                    return 0;
                }
            }
    #else 
            /*Standard Android way*/
    #ifdef MALI_VSYNC_EVENT_REPORT_ENABLE
            gralloc_mali_vsync_report(MALI_VSYNC_EVENT_BEGIN_WAIT);
    #endif
            if (ioctl(m->framebuffer->fd, FBIOPUT_VSCREENINFO, &m->info) == -1) 
            {
                AERR( "FBIOPUT_VSCREENINFO failed for fd: %d", m->framebuffer->fd );
    #ifdef MALI_VSYNC_EVENT_REPORT_ENABLE
                gralloc_mali_vsync_report(MALI_VSYNC_EVENT_END_WAIT);
    #endif
                m->base.unlock(&m->base, buffer); 
                return -errno;
            }
    #ifdef MALI_VSYNC_EVENT_REPORT_ENABLE
            gralloc_mali_vsync_report(MALI_VSYNC_EVENT_END_WAIT);
    #endif
    #endif
    
            m->currentBuffer = buffer;
        } 
        else
        {
            void* fb_vaddr;
            void* buffer_vaddr;
    
            m->base.lock(&m->base, m->framebuffer, GRALLOC_USAGE_SW_WRITE_RARELY, 
                    0, 0, m->info.xres, m->info.yres, &fb_vaddr);
    
            m->base.lock(&m->base, buffer, GRALLOC_USAGE_SW_READ_RARELY, 
                    0, 0, m->info.xres, m->info.yres, &buffer_vaddr);
    
            memcpy(fb_vaddr, buffer_vaddr, m->finfo.line_length * m->info.yres);
    
            m->base.unlock(&m->base, buffer); 
            m->base.unlock(&m->base, m->framebuffer); 
        }
    
        return 0;
    }

    送显的核心代码是这一句:

            if (ioctl(m->framebuffer->fd, FBIOPAN_DISPLAY, &m->info) == -1) 

    再往下看得跟厂商的内核代码了,这里没代码略过。关于LCD显示原理看老罗那篇就好了。
    调用gralloc模块的post函数不是惟一一种将图形内存送到LCD显示的方法,另一种方式是Overlay(hwcomposer的硬件合成)。

    展开全文
  • 现阶段广泛应用于多媒体、Graphics领域的共享内存方式,某种意义上不再强调映射到进程虚拟地址空间的概念(那无非是为了让CPU访问),而更强调以某种“句柄”的形式,让大家知道某一片视频、图形图像数据的存在并...

    共享单车、共享充电宝、共享雨伞,世间的共享有千万种,而我独爱共享内存。

    早期的共享内存,着重于强调把同一片内存,map到多个进程的虚拟地址空间(在相应进程找到一个VMA区域),以便于CPU可以在各个进程访问到这片内存。

    现阶段广泛应用于多媒体、Graphics领域的共享内存方式,某种意义上不再强调映射到进程虚拟地址空间的概念(那无非是为了让CPU访问),而更强调以某种“句柄”的形式,让大家知道某一片视频、图形图像数据的存在并可以借助此“句柄”来跨进程引用这片内存,让视频encoder、decoder、GPU等可以跨进程访问内存。所以不同进程用的加速硬件其实是不同的,他们更在乎的是可以通过一个handle拿到这片内存,而不再特别在乎CPU访问它的虚拟地址(当然仍然可以映射到进程的虚拟地址空间供CPU访问)。

    只要内存的拷贝(memcpy)仍然是一个占据内存带宽、CPU利用率的消耗大户存在,共享内存作为Linux进程间通信、计算机系统里各个不同硬件组件通信的最高效方法,都将持续繁荣。关于内存拷贝会大多程度地占据CPU利用率,这个可以最简单地尝试拷贝1080P,帧率每秒60的电影画面,我保证你的系统的CPU,蛋会疼地不行。

    我早就想系统地写一篇综述Linux里面各种共享内存方式的文章了,但是一直被带娃这个事业牵绊,今日我决定顶着娃娃们的山呼海啸,也要写一篇文章不吐不快。

    共享内存的方式有很多种,目前主流的方式仍然有:

    共享内存的方式

    1.基于传统SYS V的共享内存;

    2.基于POSIX mmap文件映射实现共享内存;

    3.通过memfd_create()和fd跨进程共享实现共享内存;

    4.多媒体、图形领域广泛使用的基于dma-buf的共享内存。

    SYS V共享内存

    历史悠久、年代久远、API怪异,对应内核代码linux/ipc/shm.c,当你编译内核的时候不选择CONFIG_SYSVIPC,则不再具备此能力。

    你在Linux敲ipcs命令看到的share memory就是这种共享内存:

    下面写一个最简单的程序来看共享内存的写端sw.c:

    以及共享内存的读端sr.c:

    编译和准备运行:

    在此之前我们看一下系统的free:

    下面运行sw和sr:

    我们发现sr打印出来的和sw写进去的是一致的。这个时候我们再看下free:

    可以看到used显著增大了(711632 -> 715908), shared显著地增大了(2264 -> 6360),而cached这一列也显著地增大326604->330716。

    我们都知道cached这一列统计的是file-backed的文件的page cache的大小。理论上,共享内存属于匿名页,但是由于这里面有个非常特殊的tmpfs(/dev/shm指向/run/shm,/run/shm则mount为tmpfs):

    所以可以看出tmpfs的东西其实真的是有点含混:我们可以理解它为file-backed的匿名页(anonymous page),有点类似女声中的周深。前面我们反复强调,匿名页是没有文件背景的,这样当进行内存交换的时候,是与swap分区交换。磁盘文件系统里面的东西在内存的副本是file-backed的页面,所以不存在与swap分区交换的问题。但是tmpfs里面的东西,真的是在统计意义上统计到page cache了,但是它并没有真实的磁盘背景,这又和你访问磁盘文件系统里面的文件产生的page cache有本质的区别。所以,它是真地有那么一点misc的感觉,凡事都没有绝对,唯有变化本身是不变的。

    也可以通过ipcs找到新创建的SYS V共享内存:

    POSIX共享内存

    我对POSIX shm_open()、mmap () API系列的共享内存的喜爱,远远超过SYS V 100倍。原谅我就是一个懒惰的人,我就是讨厌ftok、shmget、shmat、shmdt这样的API。

    上面的程序如果用POSIX的写法,可以简化成写端psw.c:

    读端:

    编译和执行:

    这样我们会在/dev/shm/、/run/shm下面看到一个文件:

    坦白讲,mmap、munmap这样的API让我找到了回家的感觉,刚入行做Linux的时候,写好framebuffer驱动后,就是把/dev/fb0 mmap到用户空间来操作,所以mmap这样的 API,真的是特别亲切,像亲人一样。

    当然,如果你不喜欢shm_open()这个API,你也可以用常规的open来打开文件,然后进行mmap。关键的是mmap,wikipedia如是说:

    mmap

    In computing, mmap(2) is a POSIX-compliant Unix system call that maps files or devices into memory. It is a method of memory-mapped file I/O. It implements demand paging, because file contents are not read from disk directly and initially do not use physical RAM at all. The actual reads from disk are performed in a "lazy" manner, after a specific location is accessed. After the memory is no longer needed, it is important to munmap(2) the pointers to it. Protection information can be managed using mprotect(2), and special treatment can be enforced using madvise(2).

    POSIX的共享内存,仍然符合我们前面说的tmpfs的特点,在运行了sw,sr后,再运行psw和psr,我们发现free命令再次戏剧性变化:

    请将这个free命令的结果与前2次的free结果的各个字段进行对照:

    第3次比第2次的cached大了这么多?是因为我编写这篇文章边在访问磁盘里面的文件,当然POSIX的这个共享内存本身也导致cached增大了。

    memfd_create

    如果说POSIX的mmap让我找到回家的感觉,那么memfd_create()则是万般惊艳。见过这种API,才知道什么叫天生尤物——而且是尤物中的尤物,它完全属于那种让码农第一眼看到就会两眼充血,恨不得眼珠子夺眶而出贴到它身上去的那种API;一般人见到它第一次,都会忽略了它的长相,因为它的身材实在太火辣太抢眼了。

    先不要浮想联翩,在所有的所有开始之前,我们要先提一下跨进程分享fd(文件描述符,对应我们很多时候说的“句柄”)这个重要的概念。

    众所周知,Linux的fd属于一个进程级别的东西。进入每个进程的/proc/pid/fd可以看到它的fd的列表:

    这个进程的0,1,2和那个进程的0,1,2不是一回事。

    某年某月的某一天,人们发现,一个进程其实想访问另外一个进程的fd。当然,这只是目的不是手段。比如进程A有2个fd指向2片内存,如果进程B可以拿到这2个fd,其实就可以透过这2个fd访问到这2片内存。这个fd某种意义上充当了一个中间媒介的作用。有人说,那还不简单吗,如果进程A:

    fd = open();

    open()如果返回100,把这个100告诉进程B不就可以了吗,进程B访问这个100就可以了。这说明你还是没搞明白fd是一个进程内部的东西,是不能跨进程的概念。你的100和我的100,不是一个东西。这些基本的东西你搞不明白,你搞别的都是白搭。

    Linux提供一个特殊的方法,可以把一个进程的fd甩锅、踢皮球给另外一个进程(其实“甩锅”这个词用在这里不合适,因为“甩锅”是一种推卸,而fd的传递是一种分享)。我特码一直想把我的bug甩(分)锅(享)出去,却发现总是被人把bug甩锅过来。

    那么如何甩(分)锅(享)fd呢?

    Linux里面的甩锅需要借助cmsg,用于在socket上传递控制消息(也称Ancillary data),使用SCM_RIGHTS,进程可以透过UNIX Socket把一个或者多个fd(file descriptor)传递给另外一个进程。

    比如下面的这个函数,可以透过socket把fds指向的n个fd发送给另外一个进程:

    而另外一个进程,则可以透过如下函数接受这个fd:

    那么问题来了,如果在进程A中有一个文件的fd是100,发送给进程B后,它还是100吗?不能这么简单地理解,fd本身是一个进程级别的概念,每个进程有自己的fd的列表,比如进程B收到进程A的fd的时候,进程B自身fd空间里面自己的前面200个fd都已经被占用了,那么进程B接受到的fd就可能是201。数字本身在Linux的fd里面真地是一点都不重要,除了几个特殊的0,1,2这样的数字外。同样的,如果你把 cat /proc/interrupts 显示出的中断号就看成是硬件里面的中断偏移号码(比如ARM GIC里某号硬件中断),你会发现,这个关系整个是一个瞎扯。

    知道了甩锅API,那么重要的是,当它与memfd_create()结合的时候,我们准备甩出去的fd是怎么来?它是memfd_create()的返回值。

    memfd_create()这个函数的玄妙之处在于它会返回一个“匿名”内存“文件”的fd,而它本身并没有实体的文件系统路径,其典型用法如下:

    我们透过memfd_create()创建了一个“文件”,但是它实际映射到一片内存,而且在/xxx/yyy/zzz这样的文件系统下没有路径!没有路径!没有路径!

    所以,当你在Linux里面编程的时候,碰到这样的场景:需要一个fd,当成文件一样操作,但是又不需要真实地位于文件系统,那么,就请立即使用memfd_create()吧,它的manual page是这样描述的:

    memfd_create

    memfd_create() creates an anonymous file and returns a file descriptor that refers to it. The file behaves like a regular file, and so can be modified, truncated, memory-mapped, and so on. However, unlike a regular file, it lives in RAM and has a volatile backing storage.

     

    重点理解其中的regular这个单词。它的行动像一个regular的文件,但是它的背景却不regular。

    那么,它和前面我们说的透过UNIX Socket甩锅fd又有什么关系呢?memfd_create()得到了fd,它在行为上类似规则的fd,所以也可以透过socket来进行甩锅,这样A进程相当于把一片与fd对应的内存,分享给了进程B。

    下面的代码进程A通过memfd_create()创建了2片4MB的内存,并且透过socket(路径/tmp/fd-pass.socket)发送给进程B这2片内存对应的fd:

    下面的代码进程B透过相同的socket接受这2片内存对应的fd,之后通过read()读取每个文件的前256个字节并打印:

    上述代码参考了:

    https://openforums.wordpress.com/2016/08/07/open-file-descriptor-passing-over-unix-domain-sockets/

    上述的代码中,进程B是在进行read(fds[i], buffer, sizeof(buffer)),这体现了基于fd进行操作的regular特点。当然,如果是共享内存,现实的代码肯定还是多半会是mmap:

    mmap(NULL, SIZE, PROT_READ, MAP_SHARED, fd, 0);

    那么,透过socket发送memfd_create() fd来进行进程间共享内存这种方法,它究竟惊艳在哪里?

    我认为首要的惊艳之处在于编程模型的惊艳。API简单、灵活、通用。进程之间想共享几片内存共享几片内存,想怎么共享怎么共享,想共享给谁共享给谁,无非是多了几个fd和socket的传递过程。比如,我从互联网上面收到了jpeg的视频码流,一帧帧的画面,进程A可以创建多片buffer来缓存画面,然后就可以透过把每片buffer对应的fd,递交给另外的进程去解码等。Avenue to Jane(大道至简),简单的才是最好的!

    memfd_create()的另外一个惊艳之处在于支持“封印”(sealing,就是你玩游戏的时候的封印),sealing这个单词本身的意思是封条,在这个场景下,我更愿意把它翻译为“封印”。中国传说中的封印,多是采用如五行、太极、八卦等手段,并可有例如符咒、法器等物品的辅助。现指对某个单位施加一种力量,使其无法正常使用某些能力的本领(常出现于玄幻及神魔类作品,游戏中)。我这一生,最喜欢玩的游戏就是《仙剑奇侠传》和《轩辕剑——天之痕》,不知道是否暴露年龄了。

    采用memfd_create()的场景下,我们同样可以用某种法器,来控制共享内存的shrink、grow和write。最初的设想可以详见File Sealing & memfd_create()这篇文章:

    https://lwn.net/Articles/591108/

    我们如果在共享内存上施加了这样的封印,则可以限制对此片区域的ftruncate、write等动作,并建立某种意义上进程之间的相互信任,这是不是很拉风?

    还记得镇压孙悟空的五行山顶的封印吗?还记得孙悟空的紧箍咒吗?还记得悟空每次离开师傅的时候在师傅周围画的一个圈吗?

    封印

    * SEAL_SHRINK: If set, the inode size cannot be reduced * SEAL_GROW: If set, the inode size cannot be increased * SEAL_WRITE: If set, the file content cannot be modified

    File Sealing & memfd_create()文中举到的一个典型使用场景是,如果graphics client把它与graphics compoistor共享的内存交给compoistor去render,compoistor必须保证可以拿到这片内存。这里面的风险是client可能透过ftruncate()把这个memory shrink小,这样compositor就拿不到完整的buffer,会造成crash。所以compositor只愿意接受含有SEAL_SHRINK封印的fd,如果没有,对不起,我们不能一起去西天取经。

    在支持memfd_create()后,我们应尽可能地使用这种方式来替代传统的POSIX和SYS V,基本它也是一个趋势,比如我们在wayland相关项目中能看到这样的patch:

    dma_buf

    dma_buf定义

    The DMABUF framework provides a generic method for sharing buffers between multiple devices. Device drivers that support DMABUF can export a DMA buffer to userspace as a file descriptor (known as the exporter role), import a DMA buffer from userspace using a file descriptor previously exported for a different or the same device (known as the importer role), or both.

     

    简单地来说,dma_buf可以实现buffer在多个设备的共享,应用可以把一片底层驱动A的buffer导出到用户空间成为一个fd,也可以把fd导入到底层驱动 B。当然,如果进行mmap()得到虚拟地址,CPU也是可以在用户空间访问到已经获得用户空间虚拟地址的底层buffer的。

    上图中,进程A访问设备A并获得其使用的buffer的fd,之后通过socket把fd发送给进程B,而后进程B导入fd到设备B,B获得对设备A中的buffer的共享访问。如果CPU也需要在用户态访问这片buffer,则进行了mmap()动作。

    为什么我们要共享DMA buffer?想象一个场景:你要把你的屏幕framebuffer的内容透过gstreamer多媒体组件的服务,变成h264的视频码流,广播到网络上面,变成流媒体播放。在这个场景中,我们就想尽一切可能的避免内存拷贝

    技术上,管理framebuffer的驱动可以把这片buffer在底层实现为dma_buf,然后graphics compositor给这片buffer映射出来一个fd,之后透过socket发送fd 把这篇内存交给gstreamer相关的进程,如果gstreamer相关的“color space硬件转换”组件、“H264编码硬件组件”可以透过收到的fd还原出这些dma_buf的地址,则可以进行直接的加速操作了。比如color space透过接收到的fd1还原出framebuffer的地址,然后把转化的结果放到另外一片dma_buf,之后fd2对应这片YUV buffer被共享给h264编码器,h264编码器又透过fd2还原出YUV buffer的地址。

    这里面的核心点就是fd只是充当了一个“句柄”,用户进程和设备驱动透过fd最终寻找到底层的dma_buf,实现buffer在进程和硬件加速组件之间的zero-copy,这里面唯一进行了exchange的就是fd。

    再比如,如果把方向反过来,gstreamer从网络上收到了视频流,把它透过一系列动作转换为一片RGB的buffer,那么这片RGB的buffer最终还要在graphics compositor里面渲染到屏幕上,我们也需要透过dma_buf实现内存在video的decoder相关组件与GPU组件的共享。

    Linux内核的V4L2驱动(encoder、decoder多采用此种驱动)、DRM(Direct Rendering Manager,framebuffer/GPU相关)等都支持dma_buf。比如在DRM之上,进程可以透过

    int drmPrimeHandleToFD(int fd,
    uint32_t handle,
    uint32_t flags,
    int * prime_fd 
    )

    获得底层framebuffer对应的fd。如果这个fd被分享给gstreamer相关进程的video的color space转换,而color space转换硬件组件又被实现为一个V4L2驱动,则我们可以透过V4L2提供的如下接口,将这片buffer提供给V4L2驱动供其导入:

    如果是multi plane的话,则需要导入多个fd:

    相关细节可以参考这个文档:

    https://linuxtv.org/downloads/v4l-dvb-apis/uapi/v4l/dmabuf.html

    一切都是文件不是文件创造条件也要把它变成文件!这就是Linux的世界观。是不是文件不重要,关键是你得觉得它是个文件。在dma_buf的场景下,fd这个东西,纯粹就是个"句柄",方便大家通过这么一个fd能够对应到最终硬件需要访问的buffer。所以,透过fd的分享和传递,实际实现跨进程、跨设备(包括CPU)的内存共享。

    如果说前面的SYS V、POSIX、memfd_create()更加强调内存在进程间的共享,那么dma_buf则更加强调内存在设备间的共享,它未必需要跨进程。比如:

    有的童鞋说,为嘛在一个进程里面设备A和B共享内存还需要fd来倒腾一遍呢?我直接设备A驱动弄个全局变量存buffer的物理地址,设备B的驱动访问这个全局变量不就好了吗?我只能说,你对Linux内核的只提供机制不提供策略,以及软件工程每个模块各司其责,高内聚和低耦合的理解,还停留在裸奔的阶段。在没有dma_buf等类似机制的情况下,如果用户空间仍然负责构建策略并连接设备A和B,人们为了追求代码的干净,往往要进行这样的内存拷贝:

    dma_buf的支持依赖于驱动层是否实现了相关的callbacks。比如在v4l2驱动中,v4l2驱动支持把dma_buf导出(前面讲了v4l2也支持dma_buf的导入,关键看数据方向),它的代码体现在:

    drivers/media/common/videobuf2/videobuf2-dma-contig.c中的:

    其中的vb2_dc_dmabuf_ops是一个struct dma_buf_ops,它含有多个成员函数:

    当用户call VIDIOC_EXPBUF这个IOCTL的时候,可以把dma_buf转化为fd:

    int ioctl(int fd, VIDIOC_EXPBUF, struct v4l2_exportbuffer *argp);

    对应着驱动层的代码则会调用dma_buf_fd():

    应用程序可以通过如下方式拿到底层的dma_buf的fd:

    dma_buf的导入侧设备驱动,则会用到如下这些API:

    dma_buf_attach()
    dma_buf_map_attachment()
    dma_buf_unmap_attachment()
    dma_buf_detach()

    下面这张表,是笔者对这几种共享内存方式总的归纳:

    落花满天蔽月光,借一杯附荐凤台上。

    全剧终

     

    本文分享自微信公众号 - Linux阅码场(LinuxDev),作者:宋宝华

    原始发表时间:2019-12-09

     

    https://cloud.tencent.com/developer/article/1551288

     

    https://segmentfault.com/a/1190000021126368?utm_source=tag-newest

    展开全文
  • 梅花落2015-03-21 01:44:00浏览2592评论0 ... 要点 图形内存是进程共享内存,且根据其标志支持不同硬件设备的读与写。 buffer_handle_t 是 *private_handle_t,gralloc模块自定义private_handle_t类型,并实现...

     

    梅花落 2015-03-21 01:44:00 浏览2592 评论0

    摘要: 图形内存的申请与显示 这一篇回答序言中的第一个问题: 如何申请可以用来送显的内存,如何将其送往LCD? 要点 图形内存是进程共享内存,且根据其标志支持不同硬件设备的读与写。 buffer_handle_t 是 *private_handle_t,gralloc模块自定义private_handle_t类型,并实现图形内存的实际申请。 GraphicBuffer跨

    图形内存的申请与显示

    这一篇回答序言中的第一个问题: 
    如何申请可以用来送显的内存,如何将其送往LCD?

    要点

    1. 图形内存是进程共享内存,且根据其标志支持不同硬件设备的读与写。
    2. buffer_handle_t 是 *private_handle_t,gralloc模块自定义private_handle_t类型,并实现图形内存的实际申请。
    3. GraphicBuffer跨进程共享的流程是用binder传输必要信息到另一进程,另一进程调用gralloc模块的registerBuffer方法映射其到自己的内存空间。
    4. GraphicBuffer与SurfaceFlinger 没有 直接关系,图形内存不仅仅是提供给窗口系统用的,也不是非得在SurfaceFlinger进程里申请。
    5. 调用 gralloc 模块的 post 函数指针,并在HAL层发送 FBIOPAN_DISPLAY 指令,是将图形内存送显的一个路径,但不是惟一。

    GraphicBuffer

    图形内存是用来渲染和显示的,它需要被跨进程共享,并支持不同硬件(GPU、DSP等)使用。 
    Android中的图形内存包裹类为GraphicBuffer。 
    GraphicBuffer
    GraphicBuffer继承于ANativeWindowBuffer。 
    ANativeWindowBuffer包含w,h,stride和handle属性,其中handle对应一个private_handle_t的指针。 
    GraphicBuffer额外包括 mBufferMapper(映射器,为单例),mId(每块GraphicBuffer的独立id,根据进程号与申请顺序编号),mInitCheck(表示申请状态,用来检查是否实际申请到了内存)。

    GraphicBuffer申请流程

    申请释放图形内存的流程如下: 
    申请释放流程

    GraphicBuffer共享流程

    Map流程
    尽管已经将GraphicBuffer映射到了自己的进程空间,在进一步使用时,流程上需要在使用前lock,使用完后unlock,这两个步骤一般用来作cache同步(根据共享内存策略,如果是缓存式,CPU/GPU会先把数据写到缓存,达到一定量才同步到GraphicBuffer中,unlock时可以强制把缓存同步一次)。

    标志flags介绍

    在 GraphicBuffer申请及lock的参数里,有个flags属性值,gralloc模块根据这个值,去判断从什么地方申请内存,按什么方式组织内存,我们来看一下:

    USAGE_SW_READ_NEVER   = GRALLOC_USAGE_SW_READ_NEVER,
    USAGE_SW_READ_RARELY  = GRALLOC_USAGE_SW_READ_RARELY,
    USAGE_SW_READ_OFTEN   = GRALLOC_USAGE_SW_READ_OFTEN,
    USAGE_SW_READ_MASK    = GRALLOC_USAGE_SW_READ_MASK,
    USAGE_SW_WRITE_NEVER  = GRALLOC_USAGE_SW_WRITE_NEVER,
    USAGE_SW_WRITE_RARELY = GRALLOC_USAGE_SW_WRITE_RARELY,
    USAGE_SW_WRITE_OFTEN  = GRALLOC_USAGE_SW_WRITE_OFTEN,
    USAGE_SW_WRITE_MASK   = GRALLOC_USAGE_SW_WRITE_MASK,
    USAGE_SOFTWARE_MASK   = USAGE_SW_READ_MASK|USAGE_SW_WRITE_MASK,
    USAGE_PROTECTED       = GRALLOC_USAGE_PROTECTED,
    USAGE_HW_TEXTURE      = GRALLOC_USAGE_HW_TEXTURE,
    USAGE_HW_RENDER       = GRALLOC_USAGE_HW_RENDER,
    USAGE_HW_2D           = GRALLOC_USAGE_HW_2D,
    USAGE_HW_COMPOSER     = GRALLOC_USAGE_HW_COMPOSER,
    USAGE_HW_VIDEO_ENCODER= GRALLOC_USAGE_HW_VIDEO_ENCODER,
    USAGE_HW_MASK         = GRALLOC_USAGE_HW_MASK,
    USAGE_CURSOR          = GRALLOC_USAGE_CURSOR,

    GRALLOC_USAGE_SW_READ_NEVER,GRALLOC_USAGE_SW_READ_RARELY,GRALLOC_USAGE_SW_READ_OFTEN 
    分别表示CPU不需要/很少会/经常会读这块GraphicBuffer,对于READ_OFTEN经常读的情况,gralloc模块应该考虑建读缓存了。 
    CPU写的三个标志类似。 
    当发觉GraphicBuffer操作起来速度慢时,就得看一下,是不是忘了配CPU的读写标志了。

    GRALLOC_USAGE_HW_TEXTURE 
    GRALLOC_USAGE_HW_RENDER 
    这两个标志分别表示需要GPU读与写,TEXTURE表示可以映射为一个OpenGL的纹理,RENDER表示可以作为OpenGL的渲染目标。 
    一般来说,gralloc分配的内存都是gpu可读写的,也不需要加这两个标志。

    GRALLOC_USAGE_HW_COMPOSER 
    这个表示这个GraphicBuffer可以由硬件合成器直接合成。

    GRALLOC_USAGE_HW_VIDEO_ENCODER 
    这个表示这个GraphicBuffer可以作为Video硬解码(一般是DSP)的输入输出对象。

    Gralloc

    gralloc模块需要实现如下三个设备及函数指针。

    内存分配

    typedef struct alloc_device_t {  
        struct hw_device_t common;
        int (*alloc)(struct alloc_device_t* dev,  
                int w, int h, int format, int usage,  
                buffer_handle_t* handle, int* stride);  
    
        int (*free)(struct alloc_device_t* dev,  
                buffer_handle_t handle);  
    } alloc_device_t;  

    内存共享

    typedef struct gralloc_module_t {  
        ......  
    
        int (*registerBuffer)(struct gralloc_module_t const* module,  
                buffer_handle_t handle);  
    
        int (*unregisterBuffer)(struct gralloc_module_t const* module,  
                buffer_handle_t handle);  
    
    
        int (*lock)(struct gralloc_module_t const* module,  
                buffer_handle_t handle, int usage,  
                int l, int t, int w, int h,  
                void** vaddr);  
    
        int (*unlock)(struct gralloc_module_t const* module,  
                buffer_handle_t handle);  
        ......  
    }

    显示

    typedef struct framebuffer_device_t {  
        struct hw_device_t common;  
        ......
        int (*setSwapInterval)(struct framebuffer_device_t* window, int interval);//设置刷新频率
        int (*setUpdateRect)(struct framebuffer_device_t* window,int left, int top, int width, int height);//设置更新区域,对于带缓存的LCD屏,可以在只传发生了变化的区域过去,此即局部刷新。
        int (*post)(struct framebuffer_device_t* dev, buffer_handle_t buffer);//送显
        ......
    } framebuffer_device_t;

    关于gralloc模块如何注册如何打开,可看老罗的博客: 
    http://blog.csdn.net/luoshengyang/article/details/7747932 
    这里面需要校正的是 hardware/libhardware/modules/gralloc下面的代码编译出来的是gralloc.default.so,即Android系统默认提供的。一般来说厂商不会用这个so。 
    如下是Arm为Mali系列gpu提供的开源gralloc代码的链接,相对而言更有参考意义。 
    http://malideveloper.arm.com/cn/develop-for-mali/drivers/open-source-mali-gpus-android-gralloc-module/ 
    Arm提供的代码里面包括自家的ump方案和标准的ion方案,一般而言,在Android4.2之后,普遍用的都是ion方案,ion内存共享方案可参考此博客的文章。 
    http://blog.csdn.net/qq160816/article/details/38082579 
    http://blog.csdn.net/qq160816/article/details/38299251

    我们只看一下这份代码里面送显的部分:

    
    static int fb_post(struct framebuffer_device_t* dev, buffer_handle_t buffer)
    {
        if (private_handle_t::validate(buffer) < 0)
        {
            return -EINVAL;
        }
    
        private_handle_t const* hnd = reinterpret_cast<private_handle_t const*>(buffer);
        private_module_t* m = reinterpret_cast<private_module_t*>(dev->common.module);
    
        if (m->currentBuffer)
        {
            m->base.unlock(&m->base, m->currentBuffer);
            m->currentBuffer = 0;
        }
    
        if (hnd->flags & private_handle_t::PRIV_FLAGS_FRAMEBUFFER)
        {
            m->base.lock(&m->base, buffer, private_module_t::PRIV_USAGE_LOCKED_FOR_POST, 
                    0, 0, m->info.xres, m->info.yres, NULL);
    
            const size_t offset = hnd->base - m->framebuffer->base;
            int interrupt;
            m->info.activate = FB_ACTIVATE_VBL;
            m->info.yoffset = offset / m->finfo.line_length;
    
    #ifdef STANDARD_LINUX_SCREEN
    #define FBIO_WAITFORVSYNC       _IOW('F', 0x20, __u32)
    #define S3CFB_SET_VSYNC_INT _IOW('F', 206, unsigned int)
            if (ioctl(m->framebuffer->fd, FBIOPAN_DISPLAY, &m->info) == -1) 
            {
                AERR( "FBIOPAN_DISPLAY failed for fd: %d", m->framebuffer->fd );
                m->base.unlock(&m->base, buffer); 
                return 0;
            }
    
            {
                // enable VSYNC
                interrupt = 1;
                if(ioctl(m->framebuffer->fd, S3CFB_SET_VSYNC_INT, &interrupt) < 0) 
                {
                    AERR( "S3CFB_SET_VSYNC_INT enable failed for fd: %d", m->framebuffer->fd );
                    return 0;
                }
                // wait for VSYNC
    #ifdef MALI_VSYNC_EVENT_REPORT_ENABLE
                gralloc_mali_vsync_report(MALI_VSYNC_EVENT_BEGIN_WAIT);
    #endif
                int crtc = 0;
                if(ioctl(m->framebuffer->fd, FBIO_WAITFORVSYNC, &crtc) < 0)
                {
                    AERR( "FBIO_WAITFORVSYNC failed for fd: %d", m->framebuffer->fd );
    #ifdef MALI_VSYNC_EVENT_REPORT_ENABLE
                    gralloc_mali_vsync_report(MALI_VSYNC_EVENT_END_WAIT);
    #endif
                    return 0;
                }
    #ifdef MALI_VSYNC_EVENT_REPORT_ENABLE
                gralloc_mali_vsync_report(MALI_VSYNC_EVENT_END_WAIT);
    #endif
                // disable VSYNC
                interrupt = 0;
                if(ioctl(m->framebuffer->fd, S3CFB_SET_VSYNC_INT, &interrupt) < 0) 
                {
                    AERR( "S3CFB_SET_VSYNC_INT disable failed for fd: %d", m->framebuffer->fd );
                    return 0;
                }
            }
    #else 
            /*Standard Android way*/
    #ifdef MALI_VSYNC_EVENT_REPORT_ENABLE
            gralloc_mali_vsync_report(MALI_VSYNC_EVENT_BEGIN_WAIT);
    #endif
            if (ioctl(m->framebuffer->fd, FBIOPUT_VSCREENINFO, &m->info) == -1) 
            {
                AERR( "FBIOPUT_VSCREENINFO failed for fd: %d", m->framebuffer->fd );
    #ifdef MALI_VSYNC_EVENT_REPORT_ENABLE
                gralloc_mali_vsync_report(MALI_VSYNC_EVENT_END_WAIT);
    #endif
                m->base.unlock(&m->base, buffer); 
                return -errno;
            }
    #ifdef MALI_VSYNC_EVENT_REPORT_ENABLE
            gralloc_mali_vsync_report(MALI_VSYNC_EVENT_END_WAIT);
    #endif
    #endif
    
            m->currentBuffer = buffer;
        } 
        else
        {
            void* fb_vaddr;
            void* buffer_vaddr;
    
            m->base.lock(&m->base, m->framebuffer, GRALLOC_USAGE_SW_WRITE_RARELY, 
                    0, 0, m->info.xres, m->info.yres, &fb_vaddr);
    
            m->base.lock(&m->base, buffer, GRALLOC_USAGE_SW_READ_RARELY, 
                    0, 0, m->info.xres, m->info.yres, &buffer_vaddr);
    
            memcpy(fb_vaddr, buffer_vaddr, m->finfo.line_length * m->info.yres);
    
            m->base.unlock(&m->base, buffer); 
            m->base.unlock(&m->base, m->framebuffer); 
        }
    
        return 0;
    }

    送显的核心代码是这一句:

            if (ioctl(m->framebuffer->fd, FBIOPAN_DISPLAY, &m->info) == -1) 

    再往下看得跟厂商的内核代码了,这里没代码略过。关于LCD显示原理看老罗那篇就好了。 
    调用gralloc模块的post函数不是惟一一种将图形内存送到LCD显示的方法,另一种方式是Overlay(hwcomposer的硬件合成)。

     

    展开全文
  • 共享内存 Bank冲突

    2020-12-08 11:01:49
    为了获得较高的内存带宽,共享存储器被划分为多个大小相等的存储器模块,称为bank,可以被同时访问。 如果跨越多个(假设n个)不同的内存bank的对里面的m个地址进行读取和写入的操作,这是可以被同时执行的,这样就...

    计算机图形学技术 见 计算机图形学技术

    GPU上的warp其实是软件上来说的,在CUDA架构中,一个warp指的是32个线程的集合,这些线程是“编织在一起”并以一致步调(lockstep)方式执行的。在程序的每一行,warp中的每个线程对不同的数据执行相同的指令。

    为了获得较高的内存带宽,共享存储器被划分为多个大小相等的存储器模块,称为bank,可以被同时访问。

    如果跨越多个(假设n个)不同的内存bank的对里面的m个地址进行读取和写入的操作,这是可以被同时执行的,这样就大大提高了整体的带宽 ,带宽可达到单独一个bank带宽的n倍。但是如果一个warp中的不同线程访问同一个bank中的不同的地址时,就会发生bank冲突。

    如果同一个warp访问的全都是同一个bank里面的不同字段,则就是处于最坏的情况。

    以上两种访问情况都不会产生bank冲突。下图会产生bank冲突:

    但是当一个warp的全部线程全都访问某个bank的同一个字段时不会冲突,比如下图,因为CUDA广播机制,会把该字段广播到其他所有线程:

    同理,当同一个字(word)中的不同字节(8 bit)被访问时,也不会产生bank冲突。

    参考文献:

    CUDA C programming guide

    CUDA by example

    the CUDA handbook

    https://blog.csdn.net/weixin_34185560/article/details/89224449

    https://developer.nvidia.com/blog/using-shared-memory-cuda-cc/

    展开全文
  • 共享内存:用于进程之间的数据传递,是system v版本中最高效的,但是它不会同步与互斥,所以常与信号量搭配使用。...图形理解共享内存:#include <sys/types.h>#include <sys/shm.h>1.创建...
  • 对于耦合式结构,GPU 没有独立的内存,与 GPU 共享系统内存,由 MMU 进行存储管理。 图形应用程序调用 OpenGL 或 Direct3D API 功能,将 GPU 作为协处理器使用。API 通过面向特殊 GPU 优化的图形设备驱动向 GPU 发送...
  • CUDA 之 共享内存

    2020-07-18 21:16:40
    对于许多当前可用的图形处理器,这个限制是每个块512个线程,那么我们如何使用基于线程的方法来添加两个大小大于512的向量?我们将不得不使用线程和块的组合来实现这一点。 很高兴你这么问。cuda
  • 在软件中,术语共享内存指可被多个进程存取的内存,一个进程是一段程序的单个运行实例。... 多媒体、图形领域广泛使用的基于 dma-buf 的共享内存。 CRIU 是用于 Linux 操作系统的软件工具。使用..
  • 基于Linux的同步共享内存的研究与实现 共享内存是L inux 中通信效率最高的...软件前置处理器图形用户界面与核心程序的进程间通信改进中得到成功应用, 既实现了同步共享内存, 又解决了消息堵塞的弊端, 改 善了通信效率。
  • 播放器中使用共享内存进行数据缓冲 编者:李国帅 qq:9611153 微信lgs9611153 时间:2009-06-18 背景原因: 这是一个很久之前的设计,今天拿出来,整理个图形。 使用rtsp进行接收,使用ffmpeg进行解码,使用...
  • UltraLAB H490系列是一款配备intel第7代至尊处理器(4~18核,4.5Ghz以上)、最大512GB内存容量、双显卡架构的多核与超高频兼备的图形工作站H490完美适合那些对单核频率极高同时具备一定核数做并行计算要求的中大规模...
  • 情景:集成显卡 ...解说:上图总可用图形内存 = 图2中备用 + 硬件保留(768mb) 共享系统内存 =图2中备用 + 硬件保留(768mb)- 专用视频内存(256mb) 假如它实际真用不到768M,那也不理了,不想为了...
  • 通过运行Windows 10的"任务管理器"中观察,发现有一个"Windows 音频设备图形间隔"的进程占用的内存比较高。通过一些尝试,可以使用下面的方法处理。现将处理方法共享如下。1.使用鼠标右键点击Windows 10的开始菜单,...
  • SurfaceFlinger是个服务端,而每个请求服务的应用程序都对应一个Client端,Surface绘图由Client进行,而由SurfaceFlinger对所有Client绘制的图合成进行输出,那么这两者是如何共享这块图形缓冲区的内存呢?...
  • 内存

    2020-05-19 10:33:57
    它是线程共享的,堆中对象都需要考虑线程安全的问题 有垃圾回收机制 堆内存溢出 虽然有垃圾回收机制,但如果不断有新的对象产生,而且这些新产生的对象又在使用,可能会造成堆内存溢出 堆内存诊断 jps 工具 查看...
  • Ligra 是用于共享内存的轻量级图形框架。它特别适用于实现在迭代中只有顶点的子集被处理的平行图像遍历算法。此项目的成立基于这样一个事实 —— 最大的公共真实世界图形适用于共享内存中。当图形适配共享内存时,...
  • 内存数据库

    千次阅读 2014-02-23 19:53:25
    目前在国内电信行业,内存数据库已经是普遍...内存数据库是和我们经常使用的共享内存类似的技术产品,比较专业,对客户直接的感受就是,给了他们一个可以图形化维护的工具,感觉上稳定性和扩展性比各个厂商土鳖的共享
  • 对于与英特尔服务器GPU一样令人兴奋的是,一些令人兴奋的英特尔Linux图形驱动细节也被披露出来。H3C服务器GPU是他们首款面向数据中心的独立GPU。这款首款基于Gen12 Xe-LP的dGPU产品拥有128位的流水线和8GB的LPDDR4...
  • 对于与英特尔服务器GPU一样令人兴奋的是,一些令人兴奋的英特尔Linux图形驱动细节也被披露出来。H3C服务器GPU是他们首款面向数据中心的独立GPU。这款首款基于Gen12 Xe-LP的dGPU产品拥有128位的流水线和8GB的LPDDR4...
  • 现代 Linux 系统需要大量的图形内存来存储帧缓存、纹理、顶点和其他与图形相关的数据。考虑到许多数据的动态特性, 有效地管理图形内存对图形栈至关重要, 并在 DRM 基础结构中发挥中心作用。    GEM 主要是对 ...
  • 去除WPF中3D图形的锯齿 原文:去除WPF中3D图形的锯齿 理论上讲PC在计算3D图形的时候是无法避免不出现锯齿的,...这样会大量消耗显卡的存储空间或是从内存共享的存储空间,导致程序的整体性能降低,但...
  • 此外,根据不同内存的不同特征,开发了一种改进的方法,该方法利用GPU中的共享内存代替全局内存,从而进一步提高了效率。 实验结果证明,两种新算法在计算速度方面均优于传统的基于OpenCV算法。
  • 我们优化了序列数据组织以消除内存访问的带宽瓶颈,设计了一种内存分配和重用策略来充分利用GPU的有限内存,提出了一种新的修改运行长度编码(MRLE)方案可减少内存消耗,并使用高性能共享内存来加快I / O操作。 与...
  • ②而同一个进程中的所有线程都在同一内存空间工作,这些线程可以共享同一块内存和系统资源。线程的创建:1)通过继承Thread类来创建线程:①创建一个继承Thread类的类;②在创建的Thread子类中重写run()...
  • Android硬件加速(二)-Render... 第二阶段:CPU将数据上传(共享或者拷贝)给GPU,PC上一般有显存一说,但是ARM这种嵌入式设备内存一般是GPU、CPU共享内存 第三阶段:通知GPU渲染,一般而言,真机不会阻塞等待GPU渲染
  • 内存:大于2G 命令(free -g) 磁盘空间:100G 命令(df -h) 共享空间:大于等于2G 命令(df -h) 内核版本:64位 命令(uname -m) 注意:图形化安装,必须Linux系统安装了图形化 方法一:图形化安装 参考视频...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 661
精华内容 264
关键字:

共享图形内存