(转)SurfaceFlinger GraphicBuffer内存共享缓冲区机制

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

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<GraphicBuffer >, public Flattenable

EGLNativeBase 是一个模板类:

template<typename NATIVE_TYPE, typename TYPE, typename REF>

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 format;

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处理(不同进程),其它的直接读取复制使用.

利用函数dup,我们可以复制一个描述符。传给该函数一个既有的描述符,它就会返回一

个新的描述符,这个新的描述符是传给它的描述符的拷贝。这意味着,这两个描述符共享同一

数据结构

magic,flags,size,offset,base,lockState,writeOwner,pid 等复制到了客户端,从而为缓冲区共享获取到相应的信息

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

status_t Parcel::writeFileDescriptor(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;

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 = hnd->size;

munmap(base, size);

hnd->base = 0;

return 0;

}

利用buffer_handle_t与private_handle_t句柄完成共享进程数据的共享:


总结:

Android在该节使用了共享内存的方式来管理与显示相关的缓冲区,他设计成了两层,上层是缓冲区管理的代理机构GraphicBuffer,

及其相关的native_buffer_t,下层是具体的缓冲区的分配管理及其缓冲区本身。上层的对象是可以在经常间通过Binder传递的,而在进程间并不是传递缓冲区本身,而是使用mmap来获取指向共同物理内存的映射地址。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容