- 浏览: 21488609 次
- 性别:
- 来自: 杭州
最新评论
-
ZY199266:
配置文件还需要额外的配置ma
Android 客户端通过内置API(HttpClient) 访问 服务器(用Spring MVC 架构) 返回的json数据全过程 -
ZY199266:
我的一访问为什么是 /mavenwebdemo/WEB-I ...
Android 客户端通过内置API(HttpClient) 访问 服务器(用Spring MVC 架构) 返回的json数据全过程 -
lvgaga:
我又一个问题就是 如果像你的这种形式写。配置文件还需要额外的 ...
Android 客户端通过内置API(HttpClient) 访问 服务器(用Spring MVC 架构) 返回的json数据全过程 -
lvgaga:
我的一访问为什么是 /mavenwebdemo/WEB-I ...
Android 客户端通过内置API(HttpClient) 访问 服务器(用Spring MVC 架构) 返回的json数据全过程 -
y1210251848:
你的那个错误应该是项目所使用的目标框架不支持吧
log4net配置(web中使用log4net,把web.config放在单独的文件中)
Android系统匿名共享内存(Anonymous Shared Memory)C++调用接口分析
在Android系统中,针对移动设备内存空间有限的特点,提供了一种在进程间共享数据的机制:匿名共享内存,它能够辅助内存管理系统来有效地管理内存,它的实现原理我们在前面已经分析过了。为了方便使用匿名共享内存机制,系统还提供了Java调用接口(MemoryFile)和C++调用接口(MemoryHeapBase、MemoryBase),Java接口在前面也已经分析过了,本文中将继续分析它的C++接口。
在前面一篇文章Android系统匿名共享内存Ashmem(Anonymous Shared Memory)驱动程序源代码分析中,我们分析了匿名共享内存驱动程序Ashmem的实现,重点介绍了它是如何辅助内存管理系统来有效地管理内存的,简单来说,它就是给使用者提供锁机制来辅助管理内存,当我们申请了一大块匿名共享内存时,中间过程有一部分不需要使用时,我们就可以将这一部分内存块解锁,这样内存管理系统就可以把它回收回去了。接着又在前面一篇文章Android系统匿名共享内存Ashmem(Anonymous Shared Memory)在进程间共享的原理分析中,我们分析了匿名共享内存是如何通过Binder进程间通信机制来实现在进程间共享的,简单来说,就是每一个匿名共享内存块都是一个文件,当我们需要在进程间共享时,就把这个文件的打开描述符通过Binder进程间通信机制传递给另一外进程,在传递的过程中,Binder驱动程序就通过这个复制这个打开文件描述符到目标进程中去,从而实现数据共享。在文章Android系统匿名共享内存Ashmem(Anonymous Shared Memory)简要介绍和学习计划中,我们介绍了如何在Android应用程序中使用匿名共享内存,主要是通过应用程序框架层提供的MemoryFile接口来使用的,而MemoryFile接口是通过JNI方法调用到系统运行时库层中的匿名共享内存C接口,最终通过这些C接口来使用内核空间中的匿名共享内存驱动模块。为了方便开发者灵活地使用匿名共享内存,Android系统在应用程序框架层中还提供了使用匿名共享内存的C++接口,例如,Android应用程序四大组件之一Content Provider,它在应用程序间共享数据时,就是通过匿名共享内存机制来实现,但是它并不是通过MemoryFile接口来使用,而是通过调用C++接口中的MemoryBase类和MemoryHeapBase类来使用。在接下来的内容中,我们就详细分析MemoryHeapBase类和MemoryBase类的实现,以及它们是如何实现在进程间共享数据的。
如果我们想在进程间共享一个完整的匿名共享内存块,可以通过使用MemoryHeapBase接口来实现,如果我们只想在进程间共享一个匿名共享内存块中的其中一部分时,就可以通过MemoryBase接口来实现。MemoryBase接口是建立在MemoryHeapBase接口的基础上面的,它们都可以作为一个Binder对象来在进程间传输,因此,希望读者在继续阅读本文之前,对Android系统的Binder进程间通信机制有一定的了解,具体可以参考前面一篇文章Android进程间通信(IPC)机制Binder简要介绍和学习计划。下面我们就首先分析MemoryHeapBase接口的实现,然后再分析MemoryBase接口的实现,最后,通过一个实例来说明它们是如何使用的。
1. MemoryHeapBase
前面说到,MemoryHeapBase类的对象可以作为Binder对象在进程间传输,作为一个Binder对象,就有Server端对象和Client端引用的概念,其中,Server端对象必须要实现一个BnInterface接口,而Client端引用必须要实现一个BpInterface接口。下面我们就先看一下MemoryHeapBase在Server端实现的类图:
这个类图中的类可以划分为两部分,一部分是和业务相关的,即跟匿名共享内存操作相关的类,包括MemoryHeapBase、IMemoryBase和RefBase三个类,另一部分是和Binder机制相关的,包括IInterface、BnInterface、BnMemoryHeap、IBinder、BBinder、ProcessState和IPCThreadState七个类。
我们先来看跟匿名共享内存业务相关的这部分类的逻辑关系。IMemoryBase定义了匿名共享内操作的接口,而MemoryHeapBase是作为Binder机制中的Server角色的,因此,它需要实现IMemoryBase接口,此外,MemoryHeapBase还继承了RefBase类。从前面一篇文章Android系统的智能指针(轻量级指针、强指针和弱指针)的实现原理分析中,我们知道,继承了RefBase类的子类,它们的对象都可以结合Android系统的智能指针来使用,因此,我们在实例化MemoryHeapBase类时,可以通过智能指针来管理它们的生命周期。
再来看和Binder机制相关的这部分类的逻辑关系。从Android系统进程间通信(IPC)机制Binder中的Server启动过程源代码分析这篇文章中,我们知道,所有的Binder对象都必须实现IInterface接口,无论是Server端实体对象,还是Client端引用对象,通过这个接口的asBinder成员函数我们可以获得Binder对象的IBinder接口,然后通过Binder驱动程序把它传输给另外一个进程。当一个类的对象作为Server端的实体对象时,它还必须实现一个模板类BnInterface,这里负责实例化模板类BnInterface的类便是BnMemoryHeap类了,它里面有一个重要的成员函数onTransact,当Client端引用请求Server端对象执行命令时,Binder系统就会调用BnMemoryHeap类的onTransact成员函数来执行具体的命令。当一个类的对象作为Server端的实体对象时,它还要继承于BBinder类,这是一个实现了IBinder接口的类,它里面有一个重要的成员函数transact,当我们从Server端线程中接收到Client端的请求时,就会调用注册在这个线程中的BBinder对象的transact函数来处理这个请求,而这个transact函数会将这些Client端请求转发给BnMemoryHeap类的onTransact成员函数来处理。最后,ProcessState和IPCThreadState两个类是负责和Binder驱动程序打交道的,其中,ProcessState负责打开Binder设备文件/dev/binder,打开了这个Binder设备文件后,就会得到一个打开设备文件描述符,而IPCThreadState就是通过这个设备文件描述符来和Binder驱动程序进行交互的,例如它通过一个for循环来不断地等待Binder驱动程序通知它有新的Client端请求到来了,一旦有新的Client端请求到来,它就会调用相应的BBinder对象的transact函数来处理。
本文我们主要是要关注和匿名共享内存业务相关的这部分类,即IMemoryBase和MemoryHeapBase类的实现,和Binder机制相关的这部分类的实现,可以参考Android进程间通信(IPC)机制Binder简要介绍和学习计划一文。
IMemoryBase类主要定义了几个重要的操作匿名共享内存的方法,它定义在frameworks/base/include/binder/IMemory.h文件中:
- classIMemoryHeap:publicIInterface
- {
- public:
- ......
- virtualintgetHeapID()const=0;
- virtualvoid*getBase()const=0;
- virtualsize_tgetSize()const=0;
- ......
- };
成员函数getHeapID是用来获得匿名共享内存块的打开文件描述符的;成员函数getBase是用来获得匿名共享内存块的基地址的,有了这个地址之后,我们就可以在程序里面直接访问这块共享内存了;成员函数getSize是用来获得匿名共享内存块的大小的。
MemoryHeapBase类主要用来实现上面IMemoryBase类中列出来的几个成员函数的,这个类声明在frameworks/base/include/binder/MemoryHeapBase.h文件中:
- classMemoryHeapBase:publicvirtualBnMemoryHeap
- {
- public:
- ......
- /*
- *mapsmemoryfromashmem,withthegivennamefordebugging
- */
- MemoryHeapBase(size_tsize,uint32_tflags=0,charconst*name=NULL);
- ......
- /*implementIMemoryHeapinterface*/
- virtualintgetHeapID()const;
- virtualvoid*getBase()const;
- virtualsize_tgetSize()const;
- ......
- private:
- intmFD;
- size_tmSize;
- void*mBase;
- ......
- }
- MemoryHeapBase::MemoryHeapBase(size_tsize,uint32_tflags,charconst*name)
- :mFD(-1),mSize(0),mBase(MAP_FAILED),mFlags(flags),
- mDevice(0),mNeedUnmap(false)
- {
- constsize_tpagesize=getpagesize();
- size=((size+pagesize-1)&~(pagesize-1));
- intfd=ashmem_create_region(name==NULL?"MemoryHeapBase":name,size);
- LOGE_IF(fd<0,"errorcreatingashmemregion:%s",strerror(errno));
- if(fd>=0){
- if(mapfd(fd,size)==NO_ERROR){
- if(flags&READ_ONLY){
- ashmem_set_prot_region(fd,PROT_READ);
- }
- }
- }
- }
MemoryHeapBase类创建的匿名共享内存是以页为单位的,页的大小一般为4K,但是是可以设置的,这个函数首先通过getpagesize函数获得系统中一页内存的大小值,然后把size参数对齐到页大小去,即如果size不是页大小的整数倍时,就增加它的大小,使得它的值为页大小的整数倍:
- constsize_tpagesize=getpagesize();
- size=((size+pagesize-1)&~(pagesize-1));
- intfd=ashmem_create_region(name==NULL?"MemoryHeapBase":name,size);
得到了这个匿名共享内存的文件描述符后,还需要调用mapfd成函数把它映射到进程地址空间去:
- status_tMemoryHeapBase::mapfd(intfd,size_tsize,uint32_toffset)
- {
- ......
- if((mFlags&DONT_MAP_LOCALLY)==0){
- void*base=(uint8_t*)mmap(0,size,
- PROT_READ|PROT_WRITE,MAP_SHARED,fd,offset);
- ......
- mBase=base;
- ......
- }else{
- ......
- }
- mFD=fd;
- mSize=size;
- returnNO_ERROR;
- }
- void*base=(uint8_t*)mmap(0,size,
- PROT_READ|PROT_WRITE,MAP_SHARED,fd,offset);
回到前面MemoryHeapBase类的构造函数中,将匿名共享内存映射到本进程的地址空间去后,还看继续设置这块匿名共享内存的读写属性:
- if(fd>=0){
- if(mapfd(fd,size)==NO_ERROR){
- if(flags&READ_ONLY){
- ashmem_set_prot_region(fd,PROT_READ);
- }
- }
- }
这样,通过这个构造函数,一块匿名共享内存就建立好了,其余的三个成员函数getHeapID、getBase和getSize就简单了:
- intMemoryHeapBase::getHeapID()const{
- returnmFD;
- }
- void*MemoryHeapBase::getBase()const{
- returnmBase;
- }
- size_tMemoryHeapBase::getSize()const{
- returnmSize;
- }
这个类图中的类也是可以划分为两部分,一部分是和业务相关的,即跟匿名共享内存操作相关的类,包括BpMemoryHeap、IMemoryBase和RefBase三个类,另一部分是和Binder机制相关的,包括IInterface、BpInterface、BpRefBase、IBinder、BpBinder、ProcessState和IPCThreadState七个类。
在和匿名共享内存操作相关的类中,BpMemoryHeap类是前面分析的MemoryHeapBase类在Client端进程的远接接口类,当Client端进程从Service Manager或者其它途径获得了一个MemoryHeapBase对象的引用之后,就会在本地创建一个BpMemoryHeap对象来代表这个引用。BpMemoryHeap类同样是要实现IMemoryHeap接口,同时,它是从RefBase类继承下来的,因此,它可以与智能指针来结合使用。
在和Binder机制相关的类中,和Server端实现不一样的地方是,Client端不需要实现BnInterface和BBinder两个类,但是需要实现BpInterface、BpRefBase和BpBinder三个类。BpInterface类继承于BpRefBase类,而在BpRefBase类里面,有一个成员变量mRemote,它指向一个BpBinder对象,当BpMemoryHeap类需要向Server端对象发出请求时,它就会通过这个BpBinder对象的transact函数来发出这个请求。这里的BpBinder对象是如何知道要向哪个Server对象发出请深圳市的呢?它里面有一个成员变量mHandle,它表示的是一个Server端Binder对象的引用值,BpBinder对象就是要通过这个引用值来把请求发送到相应的Server端对象去的了,这个引用值与Server端Binder对象的对应关系是在Binder驱动程序内部维护的。这里的ProcessSate类和IPCThreadState类的作用和在Server端的作用是类似的,它们都是负责和底层的Binder驱动程序进行交互,例如,BpBinder对象的transact函数就通过线程中的IPCThreadState对象来将Client端请求发送出去的。这些实现具体可以参考Android系统进程间通信(IPC)机制Binder中的Client获得Server远程接口过程源代码分析一文。
这里我们主要关注BpMemoryHeap类是如何实现IMemoryHeap接口的,这个类声明和定义在frameworks/base/libs/binder/IMemory.cpp文件中:
- classBpMemoryHeap:publicBpInterface<IMemoryHeap>
- {
- public:
- BpMemoryHeap(constsp<IBinder>&impl);
- ......
- virtualintgetHeapID()const;
- virtualvoid*getBase()const;
- virtualsize_tgetSize()const;
- ......
- private:
- mutablevolatileint32_tmHeapId;
- mutablevoid*mBase;
- mutablesize_tmSize;
- ......
- }
- BpMemoryHeap::BpMemoryHeap(constsp<IBinder>&impl)
- :BpInterface<IMemoryHeap>(impl),
- mHeapId(-1),mBase(MAP_FAILED),mSize(0),mFlags(0),mRealHeap(false)
- {
- }
其余三个成员函数getHeapID、getBase和getSize的实现是类似的:
- intBpMemoryHeap::getHeapID()const{
- assertMapped();
- returnmHeapId;
- }
- void*BpMemoryHeap::getBase()const{
- assertMapped();
- returnmBase;
- }
- size_tBpMemoryHeap::getSize()const{
- assertMapped();
- returnmSize;
- }
- voidBpMemoryHeap::assertMapped()const
- {
- if(mHeapId==-1){
- sp<IBinder>binder(const_cast<BpMemoryHeap*>(this)->asBinder());
- sp<BpMemoryHeap>heap(static_cast<BpMemoryHeap*>(find_heap(binder).get()));
- heap->assertReallyMapped();
- if(heap->mBase!=MAP_FAILED){
- Mutex::Autolock_l(mLock);
- if(mHeapId==-1){
- mBase=heap->mBase;
- mSize=heap->mSize;
- android_atomic_write(dup(heap->mHeapId),&mHeapId);
- }
- }else{
- //somethingwentwrong
- free_heap(binder);
- }
- }
- }
在frameworks/base/libs/binder/IMemory.cpp文件中,定义了一个全局变量gHeapCache:
- staticsp<HeapCache>gHeapCache=newHeapCache();
它的类型为HeapCache,这也是一个定义在frameworks/base/libs/binder/IMemory.cpp文件的类,它里面维护了本进程中所有的MemoryHeapBase对象的引用。由于在Client端进程中,可能会有多个引用,即多个BpMemoryHeap对象,对应同一个MemoryHeapBase对象(这是由于可以用同一个BpBinder对象来创建多个BpMemoryHeap对象),因此,当第一个BpMemoryHeap对象在本进程中映射好这块匿名共享内存之后,后面的BpMemoryHeap对象就可以直接使用了,不需要再映射一次,当然重新再映射一次没有害处,但是会是多此一举,Google在设计这个类时,可以说是考虑得非常周到的。
我们来看一下HeapCache的实现:
- classHeapCache:publicIBinder::DeathRecipient
- {
- public:
- HeapCache();
- virtual~HeapCache();
- ......
- sp<IMemoryHeap>find_heap(constsp<IBinder>&binder);
- voidfree_heap(constsp<IBinder>&binder);
- sp<IMemoryHeap>get_heap(constsp<IBinder>&binder);
- ......
- private:
- //ForIMemory.cpp
- structheap_info_t{
- sp<IMemoryHeap>heap;
- int32_tcount;
- };
- ......
- MutexmHeapCacheLock;
- KeyedVector<wp<IBinder>,heap_info_t>mHeapCache;
- };
这里,我们主要看一下find_heap函数的实现:
- sp<IMemoryHeap>HeapCache::find_heap(constsp<IBinder>&binder)
- {
- Mutex::Autolock_l(mHeapCacheLock);
- ssize_ti=mHeapCache.indexOfKey(binder);
- if(i>=0){
- heap_info_t&info=mHeapCache.editValueAt(i);
- LOGD_IF(VERBOSE,
- "foundbinder=%p,heap=%p,size=%d,fd=%d,count=%d",
- binder.get(),info.heap.get(),
- static_cast<BpMemoryHeap*>(info.heap.get())->mSize,
- static_cast<BpMemoryHeap*>(info.heap.get())->mHeapId,
- info.count);
- android_atomic_inc(&info.count);
- returninfo.heap;
- }else{
- heap_info_tinfo;
- info.heap=interface_cast<IMemoryHeap>(binder);
- info.count=1;
- //LOGD("addingbinder=%p,heap=%p,count=%d",
- //binder.get(),info.heap.get(),info.count);
- mHeapCache.add(binder,info);
- returninfo.heap;
- }
- }
回到前面BpMemoryHeap类中的assertMapped函数中,如果本BpMemoryHeap对象中的mHeapID等于-1,那么就说明这个BpMemoryHeap对象中的匿名共享内存还没准备就绪,因此,需要执行一次映射匿名共享内存的操作。
在执行映射操作之作,先要看看在本进程中是否有其它映射到同一个MemoryHeapBase对象的BpMemoryHeap对象存在:
- sp<IBinder>binder(const_cast<BpMemoryHeap*>(this)->asBinder());
- sp<BpMemoryHeap>heap(static_cast<BpMemoryHeap*>(find_heap(binder).get()));
- classBpMemoryHeap:publicBpInterface<IMemoryHeap>
- {
- ......
- private:
- staticinlinesp<IMemoryHeap>find_heap(constsp<IBinder>&binder){
- returngHeapCache->find_heap(binder);
- }
- ......
- }
- voidBpMemoryHeap::assertReallyMapped()const
- {
- if(mHeapId==-1){
- //remotecallwithoutmLockheld,worsecasescenario,weendup
- //callingtransact()frommultiplethreads,butthat'snotaproblem,
- //onlymmapbelowmustbeinthecriticalsection.
- Parceldata,reply;
- data.writeInterfaceToken(IMemoryHeap::getInterfaceDescriptor());
- status_terr=remote()->transact(HEAP_ID,data,&reply);
- intparcel_fd=reply.readFileDescriptor();
- ssize_tsize=reply.readInt32();
- uint32_tflags=reply.readInt32();
- LOGE_IF(err,"binder=%ptransactionfailedfd=%d,size=%ld,err=%d(%s)",
- asBinder().get(),parcel_fd,size,err,strerror(-err));
- intfd=dup(parcel_fd);
- LOGE_IF(fd==-1,"cannotdupfd=%d,size=%ld,err=%d(%s)",
- parcel_fd,size,err,strerror(errno));
- intaccess=PROT_READ;
- if(!(flags&READ_ONLY)){
- access|=PROT_WRITE;
- }
- Mutex::Autolock_l(mLock);
- if(mHeapId==-1){
- mRealHeap=true;
- mBase=mmap(0,size,access,MAP_SHARED,fd,0);
- if(mBase==MAP_FAILED){
- LOGE("cannotmapBpMemoryHeap(binder=%p),size=%ld,fd=%d(%s)",
- asBinder().get(),size,fd,strerror(errno));
- close(fd);
- }else{
- mSize=size;
- mFlags=flags;
- android_atomic_write(fd,&mHeapId);
- }
- }
- }
- }
- Parceldata,reply;
- data.writeInterfaceToken(IMemoryHeap::getInterfaceDescriptor());
- status_terr=remote()->transact(HEAP_ID,data,&reply);
- intparcel_fd=reply.readFileDescriptor();
- ssize_tsize=reply.readInt32();
- uint32_tflags=reply.readInt32();
- ......
- intfd=dup(parcel_fd);
- ......
- Mutex::Autolock_l(mLock);
- if(mHeapId==-1){
- mRealHeap=true;
- mBase=mmap(0,size,access,MAP_SHARED,fd,0);
- if(mBase==MAP_FAILED){
- LOGE("cannotmapBpMemoryHeap(binder=%p),size=%ld,fd=%d(%s)",
- asBinder().get(),size,fd,strerror(errno));
- close(fd);
- }else{
- mSize=size;
- mFlags=flags;
- android_atomic_write(fd,&mHeapId);
- }
- }
从assertReallyMapped函数返回到assertMapped函数中:
- if(heap->mBase!=MAP_FAILED){
- Mutex::Autolock_l(mLock);
- if(mHeapId==-1){
- mBase=heap->mBase;
- mSize=heap->mSize;
- android_atomic_write(dup(heap->mHeapId),&mHeapId);
- }
- }else{
- //somethingwentwrong
- free_heap(binder);
- }
- mBase=heap->mBase;
- mSize=heap->mSize;
- android_atomic_write(dup(heap->mHeapId),&mHeapId);
这样,BpMemoryHeap对象中的匿名共享内存就准备就绪了,可以通过使用的它mBase成员变量来直接访问这块匿名共享内存。
至此,MemoryHeapBase类的实现就分析完了,下面我们继续分析MemoryBase类的实现。
2.MemoryBase
文章开始时说过,MemoryBase接口是建立在MemoryHeapBase接口的基础上的,它们都可以作为一个Binder对象来在进程间进行数据共享,它们的关系如下所示:
MemoryBase类包含了一个成员变量mHeap,它的类型的IMemoryHeap,MemoryBase类所代表的匿名共享内存就是通过这个成员变量来实现的。
与MemoryHeapBase的分析过程一样,我们先来看MemoryBase类在Server端的实现,然后再来看它在Client端的实现。
MemoryBase在Server端实现的类图如下所示:
MemoryBase类在Server端的实现与MemoryHeapBase类在Server端的实现是类似的,这里只要把IMemory类换成IMemoryHeap类、把BnMemory类换成BnMemoryHeap类以及MemoryBase类换成MemoryHeapBase类就变成是MemoryHeapBase类在Server端的实现了,因此,我们这里只简单分析IMemory类和MemoryBase类的实现。
IMemory类定义了MemoryBase类所需要实现的接口,这个类定义在frameworks/base/include/binder/IMemory.h文件中:
- classIMemory:publicIInterface
- {
- public:
- ......
- virtualsp<IMemoryHeap>getMemory(ssize_t*offset=0,size_t*size=0)const=0;
- ......
- void*pointer()const;
- size_tsize()const;
- ssize_toffset()const;
- };
IMemory类本身实现了pointer、size和offset三个成员函数,因此,它的子类,即MemoryBase类,只需要实现getMemory成员函数就可以了。IMemory类的实现定义在frameworks/base/libs/binder/IMemory.cpp文件中:
- void*IMemory::pointer()const{
- ssize_toffset;
- sp<IMemoryHeap>heap=getMemory(&offset);
- void*constbase=heap!=0?heap->base():MAP_FAILED;
- if(base==MAP_FAILED)
- return0;
- returnstatic_cast<char*>(base)+offset;
- }
- size_tIMemory::size()const{
- size_tsize;
- getMemory(NULL,&size);
- returnsize;
- }
- ssize_tIMemory::offset()const{
- ssize_toffset;
- getMemory(&offset);
- returnoffset;
- }
- classMemoryBase:publicBnMemory
- {
- public:
- MemoryBase(constsp<IMemoryHeap>&heap,ssize_toffset,size_tsize);
- ......
- virtualsp<IMemoryHeap>getMemory(ssize_t*offset,size_t*size)const;
- ......
- private:
- size_tmSize;
- ssize_tmOffset;
- sp<IMemoryHeap>mHeap;
- };
- MemoryBase::MemoryBase(constsp<IMemoryHeap>&heap,
- ssize_toffset,size_tsize)
- :mSize(size),mOffset(offset),mHeap(heap)
- {
- }
- sp<IMemoryHeap>MemoryBase::getMemory(ssize_t*offset,size_t*size)const
- {
- if(offset)*offset=mOffset;
- if(size)*size=mSize;
- returnmHeap;
- }
成员函数getMemory的实现很简单,只是简单地返回内部的MemoryHeapBase对象的IMemoryHeap接口,如果传进来的参数offset和size不为NULL,还会把其内部维护的这部分匿名共享内存在整个匿名共享内存块中的偏移位置以及这部分匿名共享内存的大小返回给调用者。
这里我们可以看出,MemoryBase在Server端的实现只是简单地封装了MemoryHeapBase的实现。
下面我们再来看MemoryBase类在Client端的实现,同样,先看它们的类图关系:
这个图中我们可以看出,MemoryBase类在Client端的实现与MemoryHeapBase类在Client端的实现是类似的,这里只要把IMemory类换成IMemoryHeap类以及把BpMemory类换成BpMemoryHeap类就变成是MemoryHeapBase类在Client端的实现了,因此,我们这里只简单分析BpMemory类的实现,前面已经分析过IMemory类的实现了。
BpMemory类实现在frameworks/base/libs/binder/IMemory.cpp文件中,我们先看它的声明:
- classBpMemory:publicBpInterface<IMemory>
- {
- public:
- BpMemory(constsp<IBinder>&impl);
- virtual~BpMemory();
- virtualsp<IMemoryHeap>getMemory(ssize_t*offset=0,size_t*size=0)const;
- private:
- mutablesp<IMemoryHeap>mHeap;
- mutablessize_tmOffset;
- mutablesize_tmSize;
- };
下面我们就看一下BpMemory类的成员函数getMemory的实现:
- sp<IMemoryHeap>BpMemory::getMemory(ssize_t*offset,size_t*size)const
- {
- if(mHeap==0){
- Parceldata,reply;
- data.writeInterfaceToken(IMemory::getInterfaceDescriptor());
- if(remote()->transact(GET_MEMORY,data,&reply)==NO_ERROR){
- sp<IBinder>heap=reply.readStrongBinder();
- ssize_to=reply.readInt32();
- size_ts=reply.readInt32();
- if(heap!=0){
- mHeap=interface_cast<IMemoryHeap>(heap);
- if(mHeap!=0){
- mOffset=o;
- mSize=s;
- }
- }
- }
- }
- if(offset)*offset=mOffset;
- if(size)*size=mSize;
- returnmHeap;
- }
至此,MemoryBase类的实现就分析完了,下面我们将通过一个实例来说明如何使用MemoryBase类在进程间进行内存共享,因为MemoryBase内部使用了MemoryHeapBase类,所以,这个例子同时也可以说明MemoryHeapBase类的使用方法。
3. MemoryHeapBas类e和MemoryBase类的使用示例
在这个例子中,我们将在Android源代码工程的external目录中创建一个ashmem源代码工程,它里面包括两个应用程序,一个是Server端应用程序SharedBufferServer,它提供一段共享内存来给Client端程序使用,一个是Client端应用程序SharedBufferClient,它简单地对Server端提供的共享内存进行读和写的操作。Server端应用程序SharedBufferServer和Client端应用程序SharedBufferClient通过Binder进程间通信机制来交互,因此,我们需要定义自己的Binder对象接口ISharedBuffer。Server端应用程序SharedBufferServer在内部实现了一个服务SharedBufferService,这个服务托管给Service Manager来管理,因此,Client端应用程序SharedBufferClient可以向Service Manager请求这个SharedBufferService服务的一个远接接口,然后就可以通过这个服务来操作Server端提供的这段共享内存了。
这个工程由三个模块组成,第一个模块定义服务接口,它的相关源代码位于external/ashmem/common目录下,第二个模块实现Server端应用程序SharedBufferServer,它的相关源代码位于external/ashmem/server目录下,第三个模块实现Client端应用程序SharedBufferClient,它的相关源代码码位于external/ashmem/client目录下。
首先来看common模块中的服务接口的定义。在external/ashmem/common目录下,有两个源文件ISharedBuffer.h和ISharedBuffer.cpp。源文件ISharedBuffer.h定义了服务的接口:
- #ifndefISHAREDBUFFER_H_
- #defineISHAREDBUFFER_H_
- #include<utils/RefBase.h>
- #include<binder/IInterface.h>
- #include<binder/Parcel.h>
- #defineSHARED_BUFFER_SERVICE"shy.luo.SharedBuffer"
- #defineSHARED_BUFFER_SIZE4
- usingnamespaceandroid;
- classISharedBuffer:publicIInterface
- {
- public:
- DECLARE_META_INTERFACE(SharedBuffer);
- virtualsp<IMemory>getBuffer()=0;
- };
- classBnSharedBuffer:publicBnInterface<ISharedBuffer>
- {
- public:
- virtualstatus_tonTransact(uint32_tcode,constParcel&data,Parcel*reply,uint32_tflags=0);
- };
- #endif
源代文件ISharedBuffer.cpp文件定义了一个在Client端使用的BpSharedBuffer接口,它是指向运行在Server端的实现了ISharedBuffer接口的内存共享服务的远程接口,同时,在这个文件里面,也实现了BnSharedBuffer类的onTransact成员函数:
- #defineLOG_TAG"ISharedBuffer"
- #include<utils/Log.h>
- #include<binder/MemoryBase.h>
- #include"ISharedBuffer.h"
- usingnamespaceandroid;
- enum
- {
- GET_BUFFER=IBinder::FIRST_CALL_TRANSACTION
- };
- classBpSharedBuffer:publicBpInterface<ISharedBuffer>
- {
- public:
- BpSharedBuffer(constsp<IBinder>&impl)
- :BpInterface<ISharedBuffer>(impl)
- {
- }
- public:
- sp<IMemory>getBuffer()
- {
- Parceldata;
- data.writeInterfaceToken(ISharedBuffer::getInterfaceDescriptor());
- Parcelreply;
- remote()->transact(GET_BUFFER,data,&reply);
- sp<IMemory>buffer=interface_cast<IMemory>(reply.readStrongBinder());
- returnbuffer;
- }
- };
- IMPLEMENT_META_INTERFACE(SharedBuffer,"shy.luo.ISharedBuffer");
- status_tBnSharedBuffer::onTransact(uint32_tcode,constParcel&data,Parcel*reply,uint32_tflags)
- {
- switch(code)
- {
- caseGET_BUFFER:
- {
- CHECK_INTERFACE(ISharedBuffer,data,reply);
- sp<IMemory>buffer=getBuffer();
- if(buffer!=NULL)
- {
- reply->writeStrongBinder(buffer->asBinder());
- }
- returnNO_ERROR;
- }
- default:
- {
- returnBBinder::onTransact(code,data,reply,flags);
- }
- }
- }
接下来,我们再来看看server模块的实现。在external/ashmem/common目录下,只有一个源文件SharedBufferServer.cpp,它实现了内存共享服务SharedBufferService:
- #defineLOG_TAG"SharedBufferServer"
- #include<utils/Log.h>
- #include<binder/MemoryBase.h>
- #include<binder/MemoryHeapBase.h>
- #include<binder/IServiceManager.h>
- #include<binder/IPCThreadState.h>
- #include"../common/ISharedBuffer.h"
- classSharedBufferService:publicBnSharedBuffer
- {
- public:
- SharedBufferService()
- {
- sp<MemoryHeapBase>heap=newMemoryHeapBase(SHARED_BUFFER_SIZE,0,"SharedBuffer");
- if(heap!=NULL)
- {
- mMemory=newMemoryBase(heap,0,SHARED_BUFFER_SIZE);
- int32_t*data=(int32_t*)mMemory->pointer();
- if(data!=NULL)
- {
- *data=0;
- }
- }
- }
- virtual~SharedBufferService()
- {
- mMemory=NULL;
- }
- public:
- staticvoidinstantiate()
- {
- defaultServiceManager()->addService(String16(SHARED_BUFFER_SERVICE),newSharedBufferService());
- }
- virtualsp<IMemory>getBuffer()
- {
- returnmMemory;
- }
- private:
- sp<MemoryBase>mMemory;
- };
- intmain(intargc,char**argv)
- {
- SharedBufferService::instantiate();
- ProcessState::self()->startThreadPool();
- IPCThreadState::self()->joinThreadPool();
- return0;
- }
在Server端应用程序的入口函数main中,首先是调用SharedBufferService静态成员函数instantiate函数来创建一个SharedBufferService实例,然后通过defaultServiceManager函数来获得系统中的Service Manager接口,最后通过这个Service Manager接口的addService函数来把这个SharedBufferService服务添加到Service Manager中去,这样,Client端就可以通过Service Manager来获得这个共享内存服务了。有关Service Manager的实现,请参考前面一篇文章浅谈Service Manager成为Android进程间通信(IPC)机制Binder守护进程之路,而用来获取Service Manager接口的defaultServiceManager函数的实现可以参考另外一篇文章浅谈Android系统进程间通信(IPC)机制Binder中的Server和Client获得Service Manager接口之路。初始化好这个共享内存服务之后,程序就通过ProcessState::self()->startThreadPool()函数来创建一个线程等待Client端来请求服务了,最后,程序的主线程也通过IPCThreadState::self()->joinThreadPool()函数来进入到等待Client端来请求服务的状态中。
我们还需要为这个Server端应用程序编译一个编译脚本,在external/ashmem/server目录下,新建一个Android.mk文件,它的内容如下所示:
- LOCAL_PATH:=$(callmy-dir)
- include$(CLEAR_VARS)
- LOCAL_MODULE_TAGS:=optional
- LOCAL_SRC_FILES:=../common/ISharedBuffer.cpp\
- SharedBufferServer.cpp
- LOCAL_SHARED_LIBRARIES:=libcutilslibutilslibbinder
- LOCAL_MODULE:=SharedBufferServer
- include$(BUILD_EXECUTABLE)
- #defineLOG_TAG"SharedBufferClient"
- #include<utils/Log.h>
- #include<binder/MemoryBase.h>
- #include<binder/IServiceManager.h>
- #include"../common/ISharedBuffer.h"
- intmain()
- {
- sp<IBinder>binder=defaultServiceManager()->getService(String16(SHARED_BUFFER_SERVICE));
- if(binder==NULL)
- {
- printf("Failedtogetservice:%s.\n",SHARED_BUFFER_SERVICE);
- return-1;
- }
- sp<ISharedBuffer>service=ISharedBuffer::asInterface(binder);
- if(service==NULL)
- {
- return-2;
- }
- sp<IMemory>buffer=service->getBuffer();
- if(buffer==NULL)
- {
- return-3;
- }
- int32_t*data=(int32_t*)buffer->pointer();
- if(data==NULL)
- {
- return-4;
- }
- printf("Thevalueofthesharedbufferis%d.\n",*data);
- *data=*data+1;
- printf("Addvalue1tothesharedbuffer.\n");
- return0;
- }
同样,我们需要为这个Client端应用程序编译一个编译脚本,在external/ashmem/client目录下,新建一个Android.mk文件,它的内容如下所示:
- LOCAL_PATH:=$(callmy-dir)
- include$(CLEAR_VARS)
- LOCAL_MODULE_TAGS:=optional
- LOCAL_SRC_FILES:=../common/ISharedBuffer.cpp\
- SharedBufferClient.cpp
- LOCAL_SHARED_LIBRARIES:=libcutilslibutilslibbinder
- LOCAL_MODULE:=SharedBufferClient
- include$(BUILD_EXECUTABLE)
执行以下命令进行编译和打包:
- USER-NAME@MACHINE-NAME:~/Android$mmmexternal/ashmem/server
- USER-NAME@MACHINE-NAME:~/Android$mmmexternal/ashmem/client
- USER-NAME@MACHINE-NAME:~/Android$makesnod
至此,我们就可以运行模拟器来验证我们的程序了。关于如何在Android源代码工程中运行模拟器,请参考在Ubuntu上下载、编译和安装Android最新源代码一文。
执行以下命令启动模拟器:
- USER-NAME@MACHINE-NAME:~/Android$emulator
- USER-NAME@MACHINE-NAME:~/Android$adbshell
- luo@ubuntu-11-04:~/Android$adbshell
- root@android:/#cdsystem/bin
- root@android:/system/bin#./SharedBufferServer&
- root@android:/system/bin#./SharedBufferClient
- Thevalueofthesharedbufferis0.
- Addvalue1tothesharedbuffer.
- root@android:/system/bin#./SharedBufferClient
- Thevalueofthesharedbufferis1.
- Addvalue1tothesharedbuffer.
- root@android:/system/bin#./SharedBufferClient
- Thevalueofthesharedbufferis2.
- Addvalue1tothesharedbuffer.
- root@android:/system/bin#./SharedBufferClient
- Thevalueofthesharedbufferis3.
- Addvalue1tothesharedbuffer.
至此,Android系统匿名共享内存的C++调用接口MemoryHeapBase和MemoryBase就分析完成了。
在Android系统中,针对移动设备内存空间有限的特点,提供了一种在进程间共享数据的机制:匿名共享内存,它能够辅助内存管理系统来有效地管理内存,它的实现原理我们在前面已经分析过了。为了方便使用匿名共享内存机制,系统还提供了Java调用接口(MemoryFile)和C++调用接口(MemoryHeapBase、MemoryBase),Java接口在前面也已经分析过了,本文中将继续分析它的C++接口。
在前面一篇文章Android系统匿名共享内存Ashmem(Anonymous Shared Memory)驱动程序源代码分析中,我们分析了匿名共享内存驱动程序Ashmem的实现,重点介绍了它是如何辅助内存管理系统来有效地管理内存的,简单来说,它就是给使用者提供锁机制来辅助管理内存,当我们申请了一大块匿名共享内存时,中间过程有一部分不需要使用时,我们就可以将这一部分内存块解锁,这样内存管理系统就可以把它回收回去了。接着又在前面一篇文章Android系统匿名共享内存Ashmem(Anonymous Shared Memory)在进程间共享的原理分析中,我们分析了匿名共享内存是如何通过Binder进程间通信机制来实现在进程间共享的,简单来说,就是每一个匿名共享内存块都是一个文件,当我们需要在进程间共享时,就把这个文件的打开描述符通过Binder进程间通信机制传递给另一外进程,在传递的过程中,Binder驱动程序就通过这个复制这个打开文件描述符到目标进程中去,从而实现数据共享。在文章Android系统匿名共享内存Ashmem(Anonymous Shared Memory)简要介绍和学习计划中,我们介绍了如何在Android应用程序中使用匿名共享内存,主要是通过应用程序框架层提供的MemoryFile接口来使用的,而MemoryFile接口是通过JNI方法调用到系统运行时库层中的匿名共享内存C接口,最终通过这些C接口来使用内核空间中的匿名共享内存驱动模块。为了方便开发者灵活地使用匿名共享内存,Android系统在应用程序框架层中还提供了使用匿名共享内存的C++接口,例如,Android应用程序四大组件之一Content Provider,它在应用程序间共享数据时,就是通过匿名共享内存机制来实现,但是它并不是通过MemoryFile接口来使用,而是通过调用C++接口中的MemoryBase类和MemoryHeapBase类来使用。在接下来的内容中,我们就详细分析MemoryHeapBase类和MemoryBase类的实现,以及它们是如何实现在进程间共享数据的。
如果我们想在进程间共享一个完整的匿名共享内存块,可以通过使用MemoryHeapBase接口来实现,如果我们只想在进程间共享一个匿名共享内存块中的其中一部分时,就可以通过MemoryBase接口来实现。MemoryBase接口是建立在MemoryHeapBase接口的基础上面的,它们都可以作为一个Binder对象来在进程间传输,因此,希望读者在继续阅读本文之前,对Android系统的Binder进程间通信机制有一定的了解,具体可以参考前面一篇文章Android进程间通信(IPC)机制Binder简要介绍和学习计划。下面我们就首先分析MemoryHeapBase接口的实现,然后再分析MemoryBase接口的实现,最后,通过一个实例来说明它们是如何使用的。
1. MemoryHeapBase
前面说到,MemoryHeapBase类的对象可以作为Binder对象在进程间传输,作为一个Binder对象,就有Server端对象和Client端引用的概念,其中,Server端对象必须要实现一个BnInterface接口,而Client端引用必须要实现一个BpInterface接口。下面我们就先看一下MemoryHeapBase在Server端实现的类图:
这个类图中的类可以划分为两部分,一部分是和业务相关的,即跟匿名共享内存操作相关的类,包括MemoryHeapBase、IMemoryBase和RefBase三个类,另一部分是和Binder机制相关的,包括IInterface、BnInterface、BnMemoryHeap、IBinder、BBinder、ProcessState和IPCThreadState七个类。
我们先来看跟匿名共享内存业务相关的这部分类的逻辑关系。IMemoryBase定义了匿名共享内操作的接口,而MemoryHeapBase是作为Binder机制中的Server角色的,因此,它需要实现IMemoryBase接口,此外,MemoryHeapBase还继承了RefBase类。从前面一篇文章Android系统的智能指针(轻量级指针、强指针和弱指针)的实现原理分析中,我们知道,继承了RefBase类的子类,它们的对象都可以结合Android系统的智能指针来使用,因此,我们在实例化MemoryHeapBase类时,可以通过智能指针来管理它们的生命周期。
再来看和Binder机制相关的这部分类的逻辑关系。从Android系统进程间通信(IPC)机制Binder中的Server启动过程源代码分析这篇文章中,我们知道,所有的Binder对象都必须实现IInterface接口,无论是Server端实体对象,还是Client端引用对象,通过这个接口的asBinder成员函数我们可以获得Binder对象的IBinder接口,然后通过Binder驱动程序把它传输给另外一个进程。当一个类的对象作为Server端的实体对象时,它还必须实现一个模板类BnInterface,这里负责实例化模板类BnInterface的类便是BnMemoryHeap类了,它里面有一个重要的成员函数onTransact,当Client端引用请求Server端对象执行命令时,Binder系统就会调用BnMemoryHeap类的onTransact成员函数来执行具体的命令。当一个类的对象作为Server端的实体对象时,它还要继承于BBinder类,这是一个实现了IBinder接口的类,它里面有一个重要的成员函数transact,当我们从Server端线程中接收到Client端的请求时,就会调用注册在这个线程中的BBinder对象的transact函数来处理这个请求,而这个transact函数会将这些Client端请求转发给BnMemoryHeap类的onTransact成员函数来处理。最后,ProcessState和IPCThreadState两个类是负责和Binder驱动程序打交道的,其中,ProcessState负责打开Binder设备文件/dev/binder,打开了这个Binder设备文件后,就会得到一个打开设备文件描述符,而IPCThreadState就是通过这个设备文件描述符来和Binder驱动程序进行交互的,例如它通过一个for循环来不断地等待Binder驱动程序通知它有新的Client端请求到来了,一旦有新的Client端请求到来,它就会调用相应的BBinder对象的transact函数来处理。
本文我们主要是要关注和匿名共享内存业务相关的这部分类,即IMemoryBase和MemoryHeapBase类的实现,和Binder机制相关的这部分类的实现,可以参考Android进程间通信(IPC)机制Binder简要介绍和学习计划一文。
IMemoryBase类主要定义了几个重要的操作匿名共享内存的方法,它定义在frameworks/base/include/binder/IMemory.h文件中:
- classIMemoryHeap:publicIInterface
- {
- public:
- ......
- virtualintgetHeapID()const=0;
- virtualvoid*getBase()const=0;
- virtualsize_tgetSize()const=0;
- ......
- };
成员函数getHeapID是用来获得匿名共享内存块的打开文件描述符的;成员函数getBase是用来获得匿名共享内存块的基地址的,有了这个地址之后,我们就可以在程序里面直接访问这块共享内存了;成员函数getSize是用来获得匿名共享内存块的大小的。
MemoryHeapBase类主要用来实现上面IMemoryBase类中列出来的几个成员函数的,这个类声明在frameworks/base/include/binder/MemoryHeapBase.h文件中:
- classMemoryHeapBase:publicvirtualBnMemoryHeap
- {
- public:
- ......
- /*
- *mapsmemoryfromashmem,withthegivennamefordebugging
- */
- MemoryHeapBase(size_tsize,uint32_tflags=0,charconst*name=NULL);
- ......
- /*implementIMemoryHeapinterface*/
- virtualintgetHeapID()const;
- virtualvoid*getBase()const;
- virtualsize_tgetSize()const;
- ......
- private:
- intmFD;
- size_tmSize;
- void*mBase;
- ......
- }
- MemoryHeapBase::MemoryHeapBase(size_tsize,uint32_tflags,charconst*name)
- :mFD(-1),mSize(0),mBase(MAP_FAILED),mFlags(flags),
- mDevice(0),mNeedUnmap(false)
- {
- constsize_tpagesize=getpagesize();
- size=((size+pagesize-1)&~(pagesize-1));
- intfd=ashmem_create_region(name==NULL?"MemoryHeapBase":name,size);
- LOGE_IF(fd<0,"errorcreatingashmemregion:%s",strerror(errno));
- if(fd>=0){
- if(mapfd(fd,size)==NO_ERROR){
- if(flags&READ_ONLY){
- ashmem_set_prot_region(fd,PROT_READ);
- }
- }
- }
- }
MemoryHeapBase类创建的匿名共享内存是以页为单位的,页的大小一般为4K,但是是可以设置的,这个函数首先通过getpagesize函数获得系统中一页内存的大小值,然后把size参数对齐到页大小去,即如果size不是页大小的整数倍时,就增加它的大小,使得它的值为页大小的整数倍:
- constsize_tpagesize=getpagesize();
- size=((size+pagesize-1)&~(pagesize-1));
- intfd=ashmem_create_region(name==NULL?"MemoryHeapBase":name,size);
得到了这个匿名共享内存的文件描述符后,还需要调用mapfd成函数把它映射到进程地址空间去:
- status_tMemoryHeapBase::mapfd(intfd,size_tsize,uint32_toffset)
- {
- ......
- if((mFlags&DONT_MAP_LOCALLY)==0){
- void*base=(uint8_t*)mmap(0,size,
- PROT_READ|PROT_WRITE,MAP_SHARED,fd,offset);
- ......
- mBase=base;
- ......
- }else{
- ......
- }
- mFD=fd;
- mSize=size;
- returnNO_ERROR;
- }
- void*base=(uint8_t*)mmap(0,size,
- PROT_READ|PROT_WRITE,MAP_SHARED,fd,offset);
回到前面MemoryHeapBase类的构造函数中,将匿名共享内存映射到本进程的地址空间去后,还看继续设置这块匿名共享内存的读写属性:
- if(fd>=0){
- if(mapfd(fd,size)==NO_ERROR){
- if(flags&READ_ONLY){
- ashmem_set_prot_region(fd,PROT_READ);
- }
- }
- }
这样,通过这个构造函数,一块匿名共享内存就建立好了,其余的三个成员函数getHeapID、getBase和getSize就简单了:
- intMemoryHeapBase::getHeapID()const{
- returnmFD;
- }
- void*MemoryHeapBase::getBase()const{
- returnmBase;
- }
- size_tMemoryHeapBase::getSize()const{
- returnmSize;
- }
这个类图中的类也是可以划分为两部分,一部分是和业务相关的,即跟匿名共享内存操作相关的类,包括BpMemoryHeap、IMemoryBase和RefBase三个类,另一部分是和Binder机制相关的,包括IInterface、BpInterface、BpRefBase、IBinder、BpBinder、ProcessState和IPCThreadState七个类。
在和匿名共享内存操作相关的类中,BpMemoryHeap类是前面分析的MemoryHeapBase类在Client端进程的远接接口类,当Client端进程从Service Manager或者其它途径获得了一个MemoryHeapBase对象的引用之后,就会在本地创建一个BpMemoryHeap对象来代表这个引用。BpMemoryHeap类同样是要实现IMemoryHeap接口,同时,它是从RefBase类继承下来的,因此,它可以与智能指针来结合使用。
在和Binder机制相关的类中,和Server端实现不一样的地方是,Client端不需要实现BnInterface和BBinder两个类,但是需要实现BpInterface、BpRefBase和BpBinder三个类。BpInterface类继承于BpRefBase类,而在BpRefBase类里面,有一个成员变量mRemote,它指向一个BpBinder对象,当BpMemoryHeap类需要向Server端对象发出请求时,它就会通过这个BpBinder对象的transact函数来发出这个请求。这里的BpBinder对象是如何知道要向哪个Server对象发出请深圳市的呢?它里面有一个成员变量mHandle,它表示的是一个Server端Binder对象的引用值,BpBinder对象就是要通过这个引用值来把请求发送到相应的Server端对象去的了,这个引用值与Server端Binder对象的对应关系是在Binder驱动程序内部维护的。这里的ProcessSate类和IPCThreadState类的作用和在Server端的作用是类似的,它们都是负责和底层的Binder驱动程序进行交互,例如,BpBinder对象的transact函数就通过线程中的IPCThreadState对象来将Client端请求发送出去的。这些实现具体可以参考Android系统进程间通信(IPC)机制Binder中的Client获得Server远程接口过程源代码分析一文。
这里我们主要关注BpMemoryHeap类是如何实现IMemoryHeap接口的,这个类声明和定义在frameworks/base/libs/binder/IMemory.cpp文件中:
- classBpMemoryHeap:publicBpInterface<IMemoryHeap>
- {
- public:
- BpMemoryHeap(constsp<IBinder>&impl);
- ......
- virtualintgetHeapID()const;
- virtualvoid*getBase()const;
- virtualsize_tgetSize()const;
- ......
- private:
- mutablevolatileint32_tmHeapId;
- mutablevoid*mBase;
- mutablesize_tmSize;
- ......
- }
- BpMemoryHeap::BpMemoryHeap(constsp<IBinder>&impl)
- :BpInterface<IMemoryHeap>(impl),
- mHeapId(-1),mBase(MAP_FAILED),mSize(0),mFlags(0),mRealHeap(false)
- {
- }
其余三个成员函数getHeapID、getBase和getSize的实现是类似的:
- intBpMemoryHeap::getHeapID()const{
- assertMapped();
- returnmHeapId;
- }
- void*BpMemoryHeap::getBase()const{
- assertMapped();
- returnmBase;
- }
- size_tBpMemoryHeap::getSize()const{
- assertMapped();
- returnmSize;
- }
- voidBpMemoryHeap::assertMapped()const
- {
- if(mHeapId==-1){
- sp<IBinder>binder(const_cast<BpMemoryHeap*>(this)->asBinder());
- sp<BpMemoryHeap>heap(static_cast<BpMemoryHeap*>(find_heap(binder).get()));
- heap->assertReallyMapped();
- if(heap->mBase!=MAP_FAILED){
- Mutex::Autolock_l(mLock);
- if(mHeapId==-1){
- mBase=heap->mBase;
- mSize=heap->mSize;
- android_atomic_write(dup(heap->mHeapId),&mHeapId);
- }
- }else{
- //somethingwentwrong
- free_heap(binder);
- }
- }
- }
在frameworks/base/libs/binder/IMemory.cpp文件中,定义了一个全局变量gHeapCache:
- staticsp<HeapCache>gHeapCache=newHeapCache();
它的类型为HeapCache,这也是一个定义在frameworks/base/libs/binder/IMemory.cpp文件的类,它里面维护了本进程中所有的MemoryHeapBase对象的引用。由于在Client端进程中,可能会有多个引用,即多个BpMemoryHeap对象,对应同一个MemoryHeapBase对象(这是由于可以用同一个BpBinder对象来创建多个BpMemoryHeap对象),因此,当第一个BpMemoryHeap对象在本进程中映射好这块匿名共享内存之后,后面的BpMemoryHeap对象就可以直接使用了,不需要再映射一次,当然重新再映射一次没有害处,但是会是多此一举,Google在设计这个类时,可以说是考虑得非常周到的。
我们来看一下HeapCache的实现:
- classHeapCache:publicIBinder::DeathRecipient
- {
- public:
- HeapCache();
- virtual~HeapCache();
- ......
- sp<IMemoryHeap>find_heap(constsp<IBinder>&binder);
- voidfree_heap(constsp<IBinder>&binder);
- sp<IMemoryHeap>get_heap(constsp<IBinder>&binder);
- ......
- private:
- //ForIMemory.cpp
- structheap_info_t{
- sp<IMemoryHeap>heap;
- int32_tcount;
- };
- ......
- MutexmHeapCacheLock;
- KeyedVector<wp<IBinder>,heap_info_t>mHeapCache;
- };
这里,我们主要看一下find_heap函数的实现:
- sp<IMemoryHeap>HeapCache::find_heap(constsp<IBinder>&binder)
- {
- Mutex::Autolock_l(mHeapCacheLock);
- ssize_ti=mHeapCache.indexOfKey(binder);
- if(i>=0){
- heap_info_t&info=mHeapCache.editValueAt(i);
- LOGD_IF(VERBOSE,
- "foundbinder=%p,heap=%p,size=%d,fd=%d,count=%d",
- binder.get(),info.heap.get(),
- static_cast<BpMemoryHeap*>(info.heap.get())->mSize,
- static_cast<BpMemoryHeap*>(info.heap.get())->mHeapId,
- info.count);
- android_atomic_inc(&info.count);
- returninfo.heap;
- }else{
- heap_info_tinfo;
- info.heap=interface_cast<IMemoryHeap>(binder);
- info.count=1;
- //LOGD("addingbinder=%p,heap=%p,count=%d",
- //binder.get(),info.heap.get(),info.count);
- mHeapCache.add(binder,info);
- returninfo.heap;
- }
- }
回到前面BpMemoryHeap类中的assertMapped函数中,如果本BpMemoryHeap对象中的mHeapID等于-1,那么就说明这个BpMemoryHeap对象中的匿名共享内存还没准备就绪,因此,需要执行一次映射匿名共享内存的操作。
在执行映射操作之作,先要看看在本进程中是否有其它映射到同一个MemoryHeapBase对象的BpMemoryHeap对象存在:
- sp<IBinder>binder(const_cast<BpMemoryHeap*>(this)->asBinder());
- sp<BpMemoryHeap>heap(static_cast<BpMemoryHeap*>(find_heap(binder).get()));
- classBpMemoryHeap:publicBpInterface<IMemoryHeap>
- {
- ......
- private:
- staticinlinesp<IMemoryHeap>find_heap(constsp<IBinder>&binder){
- returngHeapCache->find_heap(binder);
- }
- ......
- }
- voidBpMemoryHeap::assertReallyMapped()const
- {
- if(mHeapId==-1){
- //remotecallwithoutmLockheld,worsecasescenario,weendup
- //callingtransact()frommultiplethreads,butthat'snotaproblem,
- //onlymmapbelowmustbeinthecriticalsection.
- Parceldata,reply;
- data.writeInterfaceToken(IMemoryHeap::getInterfaceDescriptor());
- status_terr=remote()->transact(HEAP_ID,data,&reply);
- intparcel_fd=reply.readFileDescriptor();
- ssize_tsize=reply.readInt32();
- uint32_tflags=reply.readInt32();
- LOGE_IF(err,"binder=%ptransactionfailedfd=%d,size=%ld,err=%d(%s)",
- asBinder().get(),parcel_fd,size,err,strerror(-err));
- intfd=dup(parcel_fd);
- LOGE_IF(fd==-1,"cannotdupfd=%d,size=%ld,err=%d(%s)",
- parcel_fd,size,err,strerror(errno));
- intaccess=PROT_READ;
- if(!(flags&READ_ONLY)){
- access|=PROT_WRITE;
- }
- Mutex::Autolock_l(mLock);
- if(mHeapId==-1){
- mRealHeap=true;
- mBase=mmap(0,size,access,MAP_SHARED,fd,0);
- if(mBase==MAP_FAILED){
- LOGE("cannotmapBpMemoryHeap(binder=%p),size=%ld,fd=%d(%s)",
- asBinder().get(),size,fd,strerror(errno));
- close(fd);
- }else{
- mSize=size;
- mFlags=flags;
- android_atomic_write(fd,&mHeapId);
- }
- }
- }
- }
- Parceldata,reply;
- data.writeInterfaceToken(IMemoryHeap::getInterfaceDescriptor());
- status_terr=remote()->transact(HEAP_ID,data,&reply);
- intparcel_fd=reply.readFileDescriptor();
- ssize_tsize=reply.readInt32();
- uint32_tflags=reply.readInt32();
- ......
- intfd=dup(parcel_fd);
- ......
- Mutex::Autolock_l(mLock);
- if(mHeapId==-1){
- mRealHeap=true;
- mBase=mmap(0,size,access,MAP_SHARED,fd,0);
- if(mBase==MAP_FAILED){
- LOGE("cannotmapBpMemoryHeap(binder=%p),size=%ld,fd=%d(%s)",
- asBinder().get(),size,fd,strerror(errno));
- close(fd);
- }else{
- mSize=size;
- mFlags=flags;
- android_atomic_write(fd,&mHeapId);
- }
- }
从assertReallyMapped函数返回到assertMapped函数中:
- if(heap->mBase!=MAP_FAILED){
- Mutex::Autolock_l(mLock);
- if(mHeapId==-1){
- mBase=heap->mBase;
- mSize=heap->mSize;
- android_atomic_write(dup(heap->mHeapId),&mHeapId);
- }
- }else{
- //somethingwentwrong
- free_heap(binder);
- }
- mBase=heap->mBase;
- mSize=heap->mSize;
- android_atomic_write(dup(heap->mHeapId),&mHeapId);
这样,BpMemoryHeap对象中的匿名共享内存就准备就绪了,可以通过使用的它mBase成员变量来直接访问这块匿名共享内存。
至此,MemoryHeapBase类的实现就分析完了,下面我们继续分析MemoryBase类的实现。
2.MemoryBase
文章开始时说过,MemoryBase接口是建立在MemoryHeapBase接口的基础上的,它们都可以作为一个Binder对象来在进程间进行数据共享,它们的关系如下所示:
MemoryBase类包含了一个成员变量mHeap,它的类型的IMemoryHeap,MemoryBase类所代表的匿名共享内存就是通过这个成员变量来实现的。
与MemoryHeapBase的分析过程一样,我们先来看MemoryBase类在Server端的实现,然后再来看它在Client端的实现。
MemoryBase在Server端实现的类图如下所示:
MemoryBase类在Server端的实现与MemoryHeapBase类在Server端的实现是类似的,这里只要把IMemory类换成IMemoryHeap类、把BnMemory类换成BnMemoryHeap类以及MemoryBase类换成MemoryHeapBase类就变成是MemoryHeapBase类在Server端的实现了,因此,我们这里只简单分析IMemory类和MemoryBase类的实现。
IMemory类定义了MemoryBase类所需要实现的接口,这个类定义在frameworks/base/include/binder/IMemory.h文件中:
- classIMemory:publicIInterface
- {
- public:
- ......
- virtualsp<IMemoryHeap>getMemory(ssize_t*offset=0,size_t*size=0)const=0;
- ......
- void*pointer()const;
- size_tsize()const;
- ssize_toffset()const;
- };
IMemory类本身实现了pointer、size和offset三个成员函数,因此,它的子类,即MemoryBase类,只需要实现getMemory成员函数就可以了。IMemory类的实现定义在frameworks/base/libs/binder/IMemory.cpp文件中:
- void*IMemory::pointer()const{
- ssize_toffset;
- sp<IMemoryHeap>heap=getMemory(&offset);
- void*constbase=heap!=0?heap->base():MAP_FAILED;
- if(base==MAP_FAILED)
- return0;
- returnstatic_cast<char*>(base)+offset;
- }
- size_tIMemory::size()const{
- size_tsize;
- getMemory(NULL,&size);
- returnsize;
- }
- ssize_tIMemory::offset()const{
- ssize_toffset;
- getMemory(&offset);
- returnoffset;
- }
- classMemoryBase:publicBnMemory
- {
- public:
- MemoryBase(constsp<IMemoryHeap>&heap,ssize_toffset,size_tsize);
- ......
- virtualsp<IMemoryHeap>getMemory(ssize_t*offset,size_t*size)const;
- ......
- private:
- size_tmSize;
- ssize_tmOffset;
- sp<IMemoryHeap>mHeap;
- };
- MemoryBase::MemoryBase(constsp<IMemoryHeap>&heap,
- ssize_toffset,size_tsize)
- :mSize(size),mOffset(offset),mHeap(heap)
- {
- }
- sp<IMemoryHeap>MemoryBase::getMemory(ssize_t*offset,size_t*size)const
- {
- if(offset)*offset=mOffset;
- if(size)*size=mSize;
- returnmHeap;
- }
成员函数getMemory的实现很简单,只是简单地返回内部的MemoryHeapBase对象的IMemoryHeap接口,如果传进来的参数offset和size不为NULL,还会把其内部维护的这部分匿名共享内存在整个匿名共享内存块中的偏移位置以及这部分匿名共享内存的大小返回给调用者。
这里我们可以看出,MemoryBase在Server端的实现只是简单地封装了MemoryHeapBase的实现。
下面我们再来看MemoryBase类在Client端的实现,同样,先看它们的类图关系:
这个图中我们可以看出,MemoryBase类在Client端的实现与MemoryHeapBase类在Client端的实现是类似的,这里只要把IMemory类换成IMemoryHeap类以及把BpMemory类换成BpMemoryHeap类就变成是MemoryHeapBase类在Client端的实现了,因此,我们这里只简单分析BpMemory类的实现,前面已经分析过IMemory类的实现了。
BpMemory类实现在frameworks/base/libs/binder/IMemory.cpp文件中,我们先看它的声明:
- classBpMemory:publicBpInterface<IMemory>
- {
- public:
- BpMemory(constsp<IBinder>&impl);
- virtual~BpMemory();
- virtualsp<IMemoryHeap>getMemory(ssize_t*offset=0,size_t*size=0)const;
- private:
- mutablesp<IMemoryHeap>mHeap;
- mutablessize_tmOffset;
- mutablesize_tmSize;
- };
下面我们就看一下BpMemory类的成员函数getMemory的实现:
- sp<IMemoryHeap>BpMemory::getMemory(ssize_t*offset,size_t*size)const
- {
- if(mHeap==0){
- Parceldata,reply;
- data.writeInterfaceToken(IMemory::getInterfaceDescriptor());
- if(remote()->transact(GET_MEMORY,data,&reply)==NO_ERROR){
- sp<IBinder>heap=reply.readStrongBinder();
- ssize_to=reply.readInt32();
- size_ts=reply.readInt32();
- if(heap!=0){
- mHeap=interface_cast<IMemoryHeap>(heap);
- if(mHeap!=0){
- mOffset=o;
- mSize=s;
- }
- }
- }
- }
- if(offset)*offset=mOffset;
- if(size)*size=mSize;
- returnmHeap;
- }
至此,MemoryBase类的实现就分析完了,下面我们将通过一个实例来说明如何使用MemoryBase类在进程间进行内存共享,因为MemoryBase内部使用了MemoryHeapBase类,所以,这个例子同时也可以说明MemoryHeapBase类的使用方法。
3. MemoryHeapBas类e和MemoryBase类的使用示例
在这个例子中,我们将在Android源代码工程的external目录中创建一个ashmem源代码工程,它里面包括两个应用程序,一个是Server端应用程序SharedBufferServer,它提供一段共享内存来给Client端程序使用,一个是Client端应用程序SharedBufferClient,它简单地对Server端提供的共享内存进行读和写的操作。Server端应用程序SharedBufferServer和Client端应用程序SharedBufferClient通过Binder进程间通信机制来交互,因此,我们需要定义自己的Binder对象接口ISharedBuffer。Server端应用程序SharedBufferServer在内部实现了一个服务SharedBufferService,这个服务托管给Service Manager来管理,因此,Client端应用程序SharedBufferClient可以向Service Manager请求这个SharedBufferService服务的一个远接接口,然后就可以通过这个服务来操作Server端提供的这段共享内存了。
这个工程由三个模块组成,第一个模块定义服务接口,它的相关源代码位于external/ashmem/common目录下,第二个模块实现Server端应用程序SharedBufferServer,它的相关源代码位于external/ashmem/server目录下,第三个模块实现Client端应用程序SharedBufferClient,它的相关源代码码位于external/ashmem/client目录下。
首先来看common模块中的服务接口的定义。在external/ashmem/common目录下,有两个源文件ISharedBuffer.h和ISharedBuffer.cpp。源文件ISharedBuffer.h定义了服务的接口:
- #ifndefISHAREDBUFFER_H_
- #defineISHAREDBUFFER_H_
- #include<utils/RefBase.h>
- #include<binder/IInterface.h>
- #include<binder/Parcel.h>
- #defineSHARED_BUFFER_SERVICE"shy.luo.SharedBuffer"
- #defineSHARED_BUFFER_SIZE4
- usingnamespaceandroid;
- classISharedBuffer:publicIInterface
- {
- public:
- DECLARE_META_INTERFACE(SharedBuffer);
- virtualsp<IMemory>getBuffer()=0;
- };
- classBnSharedBuffer:publicBnInterface<ISharedBuffer>
- {
- public:
- virtualstatus_tonTransact(uint32_tcode,constParcel&data,Parcel*reply,uint32_tflags=0);
- };
- #endif
源代文件ISharedBuffer.cpp文件定义了一个在Client端使用的BpSharedBuffer接口,它是指向运行在Server端的实现了ISharedBuffer接口的内存共享服务的远程接口,同时,在这个文件里面,也实现了BnSharedBuffer类的onTransact成员函数:
- #defineLOG_TAG"ISharedBuffer"
- #include<utils/Log.h>
- #include<binder/MemoryBase.h>
- #include"ISharedBuffer.h"
- usingnamespaceandroid;
- enum
- {
- GET_BUFFER=IBinder::FIRST_CALL_TRANSACTION
- };
- classBpSharedBuffer:publicBpInterface<ISharedBuffer>
- {
- public:
- BpSharedBuffer(constsp<IBinder>&impl)
- :BpInterface<ISharedBuffer>(impl)
- {
- }
- public:
- sp<IMemory>getBuffer()
- {
- Parceldata;
- data.writeInterfaceToken(ISharedBuffer::getInterfaceDescriptor());
- Parcelreply;
- remote()->transact(GET_BUFFER,data,&reply);
- sp<IMemory>buffer=interface_cast<IMemory>(reply.readStrongBinder());
- returnbuffer;
- }
- };
- IMPLEMENT_META_INTERFACE(SharedBuffer,"shy.luo.ISharedBuffer");
- status_tBnSharedBuffer::onTransact(uint32_tcode,constParcel&data,Parcel*reply,uint32_tflags)
- {
- switch(code)
- {
- caseGET_BUFFER:
- {
- CHECK_INTERFACE(ISharedBuffer,data,reply);
- sp<IMemory>buffer=getBuffer();
- if(buffer!=NULL)
- {
- reply->writeStrongBinder(buffer->asBinder());
- }
- returnNO_ERROR;
- }
- default:
- {
- returnBBinder::onTransact(code,data,reply,flags);
- }
- }
- }
接下来,我们再来看看server模块的实现。在external/ashmem/common目录下,只有一个源文件SharedBufferServer.cpp,它实现了内存共享服务SharedBufferService:
- #defineLOG_TAG"SharedBufferServer"
- #include<utils/Log.h>
- #include<binder/MemoryBase.h>
- #include<binder/MemoryHeapBase.h>
- #include<binder/IServiceManager.h>
- #include<binder/IPCThreadState.h>
- #include"../common/ISharedBuffer.h"
- classSharedBufferService:publicBnSharedBuffer
- {
- public:
- SharedBufferService()
- {
- sp<MemoryHeapBase>heap=newMemoryHeapBase(SHARED_BUFFER_SIZE,0,"SharedBuffer");
- if(heap!=NULL)
- {
- mMemory=newMemoryBase(heap,0,SHARED_BUFFER_SIZE);
- int32_t*data=(int32_t*)mMemory->pointer();
- if(data!=NULL)
- {
- *data=0;
- }
- }
- }
- virtual~SharedBufferService()
- {
- mMemory=NULL;
- }
- public:
- staticvoidinstantiate()
- {
- defaultServiceManager()->addService(String16(SHARED_BUFFER_SERVICE),newSharedBufferService());
- }
- virtualsp<IMemory>getBuffer()
- {
- returnmMemory;
- }
- private:
- sp<MemoryBase>mMemory;
- };
- intmain(intargc,char**argv)
- {
- SharedBufferService::instantiate();
- ProcessState::self()->startThreadPool();
- IPCThreadState::self()->joinThreadPool();
- return0;
- }
在Server端应用程序的入口函数main中,首先是调用SharedBufferService静态成员函数instantiate函数来创建一个SharedBufferService实例,然后通过defaultServiceManager函数来获得系统中的Service Manager接口,最后通过这个Service Manager接口的addService函数来把这个SharedBufferService服务添加到Service Manager中去,这样,Client端就可以通过Service Manager来获得这个共享内存服务了。有关Service Manager的实现,请参考前面一篇文章浅谈Service Manager成为Android进程间通信(IPC)机制Binder守护进程之路,而用来获取Service Manager接口的defaultServiceManager函数的实现可以参考另外一篇文章浅谈Android系统进程间通信(IPC)机制Binder中的Server和Client获得Service Manager接口之路。初始化好这个共享内存服务之后,程序就通过ProcessState::self()->startThreadPool()函数来创建一个线程等待Client端来请求服务了,最后,程序的主线程也通过IPCThreadState::self()->joinThreadPool()函数来进入到等待Client端来请求服务的状态中。
我们还需要为这个Server端应用程序编译一个编译脚本,在external/ashmem/server目录下,新建一个Android.mk文件,它的内容如下所示:
- LOCAL_PATH:=$(callmy-dir)
- include$(CLEAR_VARS)
- LOCAL_MODULE_TAGS:=optional
- LOCAL_SRC_FILES:=../common/ISharedBuffer.cpp\
- SharedBufferServer.cpp
- LOCAL_SHARED_LIBRARIES:=libcutilslibutilslibbinder
- LOCAL_MODULE:=SharedBufferServer
- include$(BUILD_EXECUTABLE)
- #defineLOG_TAG"SharedBufferClient"
- #include<utils/Log.h>
- #include<binder/MemoryBase.h>
- #include<binder/IServiceManager.h>
- #include"../common/ISharedBuffer.h"
- intmain()
- {
- sp<IBinder>binder=defaultServiceManager()->getService(String16(SHARED_BUFFER_SERVICE));
- if(binder==NULL)
- {
- printf("Failedtogetservice:%s.\n",SHARED_BUFFER_SERVICE);
- return-1;
- }
- sp<ISharedBuffer>service=ISharedBuffer::asInterface(binder);
- if(service==NULL)
- {
- return-2;
- }
- sp<IMemory>buffer=service->getBuffer();
- if(buffer==NULL)
- {
- return-3;
- }
- int32_t*data=(int32_t*)buffer->pointer();
- if(data==NULL)
- {
- return-4;
- }
- printf("Thevalueofthesharedbufferis%d.\n",*data);
- *data=*data+1;
- printf("Addvalue1tothesharedbuffer.\n");
- return0;
- }
同样,我们需要为这个Client端应用程序编译一个编译脚本,在external/ashmem/client目录下,新建一个Android.mk文件,它的内容如下所示:
- LOCAL_PATH:=$(callmy-dir)
- include$(CLEAR_VARS)
- LOCAL_MODULE_TAGS:=optional
- LOCAL_SRC_FILES:=../common/ISharedBuffer.cpp\
- SharedBufferClient.cpp
- LOCAL_SHARED_LIBRARIES:=libcutilslibutilslibbinder
- LOCAL_MODULE:=SharedBufferClient
- include$(BUILD_EXECUTABLE)
执行以下命令进行编译和打包:
- USER-NAME@MACHINE-NAME:~/Android$mmmexternal/ashmem/server
- USER-NAME@MACHINE-NAME:~/Android$mmmexternal/ashmem/client
- USER-NAME@MACHINE-NAME:~/Android$makesnod
至此,我们就可以运行模拟器来验证我们的程序了。关于如何在Android源代码工程中运行模拟器,请参考在Ubuntu上下载、编译和安装Android最新源代码一文。
执行以下命令启动模拟器:
- USER-NAME@MACHINE-NAME:~/Android$emulator
- USER-NAME@MACHINE-NAME:~/Android$adbshell
- luo@ubuntu-11-04:~/Android$adbshell
- root@android:/#cdsystem/bin
- root@android:/system/bin#./SharedBufferServer&
- root@android:/system/bin#./SharedBufferClient
- Thevalueofthesharedbufferis0.
- Addvalue1tothesharedbuffer.
- root@android:/system/bin#./SharedBufferClient
- Thevalueofthesharedbufferis1.
- Addvalue1tothesharedbuffer.
- root@android:/system/bin#./SharedBufferClient
- Thevalueofthesharedbufferis2.
- Addvalue1tothesharedbuffer.
- root@android:/system/bin#./SharedBufferClient
- Thevalueofthesharedbufferis3.
- Addvalue1tothesharedbuffer.
至此,Android系统匿名共享内存的C++调用接口MemoryHeapBase和MemoryBase就分析完成了。
相关推荐
Android系统匿名共享内存Ashmem(Anonymous Shared Memory)在进程间共享的原理分析
C++ 调用Python发邮件 演示匿名管道 接管标准输入与输出C++ calling Python e-mail demo anonymous pipe to take over the standard input and output
国外著名的黑客组织Anonymous于近日发布了一款自行开发的操作系统,该系统被命名为Anonymous-OS,并将DuckDuckGo定为其默认的搜索引擎。 Anonymous-OS操作系统基于Ubuntu 11.10,使用Mate桌面,主要是出于教育目的,...
《Serv-U第5节Anonymous匿名用户设置》
2015年5月28日夜,绿盟科技的信息监测显示,匿名者(Anonymous)将于30日发动对华网络攻击,代号“OpChina”,疑与近期国际局势有关。消息一经传出,引起各界高度关注。鉴于以往历年该组织对华攻击的事件及防护经验...
管道(Pipe)实际是用于进程间通信的一段共享内存,创建管道的进程称为管道服务器,连接到一个管道的进程为管道客户机。一个进程在向管道写入数据后,另一进程就可以从管道的另一端将其读取出来。匿名管道...
Anonymous MethodAnonymous Method
匿名Github是一种在双盲论文提交中对Github存储库进行匿名处理之前将其匿名化的系统。 要立即开始使用Anonymous Github,请访问: : 确实,在双盲评审过程中,必须在线上附录中的开放科学数据或代码进行匿名处理...
java 匿名内部类的使用规范 java 匿名内部类的使用规范 java 匿名内部类的使用规范
引入 匿名函数 闭包 变量作用域 函数外部访问函数内部的局部变量...这意味着,函数运行在定义它的作用域中,而不是在调用它的作用域中。这是 JavaScript 的一大特色,将在后面说明。 把这两个因素结合在一起,就能
前端开源库-anonymous-insightAnonymous Insight,了解匿名向分析供应商(如Google Analytics)报告使用指标时如何使用您的工具。
以匿名方式查看其他人的LinkedIn个人资料 现在,您可以在其他人不知道您自己的情况下查看他们的linkedin个人资料! 支持语言:English
匿名浏览网页没有任何障碍! “快速访问anonymous.com服务。您的提供商或网络管理员禁止了您打开的URL?这不是问题。互联网必须是自由空间-使用该按钮以匿名方式进入该网站。它如何工作?请输入您需要的URL。它被...
很好的Java面试题第二,Anonymous Inner Class (匿名内部类) 是否可以extends(继承)其它类,是否可以implements(实现)interface(接口)?
java易混淆的地方,final, finally, finalize的区别,Anonymous Inner Class (匿名内部类) 是否可以extends(继承)其它类
Asynchronous Http Client for Android An asynchronous, callback-based Http client for Android built on top of Apache's HttpClient libraries. Changelog See what is new in version 1.4.9 released on 19th...
匿名聊天在线实时匹配,两人异性聊天室。为移动端设计。案例地址:后台管理地址:登录账号:admin登录密码:admin特性免注册随机房间时间随机情侣头像随机聊天话题支持发送自定义图片支持通过输入法键盘输入emoji并...
AnonymousClass:匿名类
安全匿名数据库查询,引入可信第三方的匿名数据库查询技术,并确保过程的安全性