Android框架揭秘-Android Binder IPC笔记

1. Binder 本是IPC工具, 但在Android中主要用于支持RPC。

2. Binder使用运行在内核空间中的抽象驱动程序Binder Driver完成进程间的IPC数据交换。


3. IPC数据由待调服务号、待调函数名、Binder协议构成。


4. Binder Driver是字符设备驱动程序,通过调用open或ioct1即可访问,

系统调用open与Binder Driver的binder_open函数连接在一起。


5. Binder IPC数据传递

应用程序通过Binder尝试RPC时, 先进行open系统调用,获取Binder Driver的文件描述符,

而后通过mmap系统调用,在内核中开辟一块区域, 以便存放接收的IPC数据。

最后调用ioctl函数,将IPC数据作为参数,传递给Binder Driver。

Binder Driver根据IPC数据中的服务号, 将IPC数据传递到服务端,并根据RPC数据找到要调用的函数,完成RPC调用。


6. Binder IPC数据流



7. Binder寻址

ContextManager负责服务的添加检索等管理,它为每个服务分配一个Handle编号,ContextManager自身的Handle编号为0。


7.1服务注册时的寻址

Service Server向Binder Driver发送IPC数据,其中包含RPC代码(ADD_SERVICE),RPC数据(注册服务的名称),并且Handle值设置为0。


Binder Driver首先查找Handle值为0的Binder节点(Node),Handle值为0的Binder节点是ContextManager。


Service Server会将IPC数据传递给ContextManager,Binder Driver会生成一个Binder节点用于表示Service Server中的服务A。


接下来生成Binder节点引用数据,以便Context Manager识别所生成的Binder节点,并将相关节点连接起来。


根据顺序,引用数据会被编号, 这种编号将插入到IPC数据中, 传递给ContextManager。


ContextManager会根据IPC数据中的服务名称与Binder节点编号,将服务注册到自身的服务的目录列表中。











7.2服务检索时的寻址

服务客户端将包含RPC代码(GET_SERVICE),RPC数据,值为0的Handle的IPC数据发送给Binder Driver。


Binder Driver通过Handle编号查找到ContextManagerBinder节点, 将IPC数据发送到ContextManager中。


ContextManager根据传递过来的IPC数据中包含的所请求的服务名称,在自身持有的服务列表中查找相应的服务编号,将编号发送给Binder Driver。


Binder Driver根据传递过来的服务编号查找对应的引用数据,然后再服务客户端生成引用数据,并将其与Context Manager引用数据所指的Binder节点连接起来。


Binder Driver将根据生成顺序为引用数据编号,并传递给服务客户端,服务客户端将编号制定为Handle,待后续使用服务时用。



7.3服务使用时的寻址

服务客户端把相关的RPC代码与RPC数据及上一步查到服务是保存的Handle编号包含进IPC数据中,发送给Binder Driver。


Binder Driver根据Handle编号,查找到服务Binder节点, 将IPC数据发送到服务端。


服务端根据收到的RPC数据,调用相关的函数, 完成RPC调用。




8. Android Binder Driver分析

8.1 从进程的角度看服务的使用

8.1.1服务注册

首先Context Manager调用open()函数, 打开Binder Driver, 而后调用mmap()函数, 在内核空间中开辟一块用于接收IPC数据的Buffer,

在调用ioctl()进入待机状态,等待接收IPC数据。


为了注册服务, Service Server先打开Binder Driver, 而后调用mmap()开辟一块Buffer,用于接收IPC应答数据。


Service Server生成IPC数据, IPC数据包含RPC代码(ADD_SERVICE), RPC数据值为0的Handle编号。


Service Server调用ioctl()函数向Binder Driver传递IPC数据。而后Binder Driver将来自Service Server的IPC数据传递给Context Manager。


Context Manager分析IPC数据中的RPC代码, 并根据RPC代码及数据调用相应的服务注册函数,完成注册。


