Binder系列5—注册服务(addService)

一.概述

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是一个完整的事务过程。 到此,其他进行便可以获取该服务,使用服务提供的方法,下一篇文章将会讲述如何获取服务

推荐阅读更多精彩内容