必威体育Betway必威体育官网
当前位置:首页 > IT技术

Binder机制详解

时间:2019-09-02 13:43:17来源:IT技术作者:seo实验室小编阅读:58次「手机版」
 

binder机制

Binder机制

Android系统中进程间通讯(IPC)的一种方式,Android中contentprovider、intentaidl都是基于Binder

内存管理

​ Binder最大只能传1M的数据,因为Binder驱动只预留了一段1M大小的虚拟地址

mmap中定义BINDER_VM_SIZE为1M,Binder数据需要跨进程传递,需要在内核上开辟空间,所以允许在Binder上传递的数据不是无限大的

Binder 架构

​ 通信采用C/S架构,包含Client、 Server、 ServiceManager 以及 Binder 驱动

在 framework 层进行了封装,通过 JNI 技术调用 Native(C/C++)层的 Binder 架构

在 Native 层以 ioctl 的方式与 Binder 驱动通讯

在这里插入图片描述

Binder 机制

在这里插入图片描述

注册服务端

​ 通过 ServiceManager 注册服务。向 Binder 驱动的全局链表 binder_procs 中插入服务端的信息,然后向ServiceManager的svcinfo列表中缓存注册的服务

获取服务端

​ 通过 ServiceManager 向 svcinfo 列表中查询,返回服务端的代理

发送请求

通过BinderProxy将请求参数发送给ServiceManager

通过共享内存的方式使用内核方法copy_from_user()将参数拷贝到内核空间

客户端进入等待状态

Binder驱动向服务端的todo队列里面插入一条事务

事务执行完之后把执行结果通过 copy_to_user()将内核的结果拷贝到用户空间(只执行拷贝命令,不拷贝数据,binder只进行一次拷贝)

唤醒客户端并响应结果

Binder 驱动

​ 运行在内核空间,负责各个用户进程通过 Binder 通信的内核模块

在这里插入图片描述

用户空间

用户程序的运行空间

只能执行简单的运算,不能直接调用系统资源,必须通过系统接口(又称 system call),才能向内核发出指令

用户空间访问内核空间的唯一方式就是系统调用,如访问文件,网络等。通过统一接口,资源访问均在内核控制下执行,以免用户程序对系统资源的越权访问。

内核空间

linux 内核的运行空间

可以执行任意命令,调用系统的一切资源

他们之间考虑到安全因素是隔离的,即使用户程序崩溃,内核也不受影响

内核状态

当一个任务(进程)执行系统调用而陷入内核代码中执行时的状态,此时处理器处于特权级最高的(0级)内核代码中执行

用户运行态

当进程在执行用户自己的代码时,此时处理器在特权级最低的(3级)用户代码中运行

Binder 进程与线程

在这里插入图片描述

对于底层Binder驱动,通过 binder_procs 链表记录所有创建的 binder_proc 结构体,binder 驱动层的每一个 binder_proc 结构体都与用户空间的一个用于 binder 通信的进程一一对应,且每个进程有且只有一个 ProcessState 对象,这是通过单例模式来保证的。在每个进程中可以有很多个线程,每个线程对应一个 IPCThreadState 对象,IPCThreadState 对象也是单例模式,即一个线程对应一个 IPCThreadState 对象,在 Binder 驱动层也有与之相对应的结构,那就是 Binder_thread 结构体。在 binder_proc 结构体中通过成员变量 rb_root threads,来记录当前进程内所有的 binder_thread。

Binder 线程池:每个 Server 进程在启动时创建一个 binder 线程池,并向其中注册一个 Binder 线程;之后 Server 进程也可以向 binder 线程池注册新的线程,或者 Binder 驱动在探测到没有空闲 binder 线程时主动向 Server 进程注册新的的 binder 线程。对于一个 Server 进程有一个最大 Binder 线程数限制,默认为16个 binder 线程,例如 Android 的 system_server 进程就存在16个线程。对于所有 Client 端进程的 binder 请求都是交由 Server 端进程的 binder 线程来处理的。

ServiceManager 启动

