binder 原理

Posted by boredream on January 3, 2020

binder机制比较复杂,学习梳理过程中基本把网上可以搜到的文章都看了一遍 下面自己试着梳理一下,也会贴出参考的相关链接

https://blog.csdn.net/universus/article/details/6211589

为什么需要IPC

Android基于Linux,不同应用在不同的进程里,内存空间是隔开的,无法直接互相交互数据。 但安卓是有进程间通讯IPC的需求,则系统提供了Binder方式进行IPC。

为什么选Binder

https://www.zhihu.com/question/39440766 Linux本身有IPC方案,共享内存、管道、socket等, 但Android是取了一个性能、稳定型和安全性等的平衡点; 共享内存:0次数据拷贝,但无法直接解决进程间同步问题 管道/消息队列/socket:需要2次数据拷贝;

Binder机制下IPC的流程

因为是CS结构,所以分为Client和Server,我们日常使用中大部分是作为Client方的。 Android代码中直接context.getSystemtService(xxx)就是获取不同系统已封装好的Server的方法。 如 InputMethod / Download / Windown + Manager,为什么都是manager?

但Client不是直接和Server交互的,Android中是通过一个ServiceManager进行统一管理分配, SM 主要负责Server的注册,和Client的查找。 下图中:

  1. 灰色线 - 注册服务
  2. 紫色线 - 查询服务
  3. 蓝色+绿色线 - Client向Server端发起请求,并收到处理后的响应

binder1

传统的2次拷贝socket都是,用户空间发送方数据拷贝到内核空间,再内核空间拷贝到接受方的用户空间。

由于用户空间都是虚拟地址,实际会对应到物理内存的地址的~ 对于binder,会在接收方和内核之间做个映射,最终都指向同一个物理内存地址。 因此当发送方拷贝到内核缓存区后,接受方直接从该地址取数据即可,无需再次拷贝操作。

所以上图共享内存部分可能会有点误解,应该是映射表


具体流程

以上是总体流程,下面是具体

图片来源于 https://blog.csdn.net/freekiteyu/article/details/70082302

参考文章 http://gityuan.com/2015/11/07/binder-start-sm/ http://gityuan.com/2015/11/14/binder-add-service/

1. ServiceManager的启动

binder2 系统的init.rc调用service_manager.c的main函数入口

  1. binder_open 打开驱动并申请128k内存空间
    1. 系统调用open打开驱动,并创建全局链表binder_procs
    2. 系统调用mmap进行内存映射
      1. get_vm_area 分配一个连续的内核虚拟空间,大小和用户虚拟空间一致
      2. binder_update_page_range 分配物理空间,并将物理空间分别映射到内核和用户空间
  2. binder_become_context_manager 设为管家
  3. binder_loop 开始循环等待client请求

2.服务的注册添加(以MediaService为例)

binder3

  1. ProcessState.self 单例创建PS对象
    1. open_driver 打开Binder驱动
    2. mmap 给binder分配一块虚拟地址空间并处理映射,大小为 1M-8K(同SM启动中的mmap)
    3. 默认设置最大并发访问线程数未 16
  2. defaultServiceManager 获取BpServiceManager对象
  3. MediaService.instantiate 初始化注册所需服务
    1. new MediaService() 新建服务的对象
    2. BpServiceManager.addService(“media.player”, mediaService) 注册服务
      1. 创建数据包data,写入名字、service对象等信息
      2. BpBinder.transact(ADD_SERVICE_TRANSACTION, data)
        1. BpBinder是代理类?实际调用是交给 IPCThreadState.transact
  4. ProcessState -> startThreadPool 启动Binder线程池
  5. IPCThreadState:self -> joinThreadPool 当前线程加入到线程池
    1. self 初始化创建IPCTS对象,并分配线程本地储存空间 TLS

3.获取服务

  1. defaultServiceManager 获取BpSM对象
  2. BpSM.getService(“media.player”) 通过名字获取服务
    1. 和注册流程一样,也是通过代理最终调用 IPCThreadState.transact 传递消息
    2. 传递的是一个 CHECK_SERVICE_TRANSACTION 命令,就是验证获取服务的
  3. 每隔0.5s循环获取一次,可能还没注册启动完成