[置顶] Android学习之Ashmem driver

参照init/Kconfig中对于ASHMEM的说明:它是一由android的提供的新的共享内存分配器, 类似于POSIX的SHM,但是拥有不同的一些行为同时引入了简单的基于文件的API.


Dave Sparks对ashmem和pmem的解释:

1. 

ISurface是对应于SurfaceFlinger的远程接口.当你调用类似postBuffer的ISurface的方法的时候,你其实是执行了一个在SurfaceFlinger的RPC方法.只有通过SurfaceFlinger来对窗口系统进行2D图像宣染.在未来,你可能使用overlay的接口,但是这更多的是针对使用硬件管道化的视频而不是软件宣染.

2.

ashmem和pmem非常相似.两者都被使用来在进程间共享内存.ashmem使用虚拟内存,而pmem使用的是物理上连续的内存.两者之间的最大区别是:使用ashmem的时候,你拥有一个在共享内存的各个进程中共享的引用计数.例如,如果两个进程共享一块ashmem内存,只有两个进程都关闭了它们各自的文件描述符来移除各自的引用,对这块内存的引用才会被清除.pmem就没有按照这样的方式工作,因为这需要维护一个从物理到虚拟内存的映射.这需要使用pmem的进程分配一个堆来保存它使用的文件描述符,知道所有其它引用该内存的其它引用被释放.

3.
你有如何使用共享内存的正确想法.是否使用ashmem或者pmem依赖于你是否需要物理上连续的内存.在G1中,我们使用硬件2D引擎来scaling, rotation, color conversion,所以我们使用pmem堆.而emulator没有pmem驱动并且并不需要它,所以我们在emulator中使用ashmem.如果你在G1上使用ashmem,你就不能使用硬件2D引擎,所以SurfaceFlinger退回到使用软件的宣染,这就缺少了color conversion.这就是你为什么只看到单色的图片的原因.


从mozilla邮件列表里面看来的关于使用ashmem需要注意的地方:

邮件的标题是:Notes for people investigating using Android ashmem

- When you allocate ashmem, unless you explicitely unpin it, it will
  never be purged by the kernel.
- When ashmem is purged by the kernel, it ends up being in the exact
  same state as if it had just been allocated: accessible and filled
  with zeros. It is important to note that this *also* includes the
  state, which means if you fill it again, it won't be purged again
  unless you unpin it!
- If my reading of the kernel source is correct, ashmem unpinned
  ranges are purged at once: you can't end up with half purged and half
  not purged. However, it is possible to unpin an ashmem by small
  chunks, in which case it is possible that some chunks are purged and
  others not.

Some further thoughts:

- The same effect as ashmem can be (mostly) implemented on Linux without
  ashmem, by mmapping a file in /dev/shm or another tmpfs, and use
  madvise(MADV_REMOVE) to purge; the main difference is that the latter
  is (obviously) not automatically triggered by the kernel on low memory.
- If the memory region doesn't need to be shared across processes,
  /dev/zero can be used instead in the above.
- OSX seems to have something similar to ashmem purge on anonymous
  mappings, with VM_FLAGS_PURGABLE, but it seems impossible to find a
  clear documentation as to what it does and when.
- Considering the above, and that the same as madvise(MADV_REMOVE) can
  be achieved on Windows by decommitting and recommitting, we could have
  a generic and cross platform purgeable cache handling class for things
  we don't need to share across processes, where the cache eviction
  would be manually triggered on non-Android non-OSX.
Also, purge happens in least recently *unpinned" region order. Which
means the only safe way to access ashmem is to pin it before use, and
unpin after. Conveniently, pinning also tells you whether the region
you're pinning was purged, so the use pattern is really pin, generate if
purged, use, unpin.

相关知识点:

static struct kmem_cache *ashmem_area_cachep __read_mostly;

这里__read_mostly是一个gcc的扩展属性,用于SMP系统中,优化cacheline,对于读操作比较多的变量有效,但是一旦有write操作,会导致比较大的性能损失.(kerneltrap中的相关说明 及 一篇介绍gcc优化的编程的ppt). 但是我查了CyanogenMod提供的kernel, 对于该扩展, arm的平台并没有支持,所以理论上对该扩展在arm体系上就不起作用.

