SurfaceFlinger GraphicBuffer Memory Sharing Buffer Mechanism

http://blog.csdn.net/andyhuabing/article/details/7489776

I was busy in the first two weeks and I didn’t have time to write this blog.
GraphicBuffer Yes Surface 系统中用于GDI内存共享缓冲区管理类,封装了与硬件相关的细节,从而简化应用层的处理逻辑

SurfaceFlinger是个服务端,而每个请求服务的应用程序Both correspond to a Client. The Surface drawing is performed by the Client, and SurfaceFlinger synthesizes all the graphics drawn by the Client for output. So how do the two share the memory of this graphics buffer? The brief is to use mmap/ummap, then how are these structured in the android system?

frameworks\base\include\ui\GraphicBuffer.h 类定义:
class GraphicBuffer
  : public EGLNativeBase<
      android_native_buffer_t, 
      GraphicBuffer, 
      LightRefBase > , public Flattenable

EGLNativeBase 是一个模板类:
template
class EGLNativeBase : public NATIVE_TYPE, public REF
类GraphicBuffer  继承LightRefBase支持轻量级引用计数控制
   派生Flattenable 用于数据序列化给Binder进行传输
   
我们来看下android_native_buffer.h 文件,这个android_native_buffer_t 结构:
typedef struct android_native_buffer_t
{
#ifdef __cplusplus
android_native_buffer_t() {
common.magic = ANDROID_NATIVE_BUFFER_MAGIC;
common.version = sizeof(android_native_buffer_t);
common.version = sizeof(android_native_buffer_t);
common. ));
    }
#endif

    struct android_native_base_t common;

    int width;
    int height;
    int stride;
    int forma t;
    int usage;
    
    void* reserved[2];

    buffer_handle_t handle;

    void* reserved_proc[8];
} android_native_buffer_t;

注意这里有个关键的变量: buffer_handle_t handle; 这个就是显示内存分配与管理的私有数据结构

1、 native_handle_t 对private_handle_t 的包裹

typedef struct
{
Int version; /* sizeof(native_handle_t) */
int numFds; /* number of file-descriptors at &data[0] */
int numInts; numInts at */data of numFds/*
int data[0]; /* numFds + numInts ints */ Here is how to use GCC’s indefinite parameter transfer< br> } native_handle_t;

/* keep the old definition for backward source-compatibility */
typedef native_handle_t native_handle;
typedef const native_handle* buffer_handle_t;

< br> native_handle_t is an upper-level abstract data structure used for inter-process transfer. For Gralloc, its content is:

data[0] 指向具体对象的内容,其中:
  static const int sNumInts = 8;
  static const int sNumFds = 1;

sNumFds=1表示有一个文件句柄:fd
sNumInts= 8表示后面跟了8个INT型的数据:magic,flags,size,offset,base,lockState,writeOwner,pid;

< br> Because the upper system does not care about the specific content of data in buffer_handle_t. Passing buffer_handle_t(native_handle_t)
between processes is actually passing the content of this handle to the client. Reading readNativeHandle @Parcel.cpp through Binder on the client side generates a new native_handle.

native_handle* Parcel::readNativeHandle() const
{
    int numFds, numInts;
    err = readInt32(&numFds);
    err = readInt32(&numInts);

    native_handle* h = native_handle_create(numFds, numInts);
    for (int i=0 ; err==NO_ERROR && i        h->data[i] = dup(readFileDescriptor());
if (h->data[i] <0) err = BAD_VALUE;
}< br> err = read(h->data + numFds, sizeof(int)*numInts);

}
When the native_handle of the client is constructed here, dup processing is performed on fd (different processes ), other direct read and copy use.
magic, flags, size, offset, base, lockState, writeOwner, pid, etc. are copied to the client, so as to obtain the corresponding information for buffer sharing.


For fd writing binder special mark BINDER_TYPE_FD: Tell the Binder driver that this is an fd descriptor

status_t Parcel::writeFil eDescriptor(int fd)
{
flat_binder_object obj;
obj.type = BINDER_TYPE_FD;
obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
d d;obj.br> cookie.handle = (void*)0;
return writeObject(obj, true);
}


2、GraphicBuffer 内存分配
三种分配方式:
  GraphicBuffer();

  // creates w * h buffer
  GraphicBuffer(uint32_t w, uint32_t h, PixelFormat format, uint32_t usage);

  // create a buffer from an existing handle
  GraphicBuffer(uint32_t w, uint32_t h, PixelFormat format, uint32_t usage,
uint32_t stride, native_handle_t* handle, bool keepOwnership);

In fact, it is through the function: initSize
status_t GraphicBuffer::initSize(uint32_t w, uint32_t h, PixelFormat format,
uint32_t reqUsage)
{
If (format == PIXEL_FORMAT_RGBX_8888)
FORM_RGB_ AT format = PIX A_8888;

    GraphicBufferAllocator& allocator = GraphicBufferAllocator::get();
    status_t err = allocator.alloc(w, h, format, reqUsage, &handle, &stride);
    if (err == NO_ERROR) {
this->width = w;
this->height = h;
this->format = format;
this->usage = reqUsage;
​​​​0;
    }
    return err;
}

