Android IPC机制Binder解析

来源:互联网 发布:eclipse格式化js代码 编辑:程序博客网 时间:2024/04/28 00:52

    花了一个上午的时间把这篇文章翻译完,确实写的很透彻,但美中不足的地方是没有插图,不能直观的了解Binder机制,说实话我自己对binder也理解的很浅显,特别是到binder kernel driver哪块,还要等我先学习linux驱动再来看一遍吧,等以后看懂了再自己绘图应该更有助于理解。

------------------------------------------------------------------------荤勾线-----------------------------------------------------------------------------------------------------

       我将利用IAudioFlinger::setMode这个API作为一个简单的场景,来展示Android IPC系统是如何工作的,AudioFlinger是多媒体服务系统中的一个服务。

 

       运行Service Manager

       service_manager 是为其它进程提供“服务”管理的“服务”。所以,它必须比于其它任何服务先运行。

 

       首先打开“/dev/binder”驱动,且调用BINDER_SET_CONTEXT_MGR这个ioctl,让内核驱动知道该设备是作为管理者(manager)的角色。然后进入一个循环,等待来自其它进程的数据。

 

        注意BINDER_SERVICE_MANAGER.

 
        BINDER_SERVICE_MANAGER是向service_manager注册的句柄(handle),其它进程必须通过它来跟service_manager通信。
        获取IserviceManager
        得到IServiceManager实例的唯一方法是调用IServiceManager.cpp实现的defaultServiceManager接口。
 
        gDefaultServiceManager在libutil内有定义,所以使用到该对象的程序都要包含该头文件。gDefaultServiceManager初始值为NULL,因此第一次运行时,会通过调用ProcessState::self()获得ProcessState实例。每个进程只能有一个这样的实例。ProcessState会打开“/dev/binder”驱动提供给IPCThreadState使用。
 
         现在我们有了一个ProcessState实例,再来看看getContextObject。
 
         我们的板子支持binder驱动,所以进入getStrongProxyForHandle。(Handle 0为service manager保留,后续会解释原因。)
 
        初次b将会是NULL,所以这段代码会new 一个BpBinder实例。BpBinder是一个远程binder对象的代理类。
 
        IPCThreadState::incWeakHandle将添加一个BC_INCREFS的命令到输出buffer
 
        现在getContextObject返回了一个BpBinder的实例,它将通过interface_cast转换为IServiceManager.interface_cast在IInterface.h有定义,展开如下:
 
现在我们来看看IServiceManager的定义
 
DECLARE_META_INTERFACE是在IInterface.h中定义的一个宏,展开如下: 
如你所见,DECLARE_META_INTERFACE宏生命了两个函数,这两个函数将在IServiceManager.cpp中被实现
IMPLEMENT_META_INTERFACE(ServiceManager, "android.os.IServiceManager");
代码展开如下:
 
所以IServiceManager::asInterface最终会new一个BpServiceManager实例,并且返回给用户。BpServiceManager作为远程BnServiceManager的代理。任何在IServiceManager操作现在实际上是调用BpServiceManager相应的虚函数。
摘要:
这个部分给出了如何获取远程对象的代理对象,加入你想实现你自己的服务IFunnyTest,你必须完成下面的步骤:
1、将DECLARE_META_INTERFACE(FunnyTest)宏写进你的接口头文件(interface header file)。
2、讲IMPLEMENT_META_INTERFACE(Funnytest, “your unique name”)宏放进你的接口源文件(interface source file)。
3、实现你自己的BpFunnyTest类。
触发AudioFlinger服务
media_server系统讲会启动AudioFlinger服务,代码如下:
 
AudioFlinger将会调用ServiceManager::addService,这是个远程调用(RPC)。
 
AudioFlinger继承字BnAudioFlinger,BnAudioFlinger是BnInterface的模板(template);
 
BnInterface继承自BBinder.
 
根据BnInterface的实现,我们知道传给IServiceManager::addService的参数实际上是一个AudioFlinger实例的地址,BBinder继承自IBinder,它的transact函数将会调用虚函数onTransact。
 
这里面最重要的函数就是onTransact。BnAudioFlinger会实现这个虚函数,在这里,我们只需要专注于SET_MODE这个分支。
 
media_server将会通过IPCThreadState::joinThreadPool进入一个循环,就向service_manager一样,它会在talkWithDriver里面等待来自其它进程的数据。
 