注册完成后,Context Manager会发送IPC应答数据给Service Server,告知服务已经注册完成。


8.1.2服务检索

服务客户端在使用Service Server时会向Context Manager请求服务的编号,这个过程称为检索。


首先服务客户端打开Binder Driver,并调用mmap函数准备一块Buffer,用于接收IPC应答数据。


将生成调用Context Manager的IPC数据, 并传递给Binder Driver。


然后Binder Driver将IPC数据传递给Context Manager, Context Manager根据收到的IPC数据中的RPC 代码及数据调用相应的函数。


8.1.3服务使用

Service server处于待机状态,等待接收IPC数据。


服务客户端将上面检索到的Binder节点编号保存到IPC数据的Handle中, 生成IPC数据传递给Binder Driver, Binder Driver根据Handle查找到相应的

binder_node结构体,并据此查找到注册有binder_node结构体的Service Server的binder_proc结构体, 然后将IPC数据拷贝到注册证binder_proc结构体中的binder_buffer中去。


Binder Driver让服务客户端进入待机状态,唤醒处于待机状态的Service Server,Service Server苏醒后接收IPC数据,通过IPC数据中的RPC代码及数据调用相应服务函数。


服务函数执行完毕后,Service Server会生成IPC应答数据,并将其传递给Binder Driver。 Binder Driver将应答数据传递给服务客户端, 完成远程服务调用。


8.2 Binder Driver函数分析

当调用open系统函数时,Binder Driver的节点文件/dev/binder/ 将作为参数传给所调用的函数, Binder Driver的文件描述符作为返回值返回给进程。

/dev/binder设备节点由Binder设备驱动源代码binder.c的binder_init()函数中的misc_register()函数创建。

8.2.1 binder_open()函数

binder_open函数将为打开Driver的进程生成并初始化binder_proc结构体,并且初始化待机队列,用来将进程切换到待机状态下。

binder_proc结构体是Binder Driver的根结构体,用来管理Binder IPC所需要的各种信息,包括打开Driver的进程信息, 接收IPC数据的Buffer信息等。

另外binder_proc结构体还拥有Binder中其他结构体的指针。


binder_open函数将在/proc/binder/proc目录下生成文件, 以便初始化binder_proc结构体或显示与Binder IPC相关的信息。



8.2.2 binder_mmap()函数

mmap函数中内核空间中开辟出一块用于接收IPC数据的Buffer,将其与用户空间的Buffer映射在一起,并将内核空间的Buffer注册到binder_proc结构体中。

首先通过get_vm_area向系统申请一块可用的虚拟内存空间,


然后使用proc->buffer=area->addr,proc_buffer即拥有了内核空间中接收IPC数据的Buffer的起始地址。


调用binder_update_page_range函数, 分配物理页, 并将存在于物理内存,内核空间接收Buffer与用户空间的接收Buffer映射起来。


 8.2.3 binder_ioctl函数分析


8.2.3.1服务注册阶段


binder_ioctl函数负责制两个进程间收发IPC数据以及IPC应答数据。

进程通过ioctl系统调用, 调用binder_ioctl函数, Binder Driver根系随ioctl函数调用传递来的iotcl命令, 而后作出相应的动作。


用户空间的IPC数据存放在binder_write_read结构体中, 用户调用ioctl函数,

Binder Driver调用copy_from_user函数将用户空间中的数据拷贝到自身的binder_write_read结构体中,

(相反,在传递IPC应答数据时,Binder Driver将调用copy_to_user函数将自身binder_write_read中的数据拷贝至用户空间。)


调用binder_thread_write, 通过get_user从binder_write_read.write_buffer 结构体中取出Binder协议(命令),


在服务注册时, 所使用的Binder协议是BC_TRASACTION, 再次调用copy_from_user将IPC数据中不包含协议的 binder_transaction_data

