1、概述
在Android中,系统提供的服务被包装成一个个系统级service,这些service往往会在设备启动之时添加进Android系统,当某个应用想要调用系统某个服务的功能时,往往是向系统发出请求,调用该服务的外部接口。在上一节我们了解到,这种外部接口,我们通常称之为代理接口,也就是我们要拿到目标服务对应的代理对象。
//TODO
在Android8.0后,谷歌引入Treble机制,binder机制增加了hwbinder和vndbinder,其中vndbinder的守护进程为vndservicemanager。
vndservicemanager和service共用同一份代码,只是传入的参数和宏控制的流程有部分差异。
vndservicemanager会传入参数"/dev/vndbinder",servicemanager使用默认的"/dev/binder"。
servicemanager主要做了以下几件事:
1、打开binder驱动,申请了128k的内存空间
2、然后调用binder_become_context_manager()让自己成为整个系统中唯一的上下文管理器,其实也就是service管理器
3、调用binder_loop()进入无限循环,不断监听并解析binder驱动发来的命令
2、Binder架构
//TODO
3、servicemanager的启动
//TODO
4、service_manager调用栈:
//TODO
5、源码分析
5.1 主程序启动main()
//TODO
5.2 binder_open()
servicemanager启动后,先通过binder_open()来打开"/dev/binder",代码如下:
binder_open()的工作也比较简单,分为以下几步:
1、通过系统调用open()来打开"/dev/binder",获得一个句柄信息,在Binder驱动重对应的是函数binder_open()
2、通过ioctl获取binder的版本信息,比较binder协议版本是否相同,不同则跳出,在Binder驱动重对应的是函数binder_ioctl()
3、通过mmap内存映射128k的内存空间,即把binder驱动文件的128K字节映射到了内存空间,这128K内存空间的servicemanager使用,在Binder驱动重对应的是函数binder_mmap()。
其他的binder服务进程会映射BINDER_VM_SIZE((1*1024*1024)-sysconf(SC_PAGE_SIZE)*2)的内存空间,SC_PAGE_SIZE表示一个page页的大小,通常情况下为4K,即(1M-4K*2)=(1M-7K)
这个page的大小,不同厂家有时候也会调整大小,一般有1M,64K,4K,1KB,通常为4K。
ServiceManager进程mmap的内存大小可以通过adb shell命令得出:
其中0x7457b61000 -0x745d41000=0x20000,转成10进制,即为128K
ARM32内存映射:
虚拟空间的低3GB部分从0-0XBFFFFFFF的虚拟线性地址,用户态和内核态都可以寻址,这部分也是每个进程的独立空间。
虚拟空间的高1G部分从0XC00000000到0XFFFFFFFF的虚拟地址,只有内核态的进程才能访问,这种限制由页目录和页眉描述符的权限标示位决定,通过MML启动控制
ARM64内存映射:
默认情况下,32位系统默认只能支持4G的内存,在打开PAE后,最大可以扩展到64G的内存,随着物理硬件的不断升级,现在的内存越来越大,因此基本上都切换到了64位系统。
理论上讲,64位的地址总线可以支持高达16EB(2^64)的内存。
2^64次方太大了,Linux内核只采用了64bits的一部分(开启CONFIG_ARM64_64K_PAGES时使用42bits,页大小是4K时使用39bits),该文假设使用的页大小是4K(VA_BITS=39)
ARM64有足够的虚拟地址,用户空间和内核空间可以有各自的2^39=512GB的虚拟地址。
需要注意到,32位应用仍然拥有512GB的内核虚拟地址空间,并且不与内核共享自己的4GB空间,但在ARM32上,32位应用只有3GB的地址空间。
ARM32和ARM64内存地址比较:
5.3 binder_become_context_manager()
binder_become_context_manager()的作用是让servicemanager成为整个系统中唯一的上下文管理器,其实也就是service管理器,这样我们就可以把ServiceManager称之为守护进程。
对应的binder驱动中操作如下:
从用户空间拷贝ioctl的参数,调用binder_ioctl_set_ctx_mgr()进行设置
BINDER_SET_CONTEXT_MGR_EXT带参数,BINDER_SET_CONTEXT_MGR不带参数
binder_ioctl_set_ctx_mgr()的流程也比较简单
1、先检查当前进程是否具有注册Context Manager的SEAndroid安全权限
2、如果具有SELinux权限,会为整个系统的上下管理器专门生成一个binder_node节点,便该节点的强弱应用加1
3、新创建的binder_node节点,记入context->binder_context_mgr_node,即ServiceManager进程的context binder节点,使之成为serviceManager的binder管理实体
5.4 binder_loop()
下一步进行守护进程的循环处理,binder_loop()会先向binder驱动发出了BC_ENTER_LOOPER命令,告诉binder驱动"本线程要进入循环状态了",接着进入一个for循环不断调用ioctl()读取发来的数据,接着解析这些数据
其中最重要的一个结构体是binder_write_read,它用来记录Binder buffer中读和写的数据信息结构体如下:
5.5 binder_parse()
在binder_loop()进入for循环之后,核心处理流程就是ioctl和binder_parse(),即不停的从Binder驱动接收读写数据,进行binder解析后,进行处理。
在binder_loop()中声明了一个128字节的栈内存-readbuf,用BINDER_WRITE_READ命令从驱动读取一些内容,并传入binder_parse(),binder_parse()根据binder驱动传来的"BR_XXX"协议码,进行相关的逻辑处理,最重要的有三个"BR_XXX"协议:
BR_TRANSACTION:事务处理,解析binder_transaction_data的数据,调用回调函数svcmgr_handler()进行服务的注册,获取等操作
BR_REPLY:消息回复
BR_DEAD_BINDER:死亡通知
只要binder_parse()解析正常,binder_loop()就会一直执行下去,ServiceManager进程不退出。
binder_parse()解析binder驱动传来的readbuf的内存,readbuf拥有128字节的栈内存,每次可以只处理一个cmd,也可以有多个cmd,所以存在一个while循环,可以同时解析多个cmd,多个cmd的结构体如下图所示:
5.5.1 BR_XXX 协议码分析
BR_XXX码,也称为Binder响应码,这里介绍了ServiceManager处理的一些响应码的作用:
5.5.2 BR_TRANSACTION解析
我们这里单独分析下BR_TRANSACTION的流程,这也是我们常用的一个流程,这是Binder驱动向Server端发送请求数据。
从readbuf中解析出binder_transaction_data的数据,最后对接收和发送数据进行了封装,传递给svcmgr_handler()做详细处理
从上main的逻辑看,我们重点关注的是binder_transaction_data这个结构,binder_transaction_data说明了transaction到底在传输什么语义,而语义码就记录在其code成员中,不同语义码需要携带的数据也是不同的,这些数据由data指定。
结构体说明如下:
从上面binder_transaction_data的结构可以看出,data可存入的数据很少,主要采用了数据其实地址和数据偏移量,根据代码上下文可知,调用了bio_init_from_txn(),从txn.transaction_data解析出binder_io的信息,存入msg
5.5.2.1 bio_init_from_txn()
bio_init_from_txn()的作用就是把binder_transaction_data的"数据起始地址","偏移量","data数据的总大小"和"偏移数组中可用的条目":
binder_transaction_data和binder_io的关联
初始化完binder_io的replay,并把transaction_data转换成了binder_io的msg后,调用回调函数svcmgr_handler()进行最终逻辑处理
5.6 svcmgr_handler()
在BR_TRANSACTION的命令解析后,就把binder_transaction_data_secctx的数据传给回调函数svcmgr_handler()进行处理。
根据不同的传输语义码(txn->code)来进行相应的操作:查询服务,注册服务,以及列举所服务
源码如下:
5.7 ServiceManager是如何管理service信息的?
//TODO
5.8 注册服务
根据传入的code:SVC_MGR_ADD_SERVICE得知,本次binder流程想要进行服务注册。
步骤:
从binder_io msg中获取服务名称和长度
从binder_io msg中获取handle
检查该服务是否有注册的selinx权限
查询服务列表svclist是否存在该handle,如果有handle,就更新该服务的handle信息,通过这个handle我们最终就能找到远端的service实体
如果svclist不存在该服务,申请一个svcinfo的空间,把服务名,长度,handle等信息存入其中
把svcinfo进入svclist的链表中
再以BC_ACQUIRE命令,handle为目标的信息,通过ioctl发送给binder驱动
最后以BC_REQEST_DEATH_NOTIFICATION命令的信息,通过ioctl发送给binder驱动,主要用于清理内存等收尾工作
5.9 查找服务
//TODO
6、总结
//TODO