一.概述
1.1 media服务注册
media入口函数是main_mediaserver.cpp中的main()方法,代码如下:
过程说明:
获取ServiceManager: 讲解了defaultServiceManager()返回的是BpServiceManager对象, 用于跟servicemanager进程通信;
理解Binder线程池的管理, 讲解了startThreadPool和joinThreadPool过程.
本文的重点就是讲解Native层服务注册的过程.
1.2 类图
在Native层的服务以media服务为例,来说一说服务注册过程,先来看看media的整个的类关系图
图解:
蓝色代表的是注册MediaPlayerService服务所涉及的类
绿色代表的是Binder架构中与Binder驱动通信过程中的最为核心的两个类;
紫色代表的是注册服务和获取服务的公共接口/父类;
1.3 时序图
先通过一幅图来说说,media服务启动过程是如何向servicemanager注册服务的。
二. ProcessState
2.1 ProcessState::self
[-> ProcessState.cpp]
获得ProcessState对象: 这也是单例模式,从而保证每一个进程只有一个ProcessState对象。其中gProcess和gProcessMutex是保存在Static.cpp类的全局变量。
2.2 ProcessState初始化
[-> ProcessState.cpp]
ProcessState的单例模式的惟一性,因此一个进程只打开binder设备一次,其中ProcessState的成员变量mDriverFD记录binder驱动的fd,用于访问binder设备。
BINDER_VM_SIZE = (1*1024*1024) - (4096 *2), binder分配的默认内存大小为1M-8k。
DEFAULT_MAX_BINDER_THREADS = 15,binder默认的最大可并发访问的线程数为16。
2.3 open_driver
[-> ProcessState.cpp]
open_driver作用是打开/dev/binder设备,设定binder支持的最大线程数。关于binder驱动的相应方法,见文章Binder Driver初探。
ProcessState采用单例模式,保证每一个进程都只打开一次Binder Driver。
2.4 mmap
参数说明:
addr: 代表映射到进程地址空间的起始地址,当值等于0则由内核选择合适地址,此处为0;
size: 代表需要映射的内存地址空间的大小,此处为1M-8K;
prot: 代表内存映射区的读写等属性值,此处为PROT_READ(可读取);
flags: 标志位,此处为MAP_PRIVATE(私有映射,多进程间不共享内容的改变)和 MAP_NORESERVE(不保留交换空间)
fd: 代表mmap所关联的文件描述符,此处为mDriverFD;
offset:偏移量,此处为0。
mmap()经过系统调用,执行binder_mmap过程。
三. 服务注册
3.1 instantiate
[-> MediaPlayerService.cpp]
注册服务MediaPlayerService:由defaultServiceManager()返回的是BpServiceManager,同时会创建ProcessState对象和BpBinder对象。 故此处等价于调用BpServiceManager->addService。其中MediaPlayerService位于libmediaplayerservice库.
3.2 BpSM.addService
[-> IServiceManager.cpp ::BpServiceManager]
服务注册过程:向ServiceManager注册服务MediaPlayerService,服务名为”media.player”;
3.2.1 writeStrongBinder
[-> parcel.cpp]
3.2.2 flatten_binder
[-> parcel.cpp]
将Binder对象扁平化,转换成flat_binder_object对象。
对于Binder实体,则cookie记录Binder实体的指针;
对于Binder代理,则用handle记录Binder代理的句柄;
关于localBinder,代码见Binder.cpp。
3.2.3 finish_flatten_binder
3.2.3 finish_flatten_binder
将flat_binder_object写入out。
3.3 BpBinder::transact
[-> BpBinder.cpp]
Binder代理类调用transact()方法,真正工作还是交给IPCThreadState来进行transact工作。先来 看看IPCThreadState::self的过程。
3.3.1 IPCThreadState::self
[-> IPCThreadState.cpp]
\TLS是指Thread local storage(线程本地储存空间),每个线程都拥有自己的TLS,并且是私有空间,线程之间不会共享。通过pthread_getspecific/pthread_setspecific函数可以获取/设置这些空间中的内容。从线程本地存储空间中获得保存在其中的IPCThreadState对象。
3.3.2 IPCThreadState初始化
[-> IPCThreadState.cpp]
每个线程都有一个IPCThreadState,每个IPCThreadState中都有一个mIn、一个mOut。成员变量mProcess保存了ProcessState变量(每个进程只有一个)。
mIn 用来接收来自Binder设备的数据,默认大小为256字节;
mOut用来存储发往Binder设备的数据,默认大小为256字节。
3.4 IPC::transact
[-> IPCThreadState.cpp]
IPCThreadState进行transact事务处理分3部分:
errorCheck() //数据错误检查
writeTransactionData() // 传输数据
waitForResponse() //f等待响应
3.5 IPC.writeTransactionData
[-> IPCThreadState.cpp]
其中handle的值用来标识目的端,注册服务过程的目的端为service manager,此处handle=0所对应的是binder_context_mgr_node对象,正是service manager所对应的binder实体对象。binder_transaction_data结构体是binder驱动通信的数据结构,该过程最终是把Binder请求码BC_TRANSACTION和binder_transaction_data结构体写入到mOut。
transact过程,先写完binder_transaction_data数据,其中Parcel data的重要成员变量:
mDataSize:保存再data_size,binder_transaction的数据大小;
mData: 保存在ptr.buffer, binder_transaction的数据的起始地址;
mObjectsSize:保存在ptr.offsets_size,记录着flat_binder_object结构体的个数;
mObjects: 保存在offsets, 记录着flat_binder_object结构体在数据偏移量;
接下来执行waitForResponse()方法。
3.6 IPC.waitForResponse
[-> IPCThreadState.cpp]
在waitForResponse过程, 首先执行BR_TRANSACTION_COMPLETE;另外,目标进程收到事务后,处理BR_TRANSACTION事务。 然后发送给当前进程,再执行BR_REPLY命令。
3.7 IPC.talkWithDriver
[-> IPCThreadState.cpp]
binder_write_read结构体用来与Binder设备交换数据的结构, 通过ioctl与mDriverFD通信,是真正与Binder驱动进行数据读写交互的过程。 主要是操作mOut和mIn变量。
ioctl()经过系统调用后进入Binder Driver.
四. Binder Driver
ioctl -> binder_ioctl -> binder_ioctl_write_read
4.1 binder_ioctl_write_read
[-> binder.c]
4.2 binder_thread_write
4.3 binder_transaction
注册服务的过程,传递的是BBinder对象,故[小节3.2.1]的writeStrongBinder()过程中localBinder不为空, 从而flat_binder_object.type等于BINDER_TYPE_BINDER。
服务注册过程是在服务所在进程创建binder_node,在servicemanager进程创建binder_ref。 对于同一个binder_node,每个进程只会创建一个binder_ref对象。
向servicemanager的binder_proc->todo添加BINDER_WORK_TRANSACTION事务,接下来进入ServiceManager进程。
4.3.1 binder_get_node
从binder_proc来根据binder指针ptr值,查询相应的binder_node。
4.3.2 binder_new_node
4.3.3 binder_get_ref_for_node
handle值计算方法规律:
每个进程binder_proc所记录的binder_ref的handle值是从1开始递增的;
所有进程binder_proc所记录的handle=0的binder_ref都指向service manager;
同一个服务的binder_node在不同进程的binder_ref的handle值可以不同;
五. ServiceManager
由Binder系列3—启动ServiceManager已介绍其原理,循环在binder_loop()过程, 会调用binder_parse()方法。
5.1 binder_parse
[-> servicemanager/binder.c]
5.2 svcmgr_handler
[-> service_manager.c]
5.3 do_add_service
[-> service_manager.c]
\svcinfo记录着服务名和handle信息,保存到svclist列表。
5.4 binder_send_reply
[-> servicemanager/binder.c]
binder_write进入binder驱动后,将BC_FREE_BUFFER和BC_REPLY命令协议发送给Binder驱动, 向client端发送reply.
六. 总结
服务注册过程(addService)核心功能:在服务所在进程创建binder_node,在servicemanager进程创建binder_ref。 其中binder_ref的desc再同一个进程内是唯一的:
每个进程binder_proc所记录的binder_ref的handle值是从1开始递增的;
所有进程binder_proc所记录的handle=0的binder_ref都指向service manager;
同一个服务的binder_node在不同进程的binder_ref的handle值可以不同;
Media服务注册的过程涉及到MediaPlayerService(作为Client进程)和Service Manager(作为Service进程),通信流程图如下所示:
过程分析:
MediaPlayerService进程调用ioctl()向Binder驱动发送IPC数据,该过程可以理解成一个事务binder_transaction(记为T1),执行当前操作的线程binder_thread(记为thread1),则T1->from_parent=NULL,T1->from = thread1,thread1->transaction_stack=T1。其中IPC数据内容包含:
Binder协议为BC_TRANSACTION;
Handle等于0;
RPC代码为ADD_SERVICE;
RPC数据为”media.player”。
Binder驱动收到该Binder请求,生成BR_TRANSACTION命令,选择目标处理该请求的线程,即ServiceManager的binder线程(记为thread2),则 T1->to_parent = NULL,T1->to_thread = thread2。并将整个binder_transaction数据(记为T2)插入到目标线程的todo队列;
Service Manager的线程thread2收到T2后,调用服务注册函数将服务”media.player”注册到服务目录中。当服务注册完成后,生成IPC应答数据(BC_REPLY),T2->form_parent = T1,T2->from = thread2, thread2->transaction_stack = T2。
Binder驱动收到该Binder应答请求,生成BR_REPLY命令,T2->to_parent = T1,T2->to_thread = thread1, thread1->transaction_stack = T2。 在MediaPlayerService收到该命令后,知道服务注册完成便可以正常使用。
整个过程中,BC_TRANSACTION和BR_TRANSACTION过程是一个完整的事务过程;BC_REPLY和BR_REPLY是一个完整的事务过程。 到此,其他进行便可以获取该服务,使用服务提供的方法,下一篇文章将会讲述如何获取服务。