复制到内核空间中。这时再次使用copy_from_user是因为, 上步中,只是复制了buffer指针, 并没有复制buffer中的内容。


调用binder_transaction函数, binder_transaction函数使用binder_transaction_data结构体中的数据来执行Binder寻址,复制Binder IPC数据, 生成及检索Binder节点。


Binder Driver根据IPC数据中的handle查找接收端的binder_node,通过binder_node获取接收端的binder_proc结构体,

给binder_proc结构体的todo赋值,以指定要执行的任务。

获取接收端的wait_queue。

生成binder_transaction结构体,用来向接收端发送IPC数据。


填充binder_transaction数据

<1>将发送IPC数据的binder_thread赋值给binder_transaction的from,以便接收方发送应答数据。

<2>将IPC数据的RPC代码赋值给binder_transaction结构体的code变量。

<3>调用binder_alloc_buf函数,分配一块空间,用来从接收端的IPC数据接收缓冲区中复制IPC数据。

      该接收缓冲空间由接收端在binder_mmap函数中确定,binder_proc结构体的free_buffers指向该区域。

<4>拷贝RPC数据到binder_buffer结构体的data变量中。

在IPC数据中, Handle,RPC代码, Binder协议三种数据形态都是一定的,其尺寸固定不变,但RPC数据是可变的,

在IPC过程中,Android将根据RPC数据的具体尺寸动态分配不同的大小。


接下来接收端将从待机状态中苏醒,并从该结构体中获取数据。


下面分析为各服务创建Binder节点的过程。

IPC 的RPC数据包含flat_binder_object结构体时,会执行case BINDER_TYPE_BINDER后面的代码,创建binder节点。

<1>调用binder_get_node函数,在注册至binder_proc结构体中的Binder节点中,确认当前的Binder节点是否已经存在。

<2>若不存在,调用binder_new_node函数,创建新的binder_node结构体并返回。

<3>嗲用binder_get_ref_for_node函数,检查指定的Binder节点是否已经存在于指定的binder_proc结构体中,

若存在返回binder节点所在的binder_ref结构体,否则新建binder_ref结构体并将指定的binder节点注册到其中。

binder_ref结构体中的desc编号用来做Context Manager管理的服务列表中区分不同的服务。

然后将binder_ref结构体注册到binder_proc结构体中的refs_by_node变量中。binder_ref结构体是Binder节点索引。


至此,Service Server创建了待注册服务的Binder节点,并将其注册到Context Manager中。

Service Server完成了在服务注册阶段传递IPC数据的所有准备工作, 接下来Context Manager从待机状态中唤醒,

使之获取binder_transaction结构体中的IPC数据。


唤醒Context Manager的过程

首先将binder_work结构体的type变量值为BINDER_WORK_TRANSACTION

然后将Context Manager的binder_proc结构体的todo变量赋值给target_list,并注册到binder_work结构体中。

Context Manager以binder_transaction结构体中的binder_work结构体中的type变量为基础, 执行动作。


最后在binder_transaction函数中调用 wake_up_interruptible函数, 让Context Manager从待机状态脱离。


Context Manager被唤醒后,首先通过binder_work结构体查找binder_transaction结构体(该结构体是Service Server中生成的)。

首先调用list_first_entry函数, 查找Service Server注册的binder_work结构体,

若binder_work结构体的type成员变量值为BINDER_WORK_TRANSACTION,则继续调用container_of函数,

查找持有binder_work结构体的binder_transaction结构体。


然后Context Manager通过Service Server创建的binder_transaction结构体获取IPC数据,并将IPC数据保存到binder_transaction_data结构体,

以便将IPC数据传递到用户空间,并将数据保存到Context Manager 的binder_write_read结构体的成员变量read_buffer中。


最后将Binder协议设置为BR_TRANSACTION准备处理Context Manager的IPC数据。


Binder Driver的binder_ioctl函数执行结束, Context Manager调用的ioctl函数返回。