利用GraphicBufferAllocator 类分配内存:
首先Load the libGralloc.hwXX.so dynamic library, allocate a memory for display, and shield the difference between different hardware platforms.
GraphicBufferAllocator::GraphicBufferAllocator()
: mAllocDev(0)
{
hw_module_t const* module;
int err = hw_get_module(GRALL_HARDWARE_MODE_MODE_MODE_MODE_HARDWARE,
(err, “FATAL: can’t find the %s module”, GRALLOC_HARDWARE_MODULE_ID);
If (err == 0) {
Gralloc_open(module, &mAllocDev);
}
}

分配方式有两种:
status_t GraphicBufferAllocator::alloc(uint32_t w, uint32_t h, PixelFormat format,
      int usage, buffer_handle_t* handle, int32_t* stride)
{< br> if (usage & GRALLOC_USAGE_HW_MASK) {
err = mAllocDev->alloc(mAllocDev, w, h, format, usage, handle, stride);
}} else {
er_alloc: er_handle_ er_ w, h, format, usage, handle, stride);
    }
    …
}

具体的内存分配方式如下:

3、共享句柄的传递
frameworks\base\libs\surfaceflinger_client\ISurface.cpp

客户端请求处理:BpSurface class:
virtual sp requestBuffer(int bufferIdx, int usage)
{
Parcel data, reply;
Data.writeInterfaceToken(ISurface::getInterfaceDescriptor());
data.writeInt32(bufferIdx);
data.writeInt32(usage);
remote()->transact(REQUEST_BUFFER, data, &reply);< br> sp buffer = new GraphicBuffer();
reply.read(*buffer);
return buffer;

}

here use sp buffer = new GraphicBuffer(); Then reply.read(*buffer) unflatten the data into this buffer and returns the new GraphicBuffer object locally. Where is the data written into it?
 
服务端呼应处理: BnSurface 类:
status_t BnSurface::onTransact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    switch(code ) {
Case REQUEST_BUFFER: {
CHECK_INTERFACE(ISurface, data, reply);
Int bufferIdx = data. ReadInt32();
data. ReadInt32();
Data.readInt32();
s ReadInt32();
data.readInt32();
style=”color:rgb(0,153,0)”>sp buffer(requestBuffer(bufferIdx, usage));
If (buffer == NULL)
Return BAD_VALUE;
return reply->write(*buffer);
}}}}}}}}}}}}}}}}}}}}}}}}}}}}

The requestBuffer function server calling process:
requestBuffer @ surfaceflinger\Layer.cpp
sp Layer::requestBuffer(int index, int usage)
{
buffer = new GraphicBuffer(w, h, mFormat, effectiveUsage);

< /span>return buffer;
}

如此的话,客户端利用new 的GraphicBuffer() 对象从Parcel中读取native_handle 对象及其内容,而在服务端由同样由requestBuffer 请求返回一个真正的GraphicBuffer Object. So how are these two data serialized and transmitted?
flatten @ GraphicBuffer.cpp
status_t GraphicBuffer::flatten(void* buffer, size_t size,
int fds[], size_t count) const
{

if (handle) {
buf[6] = handle->numFds;
buf[7] = handle->numInts;
native_handle_t const* const h = handle;
memcpy(fds, h->data, h->numFds*sizeof(int));
memcpy(&buf[8], h->data, h->numFds h->numInts*sizeof(int));
}
The function of flatten is to write the handle variable information of GraphicBuffer into the Parcel sentence, and the receiving end uses unflatten read
status_t GraphicBuffer::unflatten(void const* buffer, size_t size,
int fds[], size_t count)
{
native_handle* h = native_handle_create(numFds, numInts);
memcpy(h->data, numFds*sizeof(int));
memcpy(h->data + numFds, &buf[8], numInts*)
> Handle = h;
}
After the above operations, an equivalent GraphicBuffer object is constructed on the client. The following将继续讲两者如何操作相同的内存块

4、共享内存的管理– Graphic Mapper 功能

How to share memory between two processes, and how to obtain shared memory? Mapper does this. Two pieces of information need to be used: the shared buffer device handle and the offset during allocation. When the client needs to operate a piece of shared memory, first register a buffer_handle_t with registerBuffer, and then use the lock function to obtain the first address of the buffer for drawing, that is, use Lock and unlock map memory for use.


Using lock(mmap ) And unlock (ummap) to map a buffer.
The important code is as follows: mapper.cpp
static int gralloc_map(gralloc_module_t const* module,
buffer_handle_t handle,
void** vaddr){
private_handle_t* hnd = (private_handle_t*)handle;
void* mappedAddress = mmap(0, size,
PROT_READ|PROT_WRITE, MAP_SHARED, hnd->fd, 0);

If (mappedAddress == MAP_FAILED) {
“Could LOGE not mmap %s”, strerror(errno));
return -errno;
}
hnd->base = intptr_t(mappedAddress) + hnd->offset;
addr = (*v )hnd->base;
return 0;
}

