最近遇到个问题,需要使用共享内存。之前看audioTrack与audioFlinger部份时就看到共存内存的分配与同步,但是没有细研究这部份。这几天细看了下流程,发现使用起来挺简单的。
ashmem是android封装提供的一种共享内存实现方式。驱动会在/dev/下面注册ashmem字符设备,供上层做文件操作(ashmem驱动后面分析)。
按照一般理解(如camera videobuffer操作), 既然内核给分配了内存,那上层用户空间要使用的话,要么通过read/write文件操作接口,在用户层与内核层作拷贝;要么就是mmap映射,然后直接使用。ashmem就是直接mmap到用户空间的。大概使用原理就是server、client一端打开/dev/ashmem得到fd句柄,并mmap得到共享内存地址;再把fd与size传到对应的client、server,另一端再mmap下也得到了此地址,这样二边就可以直接操作各自mmap得到的地址就行了。当然从server、client传fd到client、server这里面也包含了binder通信。
android对ashemem的封装很简单(/system/core/libcutils/Ashmem-dev.c),提供了以下几个接口。一般我们只取ashmem_create_region,得到fd(既然是fd,那必然有close,但android并没有提供,因为只需要close(fd)就行了。那还有一个问题,既然server与client端都是fd,按理解那这2个fd的值应该是不同的,肯定会dup)。
ashmem_create_region
ashmem_set_prot_region
ashmem_pin_region
ashmem_unpin_region
ashmem_get_size_region
完全可以在这几个封装接口的基础上直接使用。除此之外android framework还封了一个IMemory类给上层操作。
1、直接操作Ashmem-dev.c接口demo
demo目录如下所示:
demo由server与client及接口组成,共享内存可由server产生也可由client去申请。为了验证共享内存的使用及正确性,server与client都可以读写此共享内存。这样就产生了同步问题。为了简化起见,弄了个ICallback类,在server、client操作完后,callback对方,这时对方才操作shareMemory,这样就不需要花太多精力与时间去弄同步部份。(典型同步见audioTrack与audioFlinger之间的cblk同步)
具体代码如下:
ITestBinder.h
|
ITestBinder.cpp
|
ICallback.h
|
ICallback.cpp
|
Android.mk
|
TestService.cpp
|
TestService.h
|
main.cpp
|
Android.mk
D_EXECUTABLE) |
TestClient.cpp
|
TestClient.h
|
main.cpp
|
Android.mk
|
1.1、由client申请共享内存
fd与size是怎样传到server端的。BpTestBinder直接写fd与size到BnTestBinder。
bn端的处理:
可以说是直接传了fd与size,server端直接拿来就mmap了。
关闭时也是各自关闭各自的fd。
操作起来相当简单。我们看打印2个fd的值。fd=5、fd=7果然是不一致。是否是ashmem驱动帮我们dup了??
1.2、由server申请共享内存
跟前面client端申请内存一样的,注意下面的,传fd、size值。在bp端读取bn端的fd时,必须得dup下,这点有些不懂,其实不dup的话,打印fd值,驱动是有帮dup的,这里为什么要再dup下,实在是想不通。如果这里不dup,那就会出现mmap fail。
在bp那里没有加dup(),mmap fail:
2、IMemory分析
通常我们调用IMemory去分配共享内存时,一般都如下面操作:
sp<MemoryHeapBase> mMemHeap = new MemoryHeapBase(sndDataSize, 0, "AudioTrack Heap Base");
sp<MemoryBase> mMemBase = new MemoryBase(mMemHeap, 0, sndDataSize);
memcpy(mMemHeap->getBase(), sndDataBuffer, sndDataSize);
由BnMemoryHeapBase转换到BnMemory,再通过IMemory类去实现进程间的共享内存通信。
IMemory是framework层在ashmem-dev.c接口上封的一个共享内存类。Bn端是memoryHeapBase与memoryBase。与直接调用ashmem-dev.c接口基本相同,memoryHeapBase也是对open与mmap封装了下。
BpMemopry端,我们看常见的getBase()接口。
fd、size、offset对应Bn端mmap()操作的参数。同样可以看到BpMemory端的dup()操作,这也验证了上面demo只有加dup()才会正常。
所以可以看到,IMemory类的操作与直接调用ashmem-dev.c接口也是大同小异。
用一个简单的audioTrack demo来说明IMemory接口的使用。
test.cpp
|
Android.mk
|
可以看到,真正的使用接口就是下面的:
从字面上理解是先分配堆内存,再在堆内存上分配实现内存,但是实际代码的操作明显就是一样的操作。为什么这样写接口,而不是一步封出来??而要有个BnMemoryHeap ->BnMemory的转换??