Context Manager分析binder_write_read结构体的read_buffer中的Binder协议,而后调用binder_transaction_data结构体的RPC代码所指的函数,

并将RPC数据作为参数传递给调用的函数。


经过这一系列的操作, Service Server的服务即被注册到Context Manager中。


8.2.3.2服务检索阶段

<1>首先Context Manager进入待机状态, 等待接收IPC数据。

<2>服务客户端生成IPC数据中包含Context manager的Handle(0),RPC代码,以及请求的服务名称,并且不包含flat_binder_object结构体

      所以不会创建binder_node结构体。发送完IPC数据后, 服务客户端进入待机状态。

<3>Context Manager接收IPC数据后, 会在服务目录中生成binder_object结构体,该结构体中包含着与服务名称相对应的服务目录编号。

      并且将结构体中的type变量设置为BINDER_TYPE_HANDLE,生成IPC应答数据,binder_object结构体会被复制到Binder Driver的flat_binder_object中,

     用来查找Binder节点。在下一个阶段中, 通过binder_object数据, 将Binder节点传递给服务客户端。

<4>服务客户端将获取所用服务的Binder节点。服务注册阶段的代码中flat_binder_object结构体的type变量为BINDER_TYPE_BINDER, 生成Binder节点。

     服务检索时type 为BINDER_TYPE_HANDLE, Binder节点被传递给服务客户端。

     首先调用binder_get_ref函数, 第一个参数proc为Context Manager的binder_proc结构体, 第二个参数fp->handle是服务客户端要使用的服务编号。即binder_ref中desc的   值,将desc的值传入binder_get_ref后即可获取与服务编号相对应的binder_ref结构体, 此结构体由Context Manager持有。

    调用binder_get_ref_for_node函数, 每当binder_get_ref_for_node函数向服务客户端注册Binder节点时都会生成一个binder_ref结构体, 同时desc的值递增。

    然后修改RPC数据中flat_binder_object结构体内的服务编号,将其设置为当前binder_ref结构体的desc编号。之后将包含修改后的RPC数据的IPC应答数据发送到服务客户端。


8.2.3.3服务使用阶段

<1>Service Server进入待机状态, 等待接收IPC数据。

<2>IPC数据包含与所用服务函数相关的RPC代码与RPC数据, 将Handle设置为服务检索阶段获取到的Binder节点编号。

<3>Binder Driver根据handle值, 查找到binder_ref,然后通过binder_ref查找到binder_node, 再根据binder_node查找binder_proc, 从服务的使用阶段开始, RPC数据中不再存在flat_binder_object结构体, 不需要生成或检索Binder节点。

<4>Binder Driver将IPC数据传递到Service Server, Service Server根据收到的IPC数据,调用相应的函数。


9 Context Manager

Context Manager对应的进程为servicemanager, 通过init.rc脚本, 可以看到Context Manager在mediaserver与 system server之前先运行了。

Context Manager采用C语言编写,源码是service_manager.c


Context Manager的main函数大致可以分为三部分。

<1>调用binder_open函数,引起open与mmap函数调用。

<2>调用binder_become_context_manager函数,其中仅有一条ioctl函数, 向Binder Driver传递BINDER_SER_CONTEXT_MGR的ioctl命令,

Binder Driver将通过binder_new_node函数, 生成编号为0的Binder节点, 并将其注册到binder_context_mgr_node全局变量中。

<3>在binder_loop函数中,在for循环中调用ioctl进入待机状态,等待接收IPC数据。

当接收到IPC数据时, binder_ioctl函数返回时,调用binder_parse函数分析IPC数据。

<4>在binder_parse函数中, Context Manager首先解析IPC数据中的Binder协议,在服务注册过程中,所使用的Binder协议为BR_TRANSACTION.

继续调用func函数------对应svcmgr_handler函数, svcmgr_handler函数根据binder_transaction_data结构体中的code执行服务注册或者检索两大任务。



















































  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值