name@author-ubuntu:~/Android/CM-kernel/cm-kernel$ grep "#define __read_mostly" * -r
arch/sh/include/asm/cache.h:#define __read_mostly __attribute__((__section__(".data..read_mostly")))
arch/cris/include/arch-v32/arch/cache.h:#define __read_mostly __attribute__((__section__(".data.read_mostly")))
arch/ia64/include/asm/cache.h:#define __read_mostly __attribute__((__section__(".data..read_mostly")))
arch/sparc/include/asm/cache.h:#define __read_mostly __attribute__((__section__(".data..read_mostly")))
arch/powerpc/include/asm/cache.h:#define __read_mostly __attribute__((__section__(".data..read_mostly")))
arch/x86/include/asm/cache.h:#define __read_mostly __attribute__((__section__(".data..read_mostly")))
arch/parisc/include/asm/cache.h:#define __read_mostly __attribute__((__section__(".data..read_mostly")))
arch/tile/include/asm/cache.h:#define __read_mostly __attribute__((__section__(".data.read_mostly")))
arch/s390/include/asm/cache.h:#define __read_mostly __attribute__((__section__(".data..read_mostly")))
include/linux/cache.h:#define __read_mostly

使用kmem_cache来管理所有的ashmem.

Ashmem的实现机制:

ashmem本身的实现加上对应的头文件也才800多行.却利用了内核既有的shm的一些机制,重新分装组合,以misc驱动的方式,通过一些ioctl来提供接口给用户使用. 这与alarm驱动一样,体现了android的设计者们的智慧,如果利用现有的基础架构构建符合自己需要的功能!!!特别是使用了内核中的内存管理模块及共享内存模块提供的接口:通过register_shrinker来像mm子系统注册自己,让其来帮助管理页面的回收,分配; 通过shmem_file_setup 接口来使ashmem驱动使用的内存与tmpfs文件系统挂钩, 使得之后对ashmem的操作都转变为对该tmpfs文件系统中生成的内存文件的操作.

其中利用了一个mm subsystem提供出来的接口--shrinker_register, 来简化了ashmem提供的pin, unpin对于cache的管理操作.(关于mm的shrink接口的理解,参见<Kernel的内存管理子系统之shrink是神马>)

用户侧的程序使用jni提供的接口frameworks/base/core/jni/android_os_MemoryFile.cpp调用system/core/libcutils/ashmem-dev.c封装的API来使用ashmem驱动,而不用自己直接操作ioctl命令.

虽然Ashmem的代码很少,但是里面用到了内存管理子系统的特性,如果对内存子系统不了解,光看代码虽然能够看懂,但是要想自己设计出这样的驱动,是需要一定功底的.另外从android系统的代码看,该驱动用到的地方很少,这是一件比较奇怪的地方.难道是做系统开发的人,还没有很好的使用它???

关于Pin/Unpin的实现:

主要是对申请空间的一些操作, 具体情况代码的注释已经比较清晰, 类似于判断交集,并集,子集之类操作. 这里不多说了, 实际使用中再参考注释进行微调即可.

Android中使用到ashmem的模块(基于4.01的source):

