转自http://hi.baidu.com/autumn%C0%B6%B8%F1%D7%D3/blog/item/54c363c688f6c0cad100608a.html
binder(一)
一、很多service都是通过binder机制来和客户端通讯交互的,我以MediaService(frameworks/base/Media/MediaServer/Main_mediaserver.cpp)来分析整个binder的使用。而在MediaService里面注册了提供媒体播放的服务程序,以下为所需要研究binder相关的类~
ServiceManager,这是Android OS的整个服务的管理程序
MediaService,这个程序里边注册了提供媒体播放的服务程序
MediaPlayerService,我们最后只分析这个
MediaPlayerClient,这个是与MediaPlayerService交互的客户端程序
二、processState(位于framework\base\libs\binder\ProcessState.cpp)
而在这个main_mediaserver.cpp中,只有少许的几句代码:
从这段代码中我们可以看到基本就是获取ProcessState实例,初始化MediaPlayer,Camera,Audio服务等,最后两句貌似是启动线程池以及加入线程池
2、有关于上面代码:sp<ProcessState> proc(ProcessState::self());解读
(1)探究下sp命令到底是什么呢,这个以后再看,现在可以先把SP当成一个普通的指针来看,如sp<ProcessState> == *ProcessState
(note:http://blog.csdn.net/fjv378/archive/2010/09/14/5883309.aspx)
(2)通过ProcessState::self得到一个ProcessState实例,再将这个实例赋值给proc变量,见ProcessState::self()源码
sp<ProcessState> ProcessState::self()
{
if (gProcess !=NULL) return gProcess;
AutoMutex_l(gProcessMutex);(锁保护)
if (gProcess ==NULL)gProcess = new ProcessState; //这里便是实例化ProcessState的地方,在ProcessState构造函数中,有句:mDriverFD(open_driver()),这个open_driver就是打开/dev/binder设备的,这个设备是专门用于IPC通讯建立的设备的
return gProcess;
}
(3)总结:下面是一个箭头示意,越往右表示越深入,来描述当启动时注册媒体播放的类做了哪些操作
A:main_mediaserver.cpp(注册媒体播放类)--->B:A中,sp<ProcessState> proc(ProcessState::self());(实例化ProcessState对象,再将这个实例赋值给proc变量)--->C:B中,也就是ProcessState::self()中,new了一个ProcessState出来--->D:ProcessState构造函数中,有句mDriverFD(open_driver)来打开设备,并判断mDriverFD是否>0(mDriverFD为int型,若打开成功则不等于0),如果>0,则将mDriverFD映射到内存中,mVMStart = mmap(0, BINDER_VM_SIZE,PROT_READ, MAP_PRIVATE | MAP_NORESERVE,mDriverFD, 0);--->E:open_driver中,返回int fd变量,其中fd = open("/dev/binder",O_RDWR);,至此真正打开内核的binder机制的,就是这句话了。这样就和内核的binder机制有了交互的通道,并映射mDriverFD到了内存中,这块内存和binder共享
(4)上面有句话是mmap(0,XXX,XXX,mDriverFD,0),那这个mmap是什么意思呢
首先这句话的大意是,将mDriverFD映射到内存中,这样内存的memcpy等操作就相当于write/read(mDriverFD)了
A、我们不得不说下这个mmap,mmap其实就是共享内存的方式之一,也是最有用的进程间通讯的方式。比如进程A和进程B,当同一块物理内存被映射到了A和B的地址空间时,进程A可以看到进程B对共享内存的数据的更新,反之亦然
B、mmap及其相关调用(通过映射将普通文件实现共享内存,进程可以访问普通文件那样对其访问,不必再read(),write()操作)
函数:void *mmap(void *start,size_t length,int prot,int flags,int fd,off_t offsize);
参数start一般设置为null,表示系统自动选定地址
参数length表示映射到空间的字节数,从开头offsize开始个字节开始算起,offsize一般设置为0表示从开头开始,length为BINDER_VM_SIZE(1*1024*1024--4096*2范围:8KB~1MB)
参数prot表示访问权限:有PROT_READ(可读) , PROT_WRITE (可写), PROT_EXEC (可执行), PROT_NONE(不可访问)
参数flags表示映射区域的各种特性:在调用mmap时,必须要指定是MAP_PRIVATE or MAP_SHARED
【1】MAP_SHARED:映射区域的写入数据会复制回文件内,而且允许其他映射该文件的进程共享。
【2】MAP_PRIVATE 对映射区域的写入操作会产生一个映射文件的复制,即私人的“写入时复制”(copy on write)对此区域作的任何修改都不会写回原来的文件内容。
【3】MAP_ANONYMOUS建立匿名映射。此时会忽略参数fd(还有个必要条件是fd=-1),不涉及文件,而且映射区域无法和其他进程共享。
【4】MAP_DENYWRITE只允许对映射区域的写入操作,其他对文件直接写入的操作将会被拒绝。
【5】MAP_LOCKED 将映射区域锁定住,这表示该区域不会被置换(swap)。
参数fd,要映射到内存的文件描述符,如之前的mDriverFD
C、系统的munmap调用,表示解除一个映射关系
函数:int munmap( void * addr, size_t len )
addr代表mmap返回的地址,len代表映射区的大小。当解除后再对地址的访问将发生错误
D、系统的msync调用,实现硬盘上的内容与共享区的内容一致(默认共享区内容不会更新到硬盘上)
函数:int msync ( void * addr , size_t len, int flags)
binder(二)
接上的mmap函数,写了一个简单的内存共享实例,这个实例很简单,就是两个不同进程对一个内存的文件进行读取操作,实现内存共享的功能,贴部分核心代码
1、BinderTest1.cpp
BinderTest1.cpp中,就是打开一个文件,再用mmap方法将p_map(People是自定义的一个对象,就包含名字和年龄两个属性)映射到内存中,并for循环为p_map及p_map+i赋值,说白了就是对共享文件进行写入操作
BinderTest2.cpp
BinderTest2.cpp中,将test.text打开,也将位置映射到p_map这块内存上,并用for循环又进行读取每一个p_map的name和age,最后的munmap表示解除映射
2、在BinderTest1.cpp中,有一句lseek(fd,sizeof(people)*5-1,SEEK_SET);这句话是什么意思呢?
原因是,在这句话之前有个open文件的操作,而所有被打开的文件都有个当前偏移量(即文件开始处到文件当前位置的字节数),而lseek函数的作用就是改变文件的偏移量,语法:lseek(int handler,long offset,int fromwhere); 而fromwhere有三个参数可选:SEEK_SET(文件头位置),SEEK_CUR(文件当前位置)和SEEK_END(文件末位置)。简而言之,lseek的意思就是从fromwhere位置开始移动offset个字节
lseek(fd,0,SEEK_SET):将读写位置移到文件开头
lseek(fd,0,SEEK_CUR):将读写位置移到文件末尾开头
lseek(fd,0,SEEK_END):取得当前文件位置
另:write函数/* write some data to the file */
binder (三)
在(一)中,我们说到了MediaService.cpp(注册了提供媒体播放的服务程序),也讲了ProcessState,通过一层层深入最后发现了打开dev/binder设备的地方,而这样的话就相当于和内核binder有了交互的通道,并且我们把fd映射到了内存上,这块内存估计就是和binder设备共享的。而这一节笔记讲下ServiceManager(这是Android OS的整个服务的管理程序)
三、defaultServiceManager
defaultServiceManager位于framework\base\libs\binder\IServiceManager.cpp中,其中实例化gDefaultServiceManager中有这样一句话
1、gDefaultServiceManager=interface_cast<IServiceManager>(ProcessState::self()->getContextObject(NULL)); //创建gDefaultServiceManager
其中ProcessState::self()肯定就是返回一个ProcessState对象,然后调用其getContextObject方法
2、回到ProcessState.getContextObject()方法
sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& caller)
{
if (supportsProcesses()) { //该函数根据打开设备是否成功来判断是否支持多线程,一般真机上都能支持
return getStrongProxyForHandle(0);//注意,这里传入0
}
}
那这个getStrongProxyForHandler又是一个什么样的东西呢,直接从字面意思来看貌似看不出来什么,就一个handler,handler在windows下对资源的一个标识。比如一个数据结构(常用的有Array、Queue、Map类型等)保存在数组中,然后handler是它在这个数组中的索引。
3、到getStrongProxyForHandle方法去看看吧~
sp<IBinder>ProcessState::getStrongProxyForHandle(int32_t handle)
{
handle_entry* e = lookupHandleLocked(handle); //lookupHandlerLocked这个方法待会儿来讲,主要意思就是从数组中查找对应索引的资源,返回一个handle_entry,handle_entry结构为IBinder* binder,RefBase::weakref_type* refs
// lookupHandlerLockerd 函数查询当前进程维护的service代理对象的列表。
if (e != NULL) //e一定不为空,因为在lookupHandleLocked中若为空也会insert一个进去{
IBinder* b = e->binder; //第一次进来,肯定为空
if (b == NULL || !e->refs->attemptIncWeak(this)) {
b = new BpBinder(handle); --->看见了吧,创建了一个新的BpBinder
e->binder = b;
result = b;
}
}
return result;返回刚才创建的BpBinder。
}
4、刚才说到lookupHandleLocked()函数,我们来看看这个函数里面有什么东西
ProcessState::handle_entry* ProcessState::lookupHandleLocked(int32_t handle){
const size_t N = mHandleToObject.size();
if (N <= (size_t)handle) {
handle_entry e;
e.binder = NULL;
e.refs = NULL;
status_t err = mHandleToObject.insertAt(e, N, handle+1-N);
if(err < NO_ERROR) return NULL;
}
return &mHandleToObject.editItemAt(handle);
}
mHandleToObject是一个以handle_entry类型为元素的Vector(Vector类在java 中可以实现自动增长的对象数组; vector在C++标准模板库中的部分内容,它是一个多功能的,能够操作多种数据结构和算法的模板类和函数库),维护当前线程的代理对象列表,比如serviceManager对应的是0,那就是列表第一项。如果(size_t)handle >= 列表大小,说明待创建的service代理对象条还未在当前线程中创建,于是就需要插入新项,并返回已创建过的代理对象条
5、我先把上面几个步骤给理一下,因为有点混乱老~~
【1】创建gDefaultServiceManager:通过ProcessState::self()->getContextObject(NULL)获得
【2】getContextObject()方法中,判断设备是否支持多线程,如果支持则调用getStrongProxyForHandle(0) (注:参数传值为0,因为serviceManager为列表第一项,对应的是0)
【3】通过handle值(标识值)去Vector中查找对应的handle_entry(lookupHandleLocked方法中查找如果该对象还未创建,就插入新项,然后返回已创建过的对象)
【4】通过查找出来的handle_entry对象创建一个BpBinder,BpBinder的参数为(handle_entry.binder)
【5】这样归纳下来,其实创建gDefaultServiceManager就约等于是创建一个BpBinder
gDefaultServiceManager=interface_cast<IServiceManager>(new BpBinder(0))); //这样下来就看起来简单多了吧~
6、接下,那么上面的公式已经归纳出是在创建一个BpBinder(framework\base\libs\binder\BpBinder.cpp)了,那么BpBinder到底是什么样的对象呢?
BpBinder::BpBinder(int32_t handle)
: mHandle(handle) //此handle为上面传入的值:0
, mAlive(1)
, mObitsSent(0)
, mObituaries(NULL)
{
IPCThreadState::self()->incWeakHandle(handle); //不看不知道。。BpBinder的构造函数中,居然有了IPCThreadState的身影,其实还感觉挺绕的。。不过没法人家Google想绕,自己的脑子还不是要跟着一起绕啊。。
}
7、那我们深入分析下IPCThreadState这个类吧
(1)IPCThreadState* IPCThreadState::self()
{
if (gHaveTLS) { //第一次进来为false,因为赋值时就是gHaveTLS=false
restart:
const pthread_key_t k = gTLS; //TLS为Thread Local Storage的缩写,大致的作用就是避免多线程同时访问全局变量而造成的混乱,也不会有同步发生,每一个线程都拥有一个自己的TLS,别的线程就获取不到其他线程中TLS的数据了
IPCThreadState* st = (IPCThreadState*)pthread_getspecific(k); //用于访问线程私有变量。
if (st) return st; //返回一个st
return new IPCThreadState; //返回一个new IPCThreadState
}
(2)IPCThreadState构造函数中,就做了两个操作:
【1】pthread_setspecific(gTLS,this); //set一个key为gTLS值,value为IPCThreadState对象的一个Thread Local变量,用于之后的访问线程私有变量操作
【2】mIn.setDataCapacity(256); mOut.setDataCapacity(256); //mIn和mOut是两个parcel(输入和输出缓冲区),parcel是一个存储基本数据类型和引用数据类型的容器,parcel.setDataCapacity表示change这个parcel的capacity(容量)
binder(四)
(3)IPCThreadState::self()->incWeakHandle(handle) //在输出缓冲区中添加一个BC_INCREFS命令。(BC_INCREFS[弱引用 如果该handle对应的代理对象已经创建过了,那么就增加它的引用计数]或者BC_ACQUIRE[强行引用])
注:
A、在这里说明下,android中sp为强指针(sp<IServiceManager>==IServiceManager*),wp为弱指针。而在C++中,指针有两个头痛的问题,一是忘记释放动态申请的对象从而造成内存泄露;二是对象在一个地方释放后,又在别的地方被使用,从而引起内存访问错误。而android中有智能指针,可以动态的自动释放内存(类似JAVA的垃圾回收),这样就不需要再调用delete来释放对象了,android智能指针类型就分为强指针与弱指针
B、强指针是通过引用计数来记录有多少使用者在使用一个对象,如果所有使用者都放弃了对该对象的引用,则该对象将被自动销毁;而弱指针也指向一个对象,但是弱指针仅仅记录该对象的地址,不能通过弱指针来访问该对象,要想访问弱指针所指向的对象,需首先将弱指针升级为强指针(通过wp类所提供的promote()方法)。弱指针所指向的对象是有可能在其它地方被销毁的,如果对象已经被销毁,wp的promote()方法将返回空指针,这样就能避免出现地址访问错的情况。也就是说对象本身会增加一个弱引用计数,同时WP还包含m_ref指针,用于wp升级为sp时使用。事实上我们对wp的唯一操作就是将其升级为sp,然后判断升级成不成功,如果成功说明该对象存在,如果不成功说明该对象已经释放掉了
C、关于指针使用
【1】加入有个类MyClass,需要使用智能指针来引用这个类对象,则该类满足两个条件:
(1)此类必须是RefBase的子类或间接子类
(2)此类必须定义虚拟构造函数,如:virtual ~MyClass();
【2】满足条件后,普通指针定义:MyClass* p_obj; 智能指针定义:sp<MyClass> p_obj;(注意不是sp<MyClass> *p_obj,牢记sp<MyClass>就约等同于MyClass*) 。
【3】定义指针变量后,就可以像普通指针一样进行对变量的操作了。如:
p_obj = new MyClass(); // 注意不要写成 p_obj = new sp<MyClass>
p_obj->func();
【4】用完以后不要delete,因为智能指针会自动帮你销毁掉,用完一个对象后,只需要p_obj = NULL; 即可
【5】上面是定义一个sp,而定义一个wp并进行操作呢
wp<MyClass> wp_obj = new MyClass();
p_obj = wp_obj.promote(); // 升级为强指针。不过这里要用.而不是-> 、
p_obj->func();
wp_obj = NULL;
【6】说明:智能指针只是一个对象而不是真正的指针,因此其效率不如指针。所以有的时候使用谁还需要自己去考虑
8、好的,我们终于绕出来了,现在可以知道的是我们创建了ProcessState、BpBinder以及IPCThreadState(主线程的)
回到之前的那个等式:gDefaultServiceManager =interface_cast<IServiceManager>(new BpBinder(0));
疑惑了,interface_cast(frameworks/base/include/binder/IInterface.h)又是什么样的东西呢?
template<typename INTERFACE>
inline sp<INTERFACE>interface_cast(const sp<IBinder>& obj)
{
return INTERFACE::asInterface(obj);
}
上面的代码template<typename INTERFACE>,表示以某个类作为模板嵌入到下面的inline语句中。比如之前我们得出的等式:gDefaultServiceManager=interface_cast<IServiceManager>(new BpBinder(0)));那么进入实际上上面的代码就应该写成:
inline sp<IServiceManager>interface_cast(const sp<IBinder>& obj)
{
return IServiceManager::asInterface(obj);
}
9、IServiceManager(framework/base/include/binder/IServiceManager.h):作用为增加服务、查询服务等
这里仅列出增加服务的代码
public:
DECLARE_META_INTERFACE(serviceManager);
virtual status_t addService(const String16& name,const sp<IBinder>& service) = 0;\\增加服务
注:DECLARE_META_INTERFACE与IMPLEMENT_META_INTERFACE都是宏(预处理),在IInterface.h中进行定义的
下面是DECLARE的宏:
#define DECLARE_META_INTERFACE(INTERFACE)
static const android::String16descriptor; //定义一个字符串
static android::sp<I##INTERFACE> asInterface(const android::sp<android::IBinder>&obj);
//定义一个asInterface方法,这就与前面的asInterface相对应了,将IServiceManager填充进去,则为android::sp<IServiceManager> asInterface(const android::sp<android::IBinder>&obj);
virtual const android::String16& getInterfaceDescriptor() const; //get函数,返回之前的String16descriptor
I##INTERFACE(); //填充进去为IServiceManager();
virtual~I##INTERFACE(); //填充进去为virtual~IServiceManager();