假如你想实现自己的IFunnyTest服务,你必须做到洗面几点:
1、实现你自己的BnFunnyTest类
2、在你的服务运行的进程里,调用IPCThreadState::joinThreadPool开始binder循环。
远程调用(RPC Call)IServiceManager::addService
我们调用IServiceManager::addService,其实是调用BpServiceManager::addService。
 
Parcel很简单,我们把它想象成一个连续的buffer。这里要注意,service这个参数只想BBinder对象(AudioFlinger继承自Bn)
 
flatten_binder将会触发一个Binber指令。因为BBinder是一个本地(local)的binder对象,所以我们的代码分支将会标识成红色
 
注意这几行红色的代码,local的地址放入(稍候会用到)。等到addService这个远程调用(RPC)用到的数据包(packet)打包好后,BpServiceManager::addService将会调用BpBinder的transact方法。
 
BpBinder调用IPCThreadState::transact开始将binder对象传送给对应的句柄(mHandler),在这里,mHandler为0。(也就是传送给service manager这个服务)。
 
IPCThreadState::transact开始会调用writeTransactionData为binder的内核驱动构造一个传输数据的结构体,注意以下代码,这对binder内核驱动区分传输目标(transaction target)非常重要。
 
然后waitForResponse会调用talkWithDriver,ioctl设置为BINDER_WRITE_READ。
 
现在,数据已经传送给了binder内核驱动。
摘要:
代理对象会触发一个需要数据包的RPC,然后调用BINDER_WRITE_READ将数据包传给binder内核驱动。这个数据包是一个格式化的包,对与RPC,它使用的包类型是BC_TRANSACTION
假定你想实现你自己的IfunnyTest,你必须完成下面几点:
在你的服务运行的进程里面,调用IServiceManager::addService想service_manager注册你的服务
在Binber内核驱动里面的传输
当任何进程打开“/dev/binder”驱动的时候,一个对应的binder_proc结构将会传递给binder_open。
 
所以,当任何ioctl到来的时候,驱动都知道他的进程信息,传输数据是通过BINDER_WRITE_READ的ioctl来传送的。
 
驱动首先进行写操作,我们先看看binder_thread_write。binder_thread_write的核心,是一个循环,它将指令打包写到buffer里面,然后执行响应的指令。
 
我们看看其中的两个指令,一个是BC_INCREFS.
 
记住,我们上面提到,在这里,我们的目标(target)是0,当system_manager调用BINDER_SET_CONTEXT_MGR这个ioctl的时候,binder_context_mgr_node代表0。所以这里仅仅是将binder_context_mgr_node节点的弱应用(weak reference)增加1.。
binder_context_mgr_node = binder_new_node(proc, NULL);
另一个指令是BC_TRANSACTION.
 
假如数据包里面包含BINDER_TYPE_BINDER这个flattened对象的话,binder_transaction将会创建一个信的binder节点。
 
binder_transaction会知道目标是句柄(handle)0,所以运行下列分支,找到target_node, target_proc和target_thread
 
最终,binder_transaction会讲请求放入列表,唤醒等待binder_thread_read里面的线程。
 
现在我们来看看binder_thread_read。当service_manager跑起来后,它会在这里等待一直到有请求到来。
 
因为之前media_server进程的写请求已经把它唤醒了,所以继续执行。下面的代码是从media_server的写buffer拷贝到system_manager的读buffer
 
摘要:
这部分展示了RPC的客户端和服务端的数据流向。
Service Manager 处理 Add Service请求
直到现在,service_manager已经从madia_server的BR_TRANSACTION数据包,然后调用binder_paser处理这个数据包。
 
binder_parser调用svcmgr_handler解析BR_TRANSACTION包,跟BpServerManager进程相反。这里的结构体binder_txn实际上跟binder_transaction_data相同。在这里,传输码(transaction code)是SVC_MGR_ADD_SERVICE.
 
因此,service_manager知道这个服务即将启动,并且调用bio_get_ref来获取服务的信息。
 
