概述
本文是看完邓凡平的《深入理解android卷1》第六章的binder篇后,在此基础上的一些个人理解。
上文从驱动角度解释了binder
通讯机制的底层运行原理,我们知道android系统中,binder
是采用CS架构来设计的,除了binderDriver
之外,还需要client
server
以及serviceManager
三个角色,才能完整实现一套CS架构的跨进程通讯机制。
从上图可以看到,一次完整的IPC 至少需要这么几个步骤
Server
通过serviceManager
注册服务Client
通过ServiceManager
查询服务Client
获取到Server
端的服务后,通过binder
驱动,完成跨进程对Server
端的引用。
下面以native层的一次IPC请求流程为例,通过client对MediaServer的调用,了解一下client
、server
、serviceManager
三者之间的通讯过程。
server端:MediaServer
MediaServer
是系统主要server
之一,它提供了
- AudioFlinger
- AudioPolicyService
- MediaplayerService
- CamerService
四个重量级服务,查看MediaServer
的源码:
1 | int main(int argc, char** argv) |
以代码中标注的1,2,3,4,5为次序,依次讲解每个部分的具体内容。
一、创建ProcessState
还是先看代码
1 | //①获得一个ProcessState实例 |
创建ProcessState实例
1 | sp<ProcessState> ProcessState::self() |
1 processState 的构造函数
1 | ProcessState::ProcessState() |
processState
是个单例对象,因为它是在程序运行时只初始化一次,所以每个进程只有一个ProcessState
对象。在创建ProcessState
时,做了这么几件事情
1 | static int open_driver() |
【笔记三:】
上文已经说过,open('dev/binder',O_RDWR)
其实对应了内核中binder
驱动的binder_open()
方法,binder_open()
的代码如下:
1 | static int binder_open(struct inode *nodp, struct file *filp) |
可以看到,在打开binder
驱动时,binder_procs
会将所有打开binder
驱动的进程加入到该列表中。
同时,通过ioctrl
的方式 告诉了binder
驱动 当前server
端线程池支持的最大线程数是15.
所以创建processState
的过程 其实做了这么几件事:
- 打开
binder
驱动 同时驱动为该进程创建对应的binder_proc
节点 - 对返回的
fd
使用mmap
方法,操作binder
驱动,binder
驱动申请了一块内存来接受通讯数据 - 因为
ProcessState
是进程单例的,每个进程只会开启binder
驱动一次。
二、 获取servicManager
defaultServiceManager()
方法在IServiceManager.cpp
中定义,返回IServiceManager
对象,先看一下这个方法的具体实现
IServiceManager.cpp
1 | sp<IServiceManager> defaultServiceManager() |
可以看到 真正的IServiceManager
是由方法 interface_cast<IServiceManager>()
传入一个 ProcessState::self()->getContextObject(NULL)
对象实现的。
先看一下ProcessState::self()
的getContextObject()
函数
ProcessState.cpp
1 | sp<IBinder>ProcessState::getContextObject(const sp<IBinder>& caller) |
继续看getStrongProxyForHandle()
ProcessState.cpp
1 | sp<IBinder>ProcessState::getStrongProxyForHandle(int32_t handle) |
可以看到 实际返回的对象是一个BpBinder
,BpBinder
里持有一个handle
成员变量。
实际上 BpBinder
BBinder
都是继承自IBinder
的。
从名字也可以看出来,BpBinder
,BProxy(proxy:代理),肯定是与客户端打交道的。如果说Proxy
代表客户端,那么BBinder
则代表服务端。这里的BpBinder
和BBinder
是一一对应的,即某个BpBinder
只能和对应的BBinder
交互。我们当然不希望通过BpBinderA
发送的请求,却由BBinderB
来处理。
刚才我们在defaultServiceManager()
函数中创建了这个BpBinder
。
前面说了,BpBinder
和BBinder
是一一对应的,那么BpBinder
如何标识它所对应的BBinder
端呢?
答案是Binder
系统通过handler
来对应BBinder
。以后我们会确认这个Handle
值的作用。
注:我们给BpBinder构造函数传的参数handle的值是0。这个0在整个Binder系统中有重要含义—因为0代表的就是ServiceManager所对应的BBinder。
详细看一下BpBinder
的实现
1. BpBinder.cpp
1 | BpBinder::BpBinder(int32_t handle) |
看上面的代码,会觉得BpBinder
确实简单,不过再仔细查看,你或许会发现,BpBinder
、BBinder
这两个类没有任何地方操作ProcessState
打开的那个/dev/binder
设备,换言之,这两个Binder类没有和binder设备直接交互。那为什么说BpBinder
会与通信相关呢? 我们接着看interface_cast()
函数
我们是从下面这个函数开始分析的:
1 | gDefaultServiceManager =interface_cast<IServiceManager>( ProcessState::self()->getContextObject(NULL)); |
现在这个函数调用将变成如下所示:
1 | gDefaultServiceManager =interface_cast<IServiceManager>(new BpBinder(0)); |
这里出现了一个interface_cast
。它是什么?其实是一个障眼法!下面就来具体分析它。
2. 障眼法——interface_cast
看看interface_cast
的具体实现,其代码如下所示:
1 | IInterface.h |
又转移到IServiceManager对象中去了,还原完模板函数,可以看到interface_cast()
实际调用的是IServiceManager
中的asInterface()
方法,该方法传入了上文所说的BpBinder
对象。看一下IServiceManager()
中做了什么操作
3. IServiceManager
刚才提到,IBinder
家族的BpBinder
和BBinder
是与通信业务相关的,那么业务层的逻辑又是如何巧妙地架构在Binder
机制上的呢?关于这些问题,可以用一个绝好的例子来解释,它就是IServiceManager
。
【笔记四:】
IServiceManager
对象其实可以当做java中的接口函数来理解。它定义在IServiceManager.h
中,描述了ServiceManager
可以提供的服务。
(1)定义业务逻辑
先回答第一个问题:如何表述应用的业务层逻辑。可以先分析一下IServiceManager
是怎么做的。IServiceManager定义了ServiceManager
所提供的服务,看它的定义可知,其中有很多有趣的内容。IServiceManager
定义在IServiceManager.h
中,代码如下所示:
IServiceManager.h
1 | class IServiceManager : public IInterface |
(2)业务与通信的挂钩
Android巧妙地通过DECLARE_META_INTERFACE
和IMPLENT_META_INTERFACE
宏,将业务和通信牢牢地钩在了一起。DECLARE_META_INTERFACE
和IMPLEMENT_META_INTERFACE
这两个宏都定义在刚才的IInterface.h
中。先看DECLARE_META_INTERFACE
这个宏,如下所示:
IInterface.h::DECLARE_META_INTERFACE
1 |
|
将IServiceManager的DELCARE
宏进行相应的替换后得到的代码如下所示:DECLARE_META_INTERFACE(IServiceManager)
1 | //定义一个描述字符串 |
DECLARE
宏声明了一些函数和一个变量,那么,IMPLEMENT
宏的作用肯定就是定义它们了。IMPLEMENT
的定义在IInterface.h
中,IServiceManager
是如何使用了这个宏呢?只有一行代码,在IServiceManager.cpp中,如下所示:
1 | IMPLEMENT_META_INTERFACE(ServiceManager,"android.os.IServiceManager"); |
我们曾提出过疑问:interface_cast
是如何把BpBinder
指针转换成一个IServiceManager
指针的呢?答案就在asInterface函数的一行代码中,如下所示:
intr = new BpServiceManager(obj);
明白了!interface_cast
不是指针的转换,而是利用BpBinder
对象作为参数新建了一个BpServiceManager
对象。我们已经知道BpBinder
和BBinder
与通信有关系,这里怎么突然冒出来一个BpServiceManager
?它们之间又有什么关系呢?
4 IServiceManager家族
要搞清这个问题,必须先了解IServiceManager
家族之间的关系,先来看图6-3,它展示了IServiceManager
的家族图谱。
图6-3 IServiceManager
的家族图谱
根据图6-3和相关的代码可知,这里有以下几个重要的点值得注意:
IServiceManager
、BpServiceManager
和BnServiceManager
都与业务逻辑相关。BnServiceManager
同时从BBinder
派生,表示它可以直接参与Binder
通信。BpServiceManager
虽然从BpInterface
中派生,但是这条分支似乎与BpBinder
没有关系。BnServiceManager
是一个虚类,它的业务函数最终需要子类来实现。
重要说明:以上这些关系很复杂,但ServiceManager
并没有使用错综复杂的派生关系,它直接打开Binder
设备并与之交互。后文,还会详细分析它的实现代码。
图6-3中的BpServiceManager
,既然不像它的兄弟BnServiceManager
那样直接与Binder
有血缘关系,那么它又是如何与Binder
交互的呢?简言之,BpRefBase
中的mRemote
的值就是BpBinder
。如果你不相信,仔细看BpServiceManager
左边的派生分支树上的一系列代码,它们都在IServiceManager.cpp
中,如下所示:
IServiceManager.cpp::BpServiceManager类
1 | //通过它的参数可得知,impl是IBinder类型,看来与Binder有间接关系,它实际上是BpBinder对象 |
BpInterface的实现代码如下所示:
1 | IInterface.h::BpInterface类 |
BpRefBase()
的实现代码如下所示:
Binder.cpp::BpRefBase类
1 | BpRefBase::BpRefBase(const sp<IBinder>&o) |
原来,BpServiceManager
的一个变量mRemote
是指向了BpBinder
。回想一下defaultServiceManager
函数,可以得到以下两个关键对象:
有一个BpBinder
对象,它的handle
值是0。
有一个BpServiceManager
对象,它的mRemote
值是BpBinder。
【笔记五:】在获取
ServiceManager
的时候,通过传入一个BpBinder(0)
对象,调用到IServiceManager
的asInterface()
函数,这个函数创建了一个BpServiceManger
对象,该对象也是定义在IServiceManager.cpp
中的,BpServiceManager
对象通过构造函数持有了我们传过去的BpBinder
,并实现了IServiceManager
的业务函数(其实并没有真正实现,只不过BpServiceManager
里有一个IServiceManager
的同名方法,在同名方法里,会将客户端调用该函数的一些参数数据进行封装,打包成parcel
对象,然后交给自己持有的BpBinder
,BpBinder
并不会直接与binder
驱动进行交互,实际上所有的交互操作都是由IPCTthreadState
完成的,后文会讲)
三、 注册MediaPlayerService
拿到了BpServiceManager
,其实就可以通过这个代理,与server 也就是ServiceManager
进行通信了。
现在要想serviceManager
注册MediaPlayerService
服务。我们看一下 代码③ 具体做了什么
MediaPlayerService.cpp
1 | void MediaPlayerService::instantiate() { |
根据前面的分析,defaultServiceManager()
实际返回的对象是BpServiceManager
,它是IServiceManager
的后代,代码如下所示:
IServiceManager.cpp::BpServiceManager的addService()函数
1 | virtual status_t addService(const String16&name, const sp<IBinder>& service) |
别急着往下走,应先思考以下两个问题:
- 调用
BpServiceManager
的addService
是不是一个业务层的函数? addService
函数中把请求数据打包成data后,传给了BpBinder的transact
函数,这是不是把通信的工作交给了BpBinder
?
两个问题的答案都是肯定的。至此,业务层的工作原理应该是很清晰了,它的作用就是将请求信息打包后,再交给通信层去处理。
通信层的工作
下面分析BpBinder
的transact
函数。前面说过,在BpBinder
中确实找不到任何与Binder设备交互的地方吗?那它是如何参与通信的呢?原来,秘密就在这个transact
函数中,它的实现代码如下所示:
BpBinder.cpp
1 | status_t BpBinder::transact(uint32_t code, constParcel& data, Parcel* reply, |
这里又遇见了IPCThreadState
,之前也见过一次。看来,它确实与Binder
通信有关,所以必须对其进行深入分析!
1 “劳者一份”的IPCThreadState
谁是“劳者”?线程,是进程中真正干活的伙计,所以它正是劳者。而“劳者一份”,就是每个伙计一份的意思。IPCThreadState
的实现代码在IPCThreadState.cpp
中,如下所示:
IPCThreadState.cpp
1 | IPCThreadState* IPCThreadState::self() |
接下来,有必要转向分析它的构造函数IPCThreadState(),如下所示:
IPCThreadState.cpp
1 | IPCThreadState::IPCThreadState() |
每个线程都有一个IPCThreadState
,每个IPCThreadState
中都有一个mIn
、一个mOut
,其中mIn是用来接收来自Binder
设备的数据的,而mOut
则是用来存储发往Binder
设备的数据的。
2 勤劳的transact
传输工作是很辛苦的。我们刚才看到BpBinder
的transact
调用了IPCThreadState
的transact
函数,这个函数实际完成了与Binder
通信的工作,如下面的代码所示:
IPCThreadState.cpp
1 | //注意,handle的值为0,代表了通信的目的端 |
多熟悉的流程:先发数据,然后等结果。再简单不过了!不过,我们有必要确认一下handle这个参数到底起了什么作用。先来看writeTransactionData函数,它的实现如下所示:
IPCThreadState.cpp
1 | status_tIPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags, |
现在,已经把addService
的请求信息写到mOut
中了。
【笔记七:】
注意观察传递数据的变化 在BpServiceManager
中还是Parcel
,然后BpServiceManager
交给了BpBinder
,BpBinder
又把数据交给了IPCThreadState
,IPCThreadState
调用writeTransactionData
方法,将数据进一步封装为binder_transaction_data
,并将binder_transaction_data
和BC_XXX
指令写到IPCThreadState
中的mOut
中。
·
【笔记八:】可以看到 真正与
binder驱动打
驱动打交道的是IPCThreadState
。与Binder
驱动打交道,意味着要往binder
驱动写指令和数据,同时要从binder
驱动读取返回的结果。writeTranscationData()
方法实际上并没有做 往binder
里写数据的操作,而是把数据写到自己的mOut
成员变量里,那这个成员变量是怎么传给binder驱动的呢? 其实是在waitForResponse()
函数里,waitForResponse()
中的talkWithDriver()
会读取mOut
的数据并将数据传递给binder
驱动,然后从binder
驱动中读取返回数据传递给mIn
,这样就完成了一次数据交互。
接下来再看发送请求和接收回复部分的实现,代码在waitForResponse
函数中,如下所示:
IPCThreadState.cpp
1 | status_t IPCThreadState::waitForResponse(Parcel*reply, status_t *acquireResult) |
接下来看看talkWithDriver()
函数
3 talkWithDriver()
talkwithDriver函数,如下所示:
IPCThreadState.cpp
1 | status_t IPCThreadState::talkWithDriver(bool doReceive) |
图 binder_write_read
结构体
【笔记九:】
waitForResponse()
是直接参与与binder
驱动交互的地方了,首先 它初始化了binder_read_write
结构体,将mIn
和mOut
中的数据读出来(如果有的话,没有就相当于初始化了),继而调用了binder
驱动的ioctrl()方法(对应驱动层的binder_ioctrl()
),将这个封装好的binder_read_write
结构发送给binder
驱动,还记得上文binder
驱动篇中的分析吗,binder驱动的binder_ioctrl
()逻辑很简单,只是取出BC码和BR码,然后根据码来做对应的操作。
这里综述一下framework到驱动层之间的通讯流程,具体如下
1.ProcessState::self 打开驱动:binder
驱动会为每一个flat_binder_object
对象在内核中创建一个唯一的BinderNode
与之对应。 同时,每一个打开了binder
驱动的进程,在内核中都有一个binder_proc
结构体与之对应,该结构体被加载在binder_procs
的全局链表上,是全局链表,所以这何一个进程(我们这里是MediaServer
)都可以访问到任何进程的binder_proc
对象了。同时,binder_node
被加载在binder_proc
的nodes
红黑树中。
.
.
2.mmap()让binder驱动去申请空间并做地址映射:还记得我们MediaServer
初始化的时候调用了一个ProcessState::self
方法吗,它除了打开驱动,还调用了mmap()
为该进程分配一个buffer,默认是4k ,也就是一个页面,这可以从分配函数看出来
1 | binder_update_page_range(proc, 1, proc->buffer, proc->buffer + PAGE_SIZE, vma); |
PAGE_SIZE = 4K
,分配完成后 以binder_buffer
的形式 保存在proc
的buffers
红黑数里,同时进行了用户空间和内核空间的物理地址映射,也就是说现在mediaserver
和内核空间映射了同一份物理地址,server
端可以直接访问该物理地址而不需要将数据从内核空间往server
进程所在的用户空间再拷贝一次了!
.
.
- 通过ProcessState的getStrongProxyForHandle方法,创建了一个客户端“信使”BpBinder(0),其中
handle = 0
,驱动其实正是通过handle值来查找客户端要通信的对端对应的binderNode
,这个后面会说。该信使还持有IPCThreadState
对象,它才是真正负责与驱动通讯的。
.
.
4.创建服务端(这里是serviceManager
做特殊的服务端,它提供的服务是注册服务add_service
方法)对应的BpServiceManager对象(BpServiceManager
对象,它是IServiceManager
的儿子,IServiceManager
定义了业务函数和interface_cast
转换函数,同时继承了BpBinderInterface
接口,我们创建服务端的BpServiceManager
,其实就是调用了IServiceManager.cpp
中的asInterface
函数,创建了一个BpServiceManager
,同时它还持有我们传进去的信使“BpBinder(0)
“的引用,对应mRemote
)。现在我们有BpServiceManager
了,它也有IserviceMaganer
的业务函数,当我们调用对应的业务函数,这里是add_service()要将我们的服务注册上去时,它会把命令交给mRemote
,也就是我们的BpBinder().transact()
方法,transact()
会调用IPCThreadState
的transact()
方法。
.
.- IPCThreadState.transact(int32_t handle,uint32_t,code, const Parcel& data,Parcel* reply, uint32_t flags)与驱动交流,先写后读。
.
.- 将请求内容写到写缓冲区mOut,通过
IPCThreadState::self.writeTransactionData
吧数据封装成binder_transaction_data
.
.- 把请求内容发送给驱动,并等待驱动返回结果,将结果写在mIn缓冲区,读写是通过
IPCThreadState
的talkWithDriver()
方法,该方法进一步封装了要传递给binder
驱动的数据,变为binder_read_write,同时把写的数据填入write_buffer
里了。在talkWithDriver
中,通过系统调用ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr)
将数据发送给驱动。注意,现在bwr
中的指令是BC_TRANSACATION
,并且wirte_size>0
,且write_buffer
不为空。
.
.- binder_proc对象,再看一下这张图复习一下
binder_proc
除了图中所画的几个成员之外,还有两个重要成员,都会在创建binder_proc对象的时候一起初始化。分别是
struct list_head todo:当进程接收到一个进程间通信请求时,Binder驱动就将该请求封装成一个工作项,并且加入到进程的待处理工作向队列中,该队列使用成员变量todo
来描述。
wait_queue_head_t wait: 线程池中空闲Binder线程会睡眠在由该成员所描述的等待队列中, 当宿主进程的待处理工作项队列增加新工作项后,驱动会唤醒这些线程,以便处理新的工作项。
后面会讲到binder
驱动会用他们来构建binder_transaction
结构体。
以我们的例子为例,
MediaServer
调用IPCTtreadState
,并将mOut通过waitForResponse()
里的ioctrl(BINDER_READ_WRITE,&data)
发送给驱动的时候,驱动早已经完成步骤1、2了。也就是MediaServer
已经有了一个对应的binder_proc
结构体,而且其携带的的flat_binder_object
的handle
指向0.注意,这里面的flat_handle_object
中的type
是handle
,同时handle = 0
;【注: 见上文 5.通讯过程中的binder实体的传递】并且做了地址映射。
.
.
9.调用驱动的ioctrl()方法发送BINDER_WRITE_READ, 在ioctrl
()函数的入口处,会执行thread = binder_get_thread(proc)
,该函数首先获取打开驱动的进程的pid
号,根据pid号,检查是否可以在threads
的红黑树中找到对应的thread
对象,有就直接返回,没有就创建对应的Thread
对象,加入binder_proc
的threads
的红黑树中。
.
.
10.现在binder驱动已经有了线程的Thread
对象,并加入到binder_proc
中的threads
红黑树中。并且知道了请求码是BINDER_WRITE_READ
,驱动篇讲过,ioctrl
的功能就是根据不同请求码调用不同的处理方法。如果命令是BINDER_WRITE_READ
,并且bwr.write_size > 0
,则调用binder_thread_write
。
1 | switch (cmd) { |
当
write_buffer
存在数据,binder
线程的写操作循环执行。这里bwr.write_size>0
,故会执行写循环,也就是binder_thread_write()方法。
.
.
11.进入binder_thread_write()来处理请求码。首先读取Binder命令,由于buffer里只是指向命令的指针,实际数据还保存在用户空间,因此调用get_user函数从用户空间读取数据(一次拷贝)。取得命令后,先更新命令的状态信息,然后根据不同命令 进行不同的处理。这里的例子中,MediaServer
发送的命令是BC_TRANSACTION
,对于请求码为BC_TRANSACTION
或BC_REPLY
时,会执行binder_transaction()
方法,这是最为频繁的操作。
1 | int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread, |
.
.
12.binder_transaction内部流程
首先梳理下当前传进来的
binder_transaction_data
到底包含了哪些数据:
- 1 根据
binder_transaction_data
中的handle
,通过映射关系
找到对应的binder_node
,进而找到目标进程binder_proc
- 2 根据本次
binder_transaction
是否是异步,如果不是异步,意味着当前的binder
传输流程还没走完,还是同一个transaction
流程,从from_parent
查找,如果是异步,从binder_proc
回溯查找target_thread
。 - 3 如果找到
target_thread
,则它就是目标线程,否则binder_proc
对应的进程是目标线程。 - 4 根据用户空间传入的数据和目标,发起事务的线程、进程信息,创建
binder_transaction
结构体,binder_transaction
其实与一次binder_transaction()
方法对应的,每执行一次,便会在驱动中为其创建一个对应的结构体。这里要解释一下什么是binder_transaction
对象。可以这么理解,binder_transaction_data
是binder
传输对象的外部表示,应用于应用程序的,而binder_transaction
是binder
传输对象的内部表示,应用于内核binder驱动本身。binder_transaction
对象都位于binder_thread
的传输栈上,其本身是一个多级链表结构,描述了传输来源和传输目标,也记录了本次传输的信息,如binder_work
、binder_buffer
、binder
命令等。
1 | struct binder_transaction { |
- 4 根据传输的目标设置本次
binder
传输的目标等待队列(wait_queue
)和本次binder_work
需要挂载的列表(list
),也就是target_wait
和target_list
。其中target_wait
中存放的就是本次要唤醒的目标进程/线程。target_list
就是目标进程中的todo
- 5 到目前,
target_node
,target_thread
,target_proc
,target_wait
和target_list
都已经找到了。下面就该为此次传输分配新的binder_transaction
对象和binder_work
对象了,并根据当前的信息填充内容 - 6 构造一个新的
binder_transaction
对象,并为期分配内存,同时修改flat_binder_object
,做好handle
到binder
地址之间的映射。如果发送端发的是binder
,驱动会把type
修改为HANDLE_TYPE
,同时找到binder_node->binder_ref
找到索引id,binder_ref->desc
,将改id赋值给handle
.如果是handle
,吧流程返过来,handle->binder_ref->binder_node
,将binder_node
赋值给flat_binder_object
中的binder
,修改type
为BINDER_TYPE
; - 7 新的
binder_flat_object
修改好了,在此之前,还要根据是同步传输还是异步传输,设置binder_transaction
中的replay
值,并将binder_transaction
插入到target_list
也即traget_tread
/target_proc
的todo
队列中。
至此 发送命令算是做完了。可以看到,调用了binder_transcation
之后,并没有把数据发送给server
,驱动只不过是创建了一个binder_transaction
结构,然后把它挂在binder_proc
的todo
队列中。
图:驱动层调用层级
总结: 客户端的每一次请求,驱动最终都会生成换一个binder_transaction结构体,并把这个结构体挂在目标进程target_proc 也就是Server端 ServerManager服务对应的那个binder_proc 中。
- 8 唤醒等待线程的目标线程
1 | if (target_wait) |
- 9 server端的目标线程开始进入
binder_loop
状态,从ServiceManager
那端来看,它走的其实和client端发起请求的流程是类似的,只不过此时mOut
为空,binder驱动执行binder_thread_read
()方法。 - 10 server端通过
ioctrl
控制驱动执行binder_thread_read
,首先读取todo
列表的首节点。这是client端发送请求操作完成之后插进来的。 - 11 根据todo中的
binder_work
找到对应的binder_transaction
,有了binder_transaction
,便从binder_transaction
和binder_buffer
中提取出client
端发送的数据,重新组装成binder_transaction_data
。 12 将
binder_transaction_data
结构体通过copy_to_user
拷贝到用户空间,由接收端ServiceManager
收到13 server端收到binder驱动转发的客户端数据 进行处理后,再发送回给binder驱动。一次循环往复。完成客户端往服务端发送数据的过程。
总结:客户端的请求,都会被binder驱动创建一个对应的binder_transaction。并将这个transaction挂在目标进程binder_proc的todo链表里,binder驱动再唤醒目标进程,目标进程对驱动执行读取命令,驱动执行binder_thread_read,同时将客户端发送的数据,以查找todo链表 -> 查找binder_transaction-> binder_buffer 的方式,重新包装成binder_transaction_data,拷贝到server端对应的用户空间。同时将改todo列表删除。至此完成客户端->server端的传输。server的返回数据流程和这个基本一致,只不过server和client的角色需要对调一下。
OK,我们已发送了请求数据,假设马上就收到了回复,后续该怎么处理呢?来看executeCommand函数,如下所示:
IPCThreadState.cpp
1 | status_t IPCThreadState::executeCommand(int32_tcmd) |
4 StartThread Pool和join Thread Pool
1.创造劳动力——startThreadPool()
startThreadPool()
的实现,如下面的代码所示:
ProcessState.cpp
//太简单,没什么好说的
1 | void ProcessState::startThreadPool() |
PoolThread是在IPCThreadState中定义的一个Thread子类,它的实现,如下所示:
IPCThreadState.h::PoolThread类
1 | class PoolThread : public Thread |
2万众归一 joinThreadPool
还需要看看IPCThreadState
的joinThreadPool
的实现,因为新创建的线程也会调用这个函数,具体代码如下所示:
IPCThreadState.cpp
1 | void IPCThreadState::joinThreadPool(bool isMain) |
原来,我们的两个伙计在talkWithDriver
,它们希望能从Binder
设备那里找到点可做的事情。
3. 有几个线程在服务
到底有多少个线程在为Service服务呢?目前看来是两个:
startThreadPool
中新启动的线程通过joinThreadPool
读取Binder设备,查看是否有请求。
主线程也调用joinThreadPool
读取Binder设备,查看是否有请求。看来,binder设备是支持多线程操作的,其中一定是做了同步方面的工作。mediaserver
这个进程一共注册了4个服务,繁忙的时候,两个线程会不会显得有点少呢?另外,如果实现的服务负担不是很重,完全可以不调用startThreadPool创建新的线程,使用主线程即可胜任。
特殊的server端,ServiceManager
刚才分析的MediaServer
,在跟servicemanager
注册服务的时候,其实扮演的是client的角色。serviceManager
是系统所有服务的大管家,提供查询,注册服务等方法。
1. serviceManager 原理
前面说过,defaultServiceManager
返回的是一个BpServiceManager
,通过它可以把命令请求发送给handle值为0的目的端。按照图6-3所示的IServiceManager
“家谱”,无论如何也应该有一个类从BnServiceManager
派生出来并处理这些来自远方的请求吧?
很可惜,源码中竟然没有这样的一个类存在!但确实又有这么一个程序完成了BnServiceManager
未尽的工作,这个程序就是servicemanager
,它的代码在Service_manager.c中,如下所示:
注意:通过这件事情是否能感悟到什么?嗯,我们确实可以抛开前面所有的那些封装,直接与Binder设备打交道。
下面来看ServiceManager
是怎么放弃华丽的封装去做Manager的。
1 ServiceManager的入口函数
ServiceManager的入口函数如下所示。
ServiceManager.c
1 | int main(int argc, char **argv) |
这里,一共有三个重要关键点。必须对其逐一地进行分析。
注意:有一些函数是在Binder.c中实现的,此Binder.c不是前面碰到的那个Binder.cpp。
2 打开Binder设备
binder_open
函数用于打开Binder设备,它的实现如下所示:
Binder.c
1 | /* |
果然如此,有了之前所学习掌握的知识,这里真的就不难理解了。
3. 成为老大
怎么才成为系统中独一无二的manager了呢?manger的实现,如下面的代码所示:
Binder.c
1 | int binder_become_context_manager(structbinder_state *bs) |
4.死磕Binder
binder_loop
是一个很尽责的函数。为什么这么说呢?因为它老是围绕着Binder设备转悠,实现代码如下所示:
Binder.c
1 | /* |
5 集中处理
往binder_loop
中传的那个函数指针是svcmgr_handler,它的代码如下所示:
Service_manager.c
1 | int svcmgr_handler(struct binder_state *bs,structbinder_txn *txn, |
2 服务的注册
上面提到的switch/case
语句,将实现IServiceManager
中定义的各个业务函数,我们重点看do_add_service
这个函数,它最终完成了对addService
请求的处理实现,代码如下所示:
Service_manager.c
1 | int do_add_service(struct binder_state *bs,uint16_t*s, unsigned len, |
将上面的函数暂时放一下,先介绍svc_can_register
函数。
1不是什么都可以注册的
do_add_service
函数中的svc_can_register
,是用来判断注册服务的进程是否有权限的,代码如下所示:
Service_manager.c
1 | int svc_can_register(unsigned uid, uint16_t *name) |
allowed
结构数组,控制那些权限达不到root和system的进程,它的定义如下所示:
1 | static struct { |
所以,如果Server进程权限不够root和system,那么请记住要在allowed
中添加相应的项。
2. 添加服务项
再回到我们的do_add_service
,如下所示:
Service_manager.c
1 | int do_add_service(struct binder_state *bs,uint16_t*s, unsigned len, |
至此,服务注册分析完毕。可以知道,ServiceManager
不过就是保存了一些服务的信息。那么,这样做又有什么意义呢?
ServiceManger
能集中管理系统内的所有服务,它能施加权限控制,并不是任何进程都能注册服务。ServiceManager
支持通过字符串名称来查找对应的Service。这个功能很像DNS。- 由于各种原因,Server进程可能生死无常。如果让每个Client都去检测,压力实在太大。现在有了统一的管理机构,Client只需要查询
ServiceManager
,就能把握动向,得到最新信息。这可能正是ServiceManager
存在的最大意义吧。
MediaPlayerService和它的Client
前面,一直在讨论ServiceManager
和它的Client
,现在我们以MediaPlayerService
的Client
换换口味吧。由于ServiceManager
不是从BnServiceManager
中派生的,所以之前没有讲述请求数据是如何从通讯层传递到业务层来处理的过程。本节,我们以MediaPlayerService
和它的Client
做为分析对象,试解决这些遗留问题。
查询ServiceManager
前文曾分析过ServiceManager
的作用,一个Client想要得到某个Service的信息,就必须先和ServiceManager
打交道,通过调用getService
函数来获取对应Service的信息。请看来源于IMediaDeathNotifier.cpp中的例子getMediaPlayerService()
,它的代码如下所示:
IMediaDeathNotifier.cpp
1 | /* |
有了BpMediaPlayerService
,就能够使用任何IMediaPlayerService
提供的业务逻辑函数了。例如createMediaRecorder
和createMetadataRetriever
等。
显而易见的是,调用的这些函数都将把请求数据打包发送给Binder驱动,由BpBinder
中的handle
值找到对应端的处理者来处理。这中间经历过如下的过程:
- (1)通讯层接收到请求。
- (2)递交给业务层处理。
##子承父业
根据前面的分析可知,MediaPlayerService
驻留在MediaServer
进程中,这个进程有两个线程在talkWithDriver
。假设其中有一个线程收到了请求,它最终会通过executeCommand
调用来处理这个请求,实现代码如下所示:
IPCThreadState.cpp
1 | status_t IPCThreadState::executeCommand(int32_tcmd) |
BBinder
和业务层有什么关系?还记得图6-3吗?我们以MediaPlayerService
为例,来梳理一下其派生关系,如图6-5所示:
图6-5 MediaPlayerService
家谱
BnMediaPlayerService
实现了onTransact
函数,它将根据消息码调用对应的业务逻辑函数,这些业务逻辑函数由MediaPlayerService
来实现。这一路的历程,如下面的代码所示:
Binder.cpp
1 | status_t BBinder::transact( |
IMediaPlayerService.cpp
1 | status_t BnMediaPlayerService::onTransact(uint32_tcode, const Parcel& data, |