static int gralloc_unmap(gralloc_module_t const* module,
buffer_handle_t handle){
private_handle_t* hnd = (private_handle_t*)handle;
nd voidh base-> br> size_t size = h nd->size;
munmap(base, size);
hnd->base = 0;
return 0;
}
利用buffer_handle_t与private_handle_t句柄完成共享进程数据的共享:

总结:
Android uses shared memory to manage display-related buffers in this section. He designed two layers, the upper layer is the buffer management agency GraphicBuffer,
And related native_buffer_t, the lower layer is the allocation management of the specific buffer and the buffer itself. The upper object can be passed through Binder frequently, and the buffer itself is not passed between processes, but mmap is used to obtain the mapping address pointing to the common physical memory.

Top
13
Dislike
0

My similar articles

  • s p&wp’s three axes2015-11-09
  • Android standby function flow analysis2015-11-04
  • bootchart instructions and code analysis2014-04-14
  • android-home Key and launcher startup process analysis2013-11-01
  • android recovery main system code analysis2013-07-05
  • Android can’t receive boot broadcast issue2015-11- 06
  • •< /span>Character encoding problem2015-11-04
  • The realization process of sp and wp~~2014-02-28
  • android SystemUI process analysis2013-10-18
  • < li style="list-style:none; line-height:30px">android – compile The difference between different libraries and compiled scripts2013-03-18

more articles< /div>

Guess what you are looking for

2016 software test system integration project management engineer -In the morning, the training video course of real question analysis over the years
2016 software test system integration project management engineer video tutorial Explaining the basic knowledge (below)
2016 software test system integration project management engineer video tutorial to explain the basic knowledge (on)
Information System Project Manager Advanced Exam Video Tutorial Course
Linux operating system and common commands practical and advanced

GraphicBuffer synchronization mechanism in Android-Fence
GraphicBuffer synchronization mechanism in Android Fence
in Android GraphicBuffer synchronization mechanism-Fence
GraphicBuffer synchronization mechanism in Android Fence
GraphicBuffer synchronization mechanism in Android-Fence

View comments
3rd floor CyberLogic 2014-09-29 11:11 published [Reply]
The analysis is good, haha
2nd floor goodtalent Posted at 17:33 2014-03-12 [Reply]
Can’t read it.

Re: andyhuabing2014-03-18 17:25Published[ Reply]
Reply to goodtalent: Look at it and you’ll understand, haha< /dd>
1st floor houyizi3132013-04-12 10:39 [Reply] [quote] [Report]
Brother: You understand the esoteric! I have a problem! Also need to study the Android test display mechanism! I want to know which file is the source code that controls the display size of the video when Android plays the video on the local SD card? That part? Because at the beginning, the android layer is sealed, and the display system is also layered. I even have a clear distribution of these layers!所以难到了……非常感谢……

