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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容