bio_get_ref做了flatten_binder完全相反的工作。do_add_service最终通过调用BC_ACQUIRE得到一个对象的强引用,由ptr指向它。
摘要:
这部分展示了服务怎么添加到service manager中去。
假如你想实现你自己的服务IFunnyTest,你必须完成一下步骤:
1、将你的服务名字添加到service_manager的服务列表里去
获取IaudioFlinger
获取service接口的唯一途径是通过调用IServiceManager::getService。比如说,这里的获取AudioSystem的方法是IAudioFlinger.
 

就跟刚才分析的一样,这个调用最终会通过binder内核驱动被service_manager处理。
 
然后service_manager会返回一个先前的句柄(previous handle),这个句柄由media_server设置,实际上就是AudioFlinger实例的地址。然后BpServiceManager::checkService会从remote()->transact调用中返回。然后,就像在IServiceManager中分析的一样,它将创建一个新的BpBinder实例,对应service_manager返回的handle,interface_cast<IAudioFlinger>(binder)最终返回一个BpAudioFlinger实例。
摘要
就想获取IServieManager一样,但是这次需要获取一个service_manager的handle,当然我们的IServiceManager的handle总是为0.

远程调用IAudioFlinger::SetMode
加入我们在AAA进程中调用IAudioFlinger::SetMode,实际上我们调用的是
 
就像分析IServiceManager::addService一样,这个函数将会出发一个数据包,并写入binder驱动内核,等待读应答。唯一的不同点是target handle指向了media_server进程的某些地址。
处理 IAudioFlinger::SetMode
Binder内核驱动最终会唤醒在media_server进程中运行在IPCThreadState::joinThreadPool的读线程,现在我们再来看看这段代码:
 
这一次,talkWithDriver讲返回BpServiceManager::setMode生成的数据包,然后executeCommand会处理这些命令,在这里,命令是BR_TRANSACTION.
 
最重要的两行已经标记为红色,这里获取了从binder内核驱动的地址,并且强制转换为BBinder指针(这个地址当调用IServiceManager::addService时传给了binder内核驱动)。记住,AudioFlinger是派生自Bbinder。这个指针实际上指向的就是我们的AudioFlinger实例。所以写下来的trancat调用最终会调用我们BnAudioFlinger的onTransact这个虚函数。
 
然后将通过调用sendReply将应答写回去。
 
它最终写到binder内核驱动里去,内核驱动最后会唤醒AAA进程的读线程。
摘要
Service_manager<---->service provider<------->service user
     |                                         |                                 | 
     |                                         |                                 |
     |                                         |                                 |
     |                                         |                                 |
     ------------------------>Binder Driver<-----------------
这是Android IPC系统的大致架构,分为四大块。
1、Binder Driver
   这是IPC系统的核心,负责service provider 和service user之间的数据传输
2、service provider
   它提供一些服务,它解析来自binder driver的数据,并且真正执行动作。
3、service_manager
   这是一个特殊的服务。它为其它的服务提供管理服务。
4、service user
   它远程调用service provider,出发一个RPC调用,并将数据传入binder driver
在我们所列举的场景里面,下面列举出了主要的控制流程
1、service_manager首先运行,它想binder driver注册特殊的节点0
2、Media_server 获取特殊节点0的IServiceManager代理对象
3、media_server通过RPC调用IServiceManager::addService添加IAudioFlinger服务,
这个调用的目的节点为0。然后向binder driver发送数据。
4、Binder driver知道数据是给节点0的,然后数据包含了创建新节点的指令,因此它为IAudioFlinger创建另外一个节点(假如为A),表示service_manager.
5、service_manager读取来自binder driver的数据,然后处理IServiceManager::addService的RPC调用
6、另外一个进程P获取特殊节点0的IServiceManager对象
7、P通过RPC调用IServiceManager::getService获取IAudioFlinger服务。这个调用追溯到节点0,它发送据给binder driver
8、Binder driver知道数据是给节点0的,所以它把数据传给service_manager.
9、service_manager读取从binder driver传上来的数据,处理IServiceManager::getService调用,将代表IAudioFlinger服务的节点A发回给binder driver
10、P 通过RPC调用l IAudioFlinger::setMode. 现在这个调用追溯的节点是A.
11、Binder driver知道数据是给节点A的,所以它把数据传给media_server.
12、media_server读取数据,处理IAudioFlinger::setMode调用,将结果发回给binder driver
13、Binder driver讲结果发回给进程P
14、P从binder driver读取数据,这样最终的结果就得到了
原创粉丝点击