Re:  andyhuabing 2014-03-18 17:24发表 [回复] [引用] [举报]
回复houyizi337825770:本地播放是使用JAVA类 : MediaPlayer.java /** * Sets the {@link SurfaceHolder} to use for displaying the video * portion of the media. * * Either a surface holder or surface must be set if a display or video sink * is needed. Not calling this method or {@link #setSurface(Surface)} * when playing back a video will result in only the audio track being played. * A null surface holder or surface will result in only the audio track being * played. * * @param sh the SurfaceHolder to use for video display */ public static final int KEY_PARAMETER_VIDEO_POSITION_INFO = 2000; public void setDisplay(SurfaceHolder sh) { 通过SurfaceView的大小指定视频大小的。其实就是将图形层挖了一个“洞”将视频透出来了。

前两周比较忙,没时间写下这篇博客
GraphicBuffer 是 Surface 系统中用于GDI内存共享缓冲区管理类,封装了与硬件相关的细节,从而简化应用层的处理逻辑

SurfaceFlinger是个服务端,而每个请求服务的应用程序都对应一个Client端,Surface绘图由Client进行,而由SurfaceFlinger对所有Client绘制的图合成进行输出,那么这两者是如何共享这块图形缓冲区的内存呢?简要之就是利用mmap/ummap,那么这些在android系统中是如何构架完成的呢?

frameworks\base\include\ui\GraphicBuffer.h 类定义:
class GraphicBuffer
  : public EGLNativeBase<
      android_native_buffer_t, 
      GraphicBuffer, 
      LightRefBase >, public Flattenable

EGLNativeBase 是一个模板类:
template
class EGLNativeBase : public NATIVE_TYPE, public REF
类 GraphicBuffer  继承LightRefBase支持轻量级引用计数控制
   派生 Flattenable 用于数据序列化给Binder进行传输
   
我们来看下 android_native_buffer.h 文件,这个 android_native_buffer_t 结构:
typedef struct android_native_buffer_t
{
#ifdef __cplusplus
    android_native_buffer_t() { 
        common.magic = ANDROID_NATIVE_BUFFER_MAGIC;
        common.version = sizeof(android_native_buffer_t);
        memset(common.reserved, 0, sizeof(common.reserved));
    }
#endif

    struct android_native_base_t common;

    int width;
    int height;
    int stride;
    int forma t;
    int usage;
    
    void* reserved[2];

    buffer_handle_t handle;

    void* reserved_proc[8];
} android_native_buffer_t;

注意这里有个关键的变量: buffer_handle_t handle; 这个就是显示内存分配与管理的私有数据结构

1、 native_handle_t 对 private_handle_t 的包裹

typedef struct
{
    int version;        /* sizeof(native_handle_t) */
    int numFds;         /* number of file-descriptors at &data[0] */
    int numInts;        /* number of ints at &data[numFds] */
    int data[0];        /* numFds + numInts ints */  这里是利用GCC的无定参数传递的写法
} native_handle_t;

/* keep the old definition for backward source-compatibility */
typedef native_handle_t native_handle;
typedef const native_handle* buffer_handle_t;

native_handle_t 是上层抽象的用于进程间传递的数据结构,对于 Gralloc 而言其内容就是:

data[0] 指向具体对象的内容,其中:
  static const int sNumInts = 8;
  static const int sNumFds = 1;

sNumFds=1表示有一个文件句柄:fd
sNumInts= 8表示后面跟了8个INT型的数据:magic,flags,size,offset,base,lockState,writeOwner,pid;

由于在上层系统不要关心buffer_handle_t中data的具体内容。在进程间传递buffer_handle_t(native_handle_t)
句柄是其实是将这个句柄内容传递到Client端。在客户端通过Binder读取readNativeHandle @Parcel.cpp新生成一个native_handle。

native_handle* Parcel::readNativeHandle() const
{
    int numFds, numInts;
    err = readInt32(&numFds);
    err = readInt32(&numInts);

    native_handle* h = native_handle_create(numFds, numInts);
    for (int i=0 ; err==NO_ERROR && i         h->data[i] = dup(readFileDescriptor());
        if (h->data[i] < 0) err = BAD_VALUE;
    }
    err = read(h->data + numFds, sizeof(int)*numInts);

}
这里构造客户端的native_handle时,对于fd进行dup处理(不同进程),其它的直接读取复制使用.
magic,flags,size,offset,base,lockState,writeOwner,pid 等复制到了客户端,从而为缓冲区共享获取到相应的信息


对于fd的写入binder特殊标志 BINDER_TYPE_FD:告诉Binder驱动这是一个fd描述符

status_t Parcel::writeFileDe scriptor(int fd)
{
    flat_binder_object obj;
    obj.type = BINDER_TYPE_FD;
    obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
    obj.handle = fd;
    obj.cookie = (void*)0;
    return writeObject(obj, true);
}


2、GraphicBuffer 内存分配
三种分配方式:
  GraphicBuffer();

  // creates w * h buffer
  GraphicBuffer(uint32_t w, uint32_t h, PixelFormat format, uint32_t usage);

  // create a buffer from an existing handle
  GraphicBuffer(uint32_t w, uint32_t h, PixelFormat format, uint32_t usage,
          uint32_t stride, native_handle_t* handle, bool keepOwnership);

其实最终都是通过函数:initSize
status_t GraphicBuffer::initSize(uint32_t w, uint32_t h, PixelFormat format,
        uint32_t reqUsage)
{
    if (format == PIXEL_FORMAT_RGBX_8888)
        format = PIXEL_FORMAT_RGBA_88 88;

    GraphicBufferAllocator& allocator = GraphicBufferAllocator::get();
    status_t err = allocator.alloc(w, h, format, reqUsage, &handle, &stride);
    if (err == NO_ERROR) {
        this->width  = w;
        this->height = h;
        this->format = format;
        this->usage  = reqUsage;
        mVStride = 0;
    }
    return err;
}

利用 GraphicBufferAllocator 类分配内存:
首先加载 libGralloc.hwXX.so 动态库,分配一块用于显示的内存,屏蔽掉不同硬件平台的区别。
GraphicBufferAllocator::GraphicBufferAllocator()
    : mAllocDev(0)
{
    hw_module_t const* module;
    int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module);
    LOGE_IF(err, “FATAL: can’t find the %s module”, GRALLOC_HARDWARE_MODULE_ID);
    if (err == 0) {
        gralloc_open(module, &mAllocDev);
    }
}

分配方式有两种:
status_t GraphicBufferAllocator::alloc(uint32_t w, uint32_t h, PixelFormat format,
      int usage, buffer_handle_t* handle, int32_t* stride)
{
    if (usage & GRALLOC_USAGE_HW_MASK) {
        err = mAllocDev->alloc(mAllocDev, w, h, format, usage, handle, stride);
    } else {
        err = sw_gralloc_handle_t::alloc(w, h, format, usage, handle, stride);
    }
    …
}

具体的内存分配方式如下:

3、共享句柄的传递
frameworks\base\libs\surfaceflinger_client\ISurface.cpp

客户端请求处理:BpSurface 类:
  virtual sp requestBuffer(int bufferIdx, int usage)
  {
      Parcel data, reply;
      data.writeInterfaceToken(ISurface::getInterfaceDescriptor());
      data.writeInt32(bufferIdx);
      data.writeInt32(usage);
      remote()->transact(REQUEST_BUFFER, data, &reply);
      sp buffer = new GraphicBuffer();
      reply.read(*buffer);
      return buffer;

  }
  
 这里利用 sp buffer = new GraphicBuffer(); 然后reply.read(*buffer)将数据利用 unflatten反序化到这个buffer中并返回这个本地new出来的GraphicBuffer对象,而这个数据是在哪里写入进去的呢?
 