system/core/libcutils/ashmem-dev.c:#define ASHMEM_DEVICE    "/dev/ashmem"
system/core/libcutils/ashmem-dev.c: fd = open(ASHMEM_DEVICE, O_RDWR);
dalvik/vm/LinearAlloc.cpp:    fd = ashmem_create_region("dalvik-LinearAlloc", DEFAULT_MAX_LENGTH);
dalvik/vm/compiler/Compiler.cpp:    fd = ashmem_create_region("dalvik-jit-code-cache", gDvmJit.codeCacheSize);
dalvik/vm/alloc/TEST/HeapBitmapTest/include/cutils/ashmem.h:ashmem_create_region(const char *name, size_t len)
dalvik/vm/Misc.cpp:    fd = ashmem_create_region(name, byteCount);
development/tools/emulator/opengl/system/gralloc/gralloc.cpp:        fd = ashmem_create_region("gralloc-buffer", ashmem_size);
external/skia/src/ports/SkImageRef_ashmem.cpp:            fd = ashmem_create_region(fName, size);
external/skia/src/ports/SkImageRef_ashmem.cpp:            SkDebugf("=== ashmem_create_region %s size=%d fd=%d\n", fName, size, fd);
external/jpeg/jmem-ashmem.c:  int fd = ashmem_create_region(path, total_bytes_needed);
frameworks/base/core/jni/android_database_SQLiteStatement.cpp:    int fd = ashmem_create_region(NULL, length);
frameworks/base/core/jni/android_database_SQLiteStatement.cpp:        LOGE("ashmem_create_region failed: %s", strerror(errno));
frameworks/base/core/jni/android_os_MemoryFile.cpp:    int result = ashmem_create_region(namestr, length);
frameworks/base/core/jni/android_os_MemoryFile.cpp:        jniThrowException(env, "java/io/IOException", "ashmem_create_region failed");
frameworks/base/libs/binder/CursorWindow.cpp:    int ashmemFd = ashmem_create_region(ashmemName.string(), size);
frameworks/base/libs/binder/MemoryHeapBase.cpp:    int fd = ashmem_create_region(name == NULL ? "MemoryHeapBase" : name, size);
frameworks/base/libs/binder/Parcel.cpp:    int fd = ashmem_create_region("Parcel Blob", len);
frameworks/base/libs/ui/InputTransport.cpp:    int serverAshmemFd = ashmem_create_region(ashmemName.string(), DEFAULT_MESSAGE_BUFFER_SIZE);
hardware/msm7k/libgralloc/gralloc.cpp:        fd = ashmem_create_region("gralloc-buffer", size);
hardware/msm7k/libgralloc-qsd8k/gpu.cpp:        fd = deps.ashmem_create_region("gralloc-buffer", size);
hardware/msm7k/libgralloc-qsd8k/gralloc.cpp:    virtual int ashmem_create_region(const char *name, size_t size) {
hardware/msm7k/libgralloc-qsd8k/gralloc.cpp:        return ::ashmem_create_region(name, size);
hardware/msm7k/libgralloc-qsd8k/gpu.h:        virtual int ashmem_create_region(const char *name, size_t size) = 0;
hardware/libhardware/modules/gralloc/gralloc.cpp:    fd = ashmem_create_region("gralloc-buffer", size);
system/core/include/cutils/ashmem.h:int ashmem_create_region(const char *name, size_t size);
system/core/libcutils/ashmem-host.c:int ashmem_create_region(const char *ignored, size_t size)
system/core/libcutils/mspace.c:  fd = ashmem_create_region(buf, max_capacity);
system/core/libcutils/ashmem-dev.c: * ashmem_create_region - creates a new ashmem region and returns the file
system/core/libcutils/ashmem-dev.c:int ashmem_create_region(const char *name, size_t size)


在我的Nexus one中显示系统中所有的ashmem slab信息如下:
# cat /proc/slabinfo | grep ashmem
ashmem_range_cache      9    113     32  113    1 : tunables  120   60    0 : slabdata      1      1      0
ashmem_area_cache     38     52    288   13    1 : tunables   54   27    0 : slabdata      4      4      0

关于以上的这些相关的代码如何组织并串行起来给java侧使用,大家可以方便的从代码中找到路径. 也可以参照以下这篇博文来帮助理解(这里谢谢老罗详细的分析)

<Android系统匿名共享内存Ashmem驱动程序源代码分析>


参考资料:

http://elinux.org/Android_Kernel_Features#ashmem简单介绍
http://huenlil.pixnet.net/blog/post/26662128-android-ashmem-%E7%9B%B8%E9%97%9C%E8%B3%87%E8%A8%8A 综述及简单的机制分析

http://www.androidenea.com/2010/03/share-memory-using-ashmem-and-binder-in.html如何使用的例子
http://cs736-android.pbworks.com/w/page/5834465/ASHMEM有关Ashmem和Pmem

http://groups.google.com/group/android-ndk/browse_thread/thread/02adcb133efbdcc3/577587358d32bb29?pli=1及

http://www.androidenea.com/2010/03/share-memory-using-ashmem-and-binder-in.html 关于如何使用ashmem来在不同process间共享内存的讨论




你可能感兴趣的:(android,cache,database,documentation,mozilla,代码分析)