分为 framework 层和 native 层,framework 层只是对 native 层进行了封装方便调用

启动是系统在开机时,init 进程解析 init.rc 文件调用 service_manager.c 中的 main() 入口启动的

在这里插入图片描述

启动过程

1、打开驱动创建全局链表 binder_procs

2、将自己当前进程信息保存到 binder_procs 链表

3、开启 loop 不断的处理共享内存中的数据,并处理响应

ServiceManager 注册服务

​ 通过 ServiceManager 的 addService() 方法来注册服务

在这里插入图片描述

注册过程

1、ServiceManager 向 Binder 驱动发送 BC_transaction 命令,携带 ADD_SERVICE_TRANSACTION 命令

2、注册服务的线程进入等待状态。waitforresponse()

3、Binder 驱动收到请求命令向 todo 队列里面添加一条注册服务的事务

4、事务处理完之后发送 BR_TRANSACTION 命令

5、ServiceManager 收到命令后向 svcinfo 列表中添加已经注册的服务

6、最后发送 BR_REPLY 命令唤醒等待的线程,通知注册成功

ServiceManager 获取服务

在这里插入图片描述

获取过程

1、ServiceManager 向 Binder 驱动发送 BC_TRANSACTION 命令携带 CHECK_SERVICE_TRANSACTION 命令

2、获取服务的线程进入等待状态 waitForResponse()

3、Binder 驱动收到请求命令向 todo 队列里面添加一条查询服务的事务

4、如果查询到,事务发送BC_TRANSACTION命令唤醒

5、ServiceManager 收到命令后向 svcinfo 列表中添加已经注册的服务

6、最后发送 BR_REPLY 命令唤醒等待的线程,通知查询成功

7、如果未查询到,与 binder_procs 链表中的服务进行一次通讯再响应

完整通讯

在这里插入图片描述

步骤

1、通过 ServiceManager 获取到服务端的 BinderProxy 代理对象

2、调用 BinderProxy 将参数,方法标识(如:TRANSACTION_test,AIDL中自动生成)传给 ServiceManager

3、客户端线程进入等待状态

4、ServiceManager 将用户空间的参数等请求数据复制到内核空间,并向服务端插入一条执行执行方法的事务

5、事务执行完通知 ServiceManager 将执行结果从内核空间复制到用户空间

6、唤醒等待的线程,响应结果

问题

传统的 Linux 通信机制,比如 socket,管道等都是内核支持的;但是 Binder 并不是 Linux 内核的一部分,它是怎么做到访问内核空间的呢

​ Linux 的动态可加载内核模块(Loadable Kernel Module,LKM)。

模块是具有独立功能的程序,它可以被单独编译,但不能独立运行

它在运行时被链接到内核作为内核的一部分在内核空间运行

Binder一次通信中进行了几次有效数据拷贝

​ 一次。

Binder驱动程序位于内存的内核空间,Binder服务程序位于位于内存的用户空间,为Binder驱动和Binder服务分配了同一块物理内存地址

ServiceManager通过内核方法 copy_from_user()将数据拷贝到内核空间,这个过程中数据的物理地址发生了变化

事务将执行结果通过copy_to_user() 将结果拷贝到用户空间,这个过程中只传递了数据的虚拟地址,数据的物理地址没有变化

还有那些IPC通讯方式

​ linux中消息队列、共享内存、信号量、socket

socket传输效率低,开销大,用于网络和进程间低速通信,两次数据拷贝

消息队列、信号量采用存储-转发方式,两次拷贝

共享内存控制复杂,0次拷贝

相关阅读

【Spring源码分析】08-DataBinder

DataBinder实现了TypeConverter和PropertyEditorRegistry接口提供了类型转换功能,并且在设置值的同时做Validation。 DataBinder

DataBinder.Eval用法范例

//一、DataBinder.Eval的基本格式//在绑定数据时经常会用到这个句程序:<%...# DataBinder.Eval(Container.DataItem,"xxxx")%>或

分享到:

栏目导航

推荐阅读

热门阅读