服务端呼应处理: BnSurface 类:
status_t BnSurface::onTransact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    switch(code) {
        case REQUEST_BUFFER: {
            CHECK_INTERFACE(ISurface, data, reply);
            int bufferIdx = data.readInt32();
            int usage = data.readInt32();
            sp buffer(requestBuffer(bufferIdx, usage));
            if (buffer == NULL)
                return BAD_VALUE;
            return reply->write(*buffer);
        }  
 
requestBuffer函数服务端调用流程:
requestBuffer @ surfaceflinger\Layer.cpp
sp Layer::requestBuffer(int index, int usage)
{
buffer = new GraphicBuffer(w, h, mFormat, effectiveUsage);

return buffer;
}

如此的话,客户端利用new 的 GraphicBuffer() 对象从 Parcel中读取 native_handle 对象及其内容,而在服务端由同样由 requestBuffer 请求返回一个真正的GraphicBuffer对象。那么这两个数据如何序列化传递的呢?
flatten @ GraphicBuffer.cpp
status_t GraphicBuffer::flatten(void* buffer, size_t size,
        int fds[], size_t count) const
{

    if (handle) {
        buf[6] = handle->numFds;
        buf[7] = handle->numInts;
        native_handle_t const* const h = handle;
        memcpy(fds,     h->data,             h->numFds*sizeof(int));
        memcpy(&buf[8], h->data + h->numFds, h->numInts*sizeof(int));
    }
flatten的职能就是将GraphicBuffer的handle变量信息写到Parcel句中,接收端利用unflatten读取
status_t GraphicBuffer::unflatten(void const* buffer, size_t size,
        int fds[], size_t count)
{
        native_handle* h = native_handle_create(numFds, numInts);
        memcpy(h->data,          fds,     numFds*sizeof(int));
        memcpy(h->data + numFds, &buf[8], numInts*sizeof(int));
        handle = h;
}
经过以上操作,在客户端构造了一个对等的 GraphicBuffer对象,下面将继续讲两者如何操作相同的内存块

4、共享内存的管理 — Graphic Mapper 功能

两个进程间如何共享内存,如何获取到共享内存? Mapper就是干这个得。需要利用到两个信息:共享缓冲区设备句柄,分配时的偏移量.客户端需要操作一块共享内存时,首先利用 registerBuffer 注册一个 buffer_handle_t,然后利用lock函数获取缓冲区首地址进行绘图,即利用lock及unlock对内存进行映射使用。


利用lock(mmap)及unlock(ummap)进行一个缓冲区的映射。
重要的代码如下:mapper.cpp 
static int gralloc_map(gralloc_module_t const* module,
      buffer_handle_t handle,
      void** vaddr){
     private_handle_t* hnd = (private_handle_t*)handle;
void* mappedAddress = mmap(0, size,
              PROT_READ|PROT_WRITE, MAP_SHARED, hnd->fd, 0);

      if (mappedAddress == MAP_FAILED) {
          LOGE(“Could not mmap %s”, strerror(errno));
          return -errno;
      }
      hnd->base = intptr_t(mappedAddress) + hnd->offset;
      *vaddr = (void*)hnd->base;      
     return 0;
}

static int gralloc_unmap(gralloc_module_t const* module,
      buffer_handle_t handle){
      private_handle_t* hnd = (private_handle_t*)handle;
      void* base = (void*)hnd->base;
      size_t size = h nd->size;
      munmap(base, size);
      hnd->base = 0;
      return 0;
}
利用buffer_handle_t与private_handle_t句柄完成共享进程数据的共享:

总结:
Android在该节使用了共享内存的方式来管理与显示相关的缓冲区,他设计成了两层,上层是缓冲区管理的代理机构GraphicBuffer,
及其相关的native_buffer_t,下层是具体的缓冲区的分配管理及其缓冲区本身。上层的对象是可以在经常间通过Binder传递的,而在进程间并不是传递缓冲区本身,而是使用mmap来获取指向共同物理内存的映射地址。

13

0

我的同类文章

  • sp&wp 的三板斧2015-11-09
  • Android 待机功能流程分析2015-11-04
  • bootchart 使用说明及代码分析2014-04-14
  • android – home键及launcher启动流程分析2013-11-01
  • android recovery 主系统代码分析2013-07-05
  • Android 无法接收开机广播的问题2015-11-06
  • 字符编码问题2015-11-04
  • sp和wp的实现过程~~2014-02-28
  • android SystemUI 流程分析2013-10-18
  • android — 编译不同库及编译脚本之区别2013-03-18

更多文章

前两周比较忙,没时间写下这篇博客
GraphicBuffer 是 Surface 系统中用于GDI内存共享缓冲区管理类,封装了与硬件相关的细节,从而简化应用层的处理逻辑

SurfaceFlinger是个服务端,而每个请求服务的应用程序都对应一个Client端,Surface绘图由Client进行,而由SurfaceFling er对所有Client绘制的图合成进行输出,那么这两者是如何共享这块图形缓冲区的内存呢?简要之就是利用mmap/ummap,那么这些在android系统中是如何构架完成的呢?

frameworks\base\include\ui\GraphicBuffer.h 类定义:
class GraphicBuffer
  : public EGLNativeBase<
      android_native_buffer_t, 
      GraphicBuffer, 
      LightRefBase >, public Flattenable

EGLNativeBase 是一个模板类:
template
class EGLNativeBase : public NATIVE_TYPE, public REF
类 GraphicBuffer  继承LightRefBase支持轻量级引用计数控制
   派生 Flattenable 用于数据序列化给Binder进行传输
   
我们来看下 android_native_buffer.h 文件,这个 android_native_buffer_t 结构:
typedef struct android_native_buffer_t
{
#ifdef __cplusplus
    android_native_buffer_t() { 
        common.magic = ANDROID_NATIVE_BUFFER_MAGIC;
        common.version = sizeof(android_native_buffer_t);
        memset(common.reserved, 0, sizeof(common.reserved));
    }
#endif

    struct android_native_base_t common;

    int width;
    int height;
    int stride;
    int forma t;
    int usage;
    
    void* reserved[2];

    buffer_handle_t handle;

    void* reserved_proc[8];
} android_native_buffer_t;

注意这里有个关键的变量: buffer_handle_t handle; 这个就是显示内存分配与管理的私有数据结构

1、 native_handle_t 对 private_handle_t 的包裹

typedef struct
{
    int version;        /* sizeof(native_handle_t) */
    int numFds;         /* number of file-descriptors at &data[0] */
    int numInts;        /* number of ints at &data[numFds] */
    int data[0];        /* numFds + numInts ints */  这里是利用GCC的无定参数传递的写法
} native_handle_t;

/* keep the old definition for backward source-compatibility */
typedef native_handle_t native_handle;
typedef const native_handle* buffer_handle_t;

native_handle_t 是上层抽象的用于进程间传递的数据结构,对于 Gralloc 而言其内容就是:

data[0] 指向具体对象的内容,其中:
  static const int sNumInts = 8;
  static const int sNumFds = 1;

sNumFds=1表示有一个文件句柄:fd
sNumInts= 8表示后面跟了8个INT型的数据:magic,flags,size,offset,base,lockState,writeOwner,pid;

由于在上层系统不要关心buffer_handle_t中data的具体内容。在进程间传递buffer_handle_t(native_handle_t)
句柄是其实是将这个句柄内容传递到Client端。在客户端通过Binder读取readNativeHandle @Parcel.cpp新生成一个native_handle。

native_handle* Parcel::readNativeHandle() const
{
    int numFds, numInts;
    err = readInt32(&numFds);
    err = readInt32(&numInts);

    native_handle* h = native_handle_create(numFds, numInts);
    for (int i=0 ; err==NO_ERROR && i         h->data[i] = dup(readFileDescriptor());
        if (h->data[i] < 0) err = BAD_VALUE;
    }
    err = read(h->data + numFds, sizeof(int)*numInts);

}
这里构造客户端的native_handle时,对于fd进行dup处理(不同进程),其它的直接读取复制使用.
magic,flags,size,offset,base,lockState,writeOwner,pid 等复制到了客户端,从而为缓冲区共享获取到相应的信息


对于fd的写入binder特殊标志 BINDER_TYPE_FD:告诉Binder驱动这是一个fd描述符

status_t Parcel::writeFileDescr iptor(int fd)
{
    flat_binder_object obj;
    obj.type = BINDER_TYPE_FD;
    obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
    obj.handle = fd;
    obj.cookie = (void*)0;
    return writeObject(obj, true);
}


2、GraphicBuffer 内存分配
三种分配方式:
  GraphicBuffer();

  // creates w * h buffer
  GraphicBuffer(uint32_t w, uint32_t h, PixelFormat format, uint32_t usage);

  // create a buffer from an existing handle
  GraphicBuffer(uint32_t w, uint32_t h, PixelFormat format, uint32_t usage,
          uint32_t stride, native_handle_t* handle, bool keepOwnership);

其实最终都是通过函数:initSize
status_t GraphicBuffer::initSize(uint32_t w, uint32_t h, PixelFormat format,
        uint32_t reqUsage)
{
    if (format == PIXEL_FORMAT_RGBX_8888)
        format = PIXEL_FORMAT_RGBA_8888;< br>

    GraphicBufferAllocator& allocator = GraphicBufferAllocator::get();
    status_t err = allocator.alloc(w, h, format, reqUsage, &handle, &stride);
    if (err == NO_ERROR) {
        this->width  = w;
        this->height = h;
        this->format = format;
        this->usage  = reqUsage;
        mVStride = 0;
    }
    return err;
}

利用 GraphicBufferAllocator 类分配内存:
首先加载 libGralloc.hwXX.so 动态库,分配一块用于显示的内存,屏蔽掉不同硬件平台的区别。
GraphicBufferAllocator::GraphicBufferAllocator()
    : mAllocDev(0)
{
    hw_module_t const* module;
    int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module);
    LOGE_IF(err, “FATAL: can’t find the %s module”, GRALLOC_HARDWARE_MODULE_ID);
    if (err == 0) {
        gralloc_open(module, &mAllocDev);
    }
}

分配方式有两种:
status_t GraphicBufferAllocator::alloc(uint32_t w, uint32_t h, PixelFormat format,
      int usage, buffer_handle_t* handle, int32_t* stride)
{
    if (usage & GRALLOC_USAGE_HW_MASK) {
        err = mAllocDev->alloc(mAllocDev, w, h, format, usage, handle, stride);
    } else {
        err = sw_gralloc_handle_t::alloc(w, h, format, usage, handle, stride);
    }
    …
}

具体的内存分配方式如下:

3、共享句柄的传递
frameworks\base\libs\surfaceflinger_client\ISurface.cpp

客户端请求处理:BpSurface 类:
  virtual sp requestBuffer(int bufferIdx, int usage)
  {
      Parcel data, reply;
      data.writeInterfaceToken(ISurface::getInterfaceDescriptor());
      data.writeInt32(bufferIdx);
      data.writeInt32(usage);
      remote()->transact(REQUEST_BUFFER, data, &reply);
      sp buffer = new GraphicBuffer();
      reply.read(*buffer);
      return buffer;

  }
  
 这里利用 sp buffer = new GraphicBuffer(); 然后reply.read(*buffer)将数据利用 unflatten反序化到这个buffer中并返回这个本地new出来的GraphicBuffer对象,而这个数据是在哪里写入进去的呢?
 
服务端呼应处理: BnSurface 类:
status_t BnSurface::onTransact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    switch(code) {
        case REQUEST_BUFFER: {
            CHECK_INTERFACE(ISurface, data, reply);
            int bufferIdx = data.readInt32();
            int usage = data.readInt32();
            sp buffer(requestBuffer(bufferIdx, usage));
            if (buffer == NULL)
                return BAD_VALUE;
            return reply->write(*buffer);
        }  
 
requestBuffer函数服务端调用流程:
requestBuffer @ surfaceflinger\Layer.cpp
sp Layer::requestBuffer(int index, int usage)
{
buffer = new GraphicBuffer(w, h, mFormat, effectiveUsage);

return buffer;
}

如此的话,客户端利用new 的 GraphicBuffer() 对象从 Parcel中读取 native_handle 对象及其内容,而在服务端由同样由 requestBuffer 请求返回一个真正的GraphicBuffer对象。那么这两个数据如何序列化传递的呢?
flatten @ GraphicBuffer.cpp
status_t GraphicBuffer::flatten(void* buffer, size_t size,
        int fds[], size_t count) const
{

    if (handle) {
        buf[6] = handle->numFds;
        buf[7] = handle->numInts;
        native_handle_t const* const h = handle;
        memcpy(fds,     h->data,             h->numFds*sizeof(int));
        memcpy(&buf[8], h->data + h->numFds, h->numInts*sizeof(int));
    }
flatten的职能就是将GraphicBuffer的handle变量信息写到Parcel句中,接收端利用unflatten读取
status_t GraphicBuffer::unflatten(void const* buffer, size_t size,
        int fds[], size_t count)
{
        native_handle* h = native_handle_create(numFds, numInts);
        memcpy(h->data,          fds,     numFds*sizeof(int));
        memcpy(h->data + numFds, &buf[8], numInts*sizeof(int));
        handle = h;
}
经过以上操作,在客户端构造了一个对等的 GraphicBuffer对象,下面将继续讲两者如何操作相同的内存块

4、共享内存的管理 — Graphic Mapper 功能

两个进程间如何共享内存,如何获取到共享内存? Mapper就是干这个得。需要利用到两个信息:共享缓冲区设备句柄,分配时的偏移量.客户端需要操作一块共享内存时,首先利用 registerBuffer 注册一个 buffer_handle_t,然后利用lock函数获取缓冲区首地址进行绘图,即利用lock及unlock对内存进行映射使用。


利用lock(mmap)及unlock(ummap)进行一个缓冲区的映射。
重要的代码如下:mapper.cpp 
static int gralloc_map(gralloc_module_t const* module,
      buffer_handle_t handle,
      void** vaddr){
     private_handle_t* hnd = (private_handle_t*)handle;
void* mappedAddress = mmap(0, size,
              PROT_READ|PROT_WRITE, MAP_SHARED, hnd->fd, 0);

      if (mappedAddress == MAP_FAILED) {
          LOGE(“Could not mmap %s”, strerror(errno));
          return -errno;
      }
      hnd->base = intptr_t(mappedAddress) + hnd->offset;
      *vaddr = (void*)hnd->base;      
     return 0;
}

static int gralloc_unmap(gralloc_module_t const* module,
      buffer_handle_t handle){
      private_handle_t* hnd = (private_handle_t*)handle;
      void* base = (void*)hnd->base;
      size_t size = h nd->size;
      munmap(base, size);
      hnd->base = 0;
      return 0;
}
利用buffer_handle_t与private_handle_t句柄完成共享进程数据的共享:

总结:
Android在该节使用了共享内存的方式来管理与显示相关的缓冲区,他设计成了两层,上层是缓冲区管理的代理机构GraphicBuffer,
及其相关的native_buffer_t,下层是具体的缓冲区的分配管理及其缓冲区本身。上层的对象是可以在经常间通过Binder传递的,而在进程间并不是传递缓冲区本身,而是使用mmap来获取指向共同物理内存的映射地址。

13
0

我的同类文章

  • sp&wp 的三板斧2015-11-09
  • Android 待机功能流程分析2015-11-04
  • bootchart 使用说明及代码分析2014-04-14
  • android – home键及launcher启动流程分析2013-11-01
  • android recovery 主系统代码分析2013-07-05
  • Android 无法接收开机广播的问题2015-11-06
  • 字符编码问题2015-11-04
  • sp和wp的实现过程~~2014-02-28
  • android SystemUI 流程分析2013-10-18
  • android — 编译不同库及编译脚本之区别2013-03-18

更多文章

  • sp&wp 的三板斧2015-11-09
  • Android 待机功能流程分析2015-11-04
  • bootchart 使用说明及代码分析2014-04-14
  • android – home键及launcher启动流程分析2013-11-01
  • android recovery 主系统代码分析2013-07-05
  • Android 无法接收开机广播的问题2015-11-06
  • 字符编码问题2015-11-04
  • sp和wp的实现过程~~2014-02-28
  • android SystemUI 流程分析2013-10-18
  • android — 编译不同库及编译脚本之区别2013-03-18

更多文章

  • sp&wp 的三板斧2015-11-09
  • Android 待机功能流程分析2015-11-04阅读684
  • bootchart 使用说明及代码分析2014-04-14
  • android – home键及launcher启动流程分析2013-11-01
  • android recovery 主系统代码分析2013-07-05
  • Android 无法接收开机广播的问题2015-11-06
  • 字符编码问题2015-11-04< /span>
  • sp和wp的实现过程~~2014-02-28
  • android SystemUI 流程分析2013-10-18
  • android — 编译不同库及编译脚本之区别2013-03-18

更多文章

查看评论
3楼  CyberLogic 2014-09-29 11:11发表 [回复]
分析的不错,呵呵
2楼  goodtalent 2014-03-12 17:33发表 [回复]
看不懂啊。

Re:  andyhuabing 2014-03-18 17:25发表 [回复]
回复goodtalent:多看看就懂了,呵呵
1楼  houyizi313 2013-04-12 10:39发表 [回复] [引用] [举报]
兄弟:你理解的台深奥了!我遇到了个问题!也需要研究下Android测显示机制!我想知道Android播放本地SD卡上面的视频,控制视频的显示大小的那一块源码在那个文件?那个部分的?因为刚开始入手,android层层封转,显示系统也是分层,我连这些层都分布清楚!所以难到了……非常感谢……

Re:  andyhuabing 2014-03-18 17:24发表 [回复] [引用] [举报]
回复houyizi337825770:本地播放是使用JAVA类 : MediaPlayer.java /** * Sets the {@link SurfaceHolder} to use for displaying the video * portion of the media. * * Either a surface holder or surface must be set if a display or video sink * is needed. Not calling this method or {@link #setSurface(Surface)} * when playing back a video will result in only the audio track being played. * A null surface holder or surface will result in only the audio track being * played. * * @param sh the SurfaceHolder to use for video display */ public static final int KEY_PARAMETER_VIDEO_POSITION_INFO = 2000; public void setDisplay(SurfaceHolder sh) { 通过SurfaceView的大小指定视频大小的。其实就是将图形层挖了一个“洞”将视频透出来了。

查看评论

3楼  CyberLogic 2014-09-29 11:11发表 [回复]
分析的不错,呵呵
2楼  goodtalent 2014-03-12 17:33发表 [回复]
看不懂啊。

Re:  andyhuabing 2014-03-18 17:25发表 [回复]
回复goodtalent:多看看就懂了,呵呵
1楼  houyizi313 2013-04-12 10:39发表 [回复] [引用] [举报]
兄弟:你理解的台深奥了!我遇到了个问题!也需要研究下Android测显示机制!我想知道Android播放本地SD卡上面的视频,控制视频的显示大小的那一块源码在那个文件?那个部分的?因为刚开始入手,android层层封转,显示系统也是分层,我连这些层都分布清楚!所以难到了……非常感谢……

Re:  andyhuabing 2014-03-18 17:24发表 [回复] [引用] [举报]
回复houyizi337825770:本地播放是使用JAVA类 : MediaPlayer.java /** * Sets the {@link SurfaceHolder} to use for displaying the video * portion of the media. * * Either a surface holder or surface mu st be set if a display or video sink * is needed. Not calling this method or {@link #setSurface(Surface)} * when playing back a video will result in only the audio track being played. * A null surface holder or surface will result in only the audio track being * played. * * @param sh the SurfaceHolder to use for video display */ public static final int KEY_PARAMETER_VIDEO_POSITION_INFO = 2000; public void setDisplay(SurfaceHolder sh) { 通过SurfaceView的大小指定视频大小的。其实就是将图形层挖了一个“洞”将视频透出来了。

Leave a Comment

Your email address will not be published.