Binder 理论部分笔记整理

主要参考文章:

  1. Android Bander设计与实现 - 设计篇 墙裂推荐!!!
  2. https://github.com/GodCoder/Blog.me/issues/13
  3. Android进程间通信(IPC)机制Binder简要介绍和学习计划
  4. 《Android 开发艺术探索》—— IPC 机制 部分
  5. Android跨进程通信:图文详解 Binder机制 原理

补充:一份Binder机制深度面试问题

1. Binder 是什么?

Binder 是 Android 系统独有的进程间通信(IPC)方式之一。不同于 Linux 原有的 IPC 实现方式(Pipe、Signal、Socket、Share Memory)。

2. 优势

(1)性能:使用内存映射机制实现数据的传输只 copy 一次。
(详细讲解: Android Bander设计与实现 - 设计篇 - 6 Binder 内存映射和接收缓存区管理

Binder 借助了内存映射的方法,在内核空间和接收方用户空间的数据缓存区之间做了一层内存映射,就相当于直接拷贝到了接收方用户空间的数据缓存区,从而减少了一次数据拷贝。
参考自:Binder在通信时,为什么只需要一次拷贝?

结合设计与实现一文的第 6 点,简单的说就是由 Binder 驱动负责管理数据接收缓存,首先会基于内存映射,接收方的用户空间会对应着驱动内一块用于缓存接收方数据的内存空间(实际上就是内核空间),这块内存是由驱动来维护管理的,且不用接收方来提供,当驱动将发送方的数据拷贝到该内存之后,因为接收方基于内存映射机制,其空间就对应着内核中数据存储的空间,因此整体上来说是一次拷贝。

IPC 方式性能对比:
(1) 共享内存:虽然无需拷贝,但控制复杂,难以使用
(2) 消息队列、管道:采用存储-转发方式,即数据先从发送方缓存区拷贝到内核开辟的缓存区中,然后再从内核缓存区拷贝到接收方缓存区,至少有两次拷贝过程
(3) socket:作为一款通用接口,其传输效率低,开销大,主要用在跨网络的进程间通信和本机上进程间的低速通信

	进程空间是 Linux 基础知识,每一个进程的内存空间分为 User Space 和 Kernel Space,
	User Space 存放应用程序的代码和数据,是进程隔离的,但是 Kernel Space 存放内核代码和
	数据,是内核和所有进程共享的——这才是能真正做到 IPC 交互的根本原因

	正常情况下通过系统调用 copyFromUser 将 Server 的数据从 Server 进程的 User Space copy 
	到 Kernel Space, 处理后再由 Kernel Space copyToUser 到 Client 进程的 User Space,
	从而进行 IPC 数据交互。

	IPC 的本质是进程 User Space 和 Kernel Space 的数据交互或转换。

(2)安全:在内核空间添加身份标识(传统IPC没有,依赖上层协议控制),从而提供 API 可以区分 Caller uid, pid 进行包名、签名校验等;(校验方法除包名、签名外还可以加简单的 custom permission)

(3)易用:是封装的很好的 C/S 架构,是面向对象的思维和调用方式。AIDL 自动生成,屏蔽细节,整个 AIDL 目录放到 Client 下,然后再有 Proxy,相当于直接有一个 Server 的引用 Object,十分易于理解和方便调用,Client 调用 Proxy, Server 调用 Stub。比 Socket/Signal/Share Memory都简单。

2. 应用场景
  • Framework 中各种 Service 的交互,如 AMS/PMS 等;
  • 应用开发层之间的进程交互。
3. 工作过程及原理

该框架定义了四个角色:Server,Client,ServiceManager(以后简称 SMgr)以及 Binder 驱动。其中 Server,Client,SMgr 运行于用户空间,驱动运行于内核空间。

Binder 驱动:Binder 驱动是一个名词,和传统的 “驱动” 不同,与实际硬件设备无关,只不过其实现方式类似于驱动。本质上是基于 C++ 实现的代码,即 Linux 底层驱动,是 Server/Client/ServiceManager 通信的桥梁,通过内存映射机制在内核实现 IPC,持有 Server 在 Kernel Space 的 Binder 实体,负责进程之间 Binder 通信的建立,Binder 在进程之间的传递(给 Client 提供 Binder 实体的引用,即我们说的 Remote Object),Binder 引用计数管理,数据包在进程之间的传递和交互等一系列底层支持。且驱动和应用程序之间定义了一套接口协议。

ServiceManager:将字符形式的 Binder 名字转化成 Client 中对该 Binder 的引用,使得 Client 能够通过 Binder 名字获得对 Server 中 Binder 实体的引用,是 Binder Driver 和 Server/Client 之间的上下文管理者,负责 Service 的注册、查询。

在这里插入图片描述

在这里插入图片描述

一个 Binder 实体可以发送给其它进程从而建立许多跨进程的引用;另外这些引用也可以在进程之间传递,就象 java 里将一个引用赋给另一个引用一样。为 Binder 在不同进程中建立引用必须有驱动的参与,由驱动在内核创建并注册相关的数据结构(该数据结构即为内核中的 Binder 实体)后接收方才能使用该引用。

简单的说,Binder 跨进程传输并不是真的把一个对象传输到了另外一个进程,只不过是 Client 端通过 SMgr 获得在 Server 中目标 Binder 的“引用”—— Binder 的代理对象(这是一个新的对象),然后进行操作(即调用代理对象的方法),然后这一“操作”会通过以 指令+数据 的形式通过驱动传递给 Server 端真正的 Binder 实体,使 Binder 解析并实现这一“操作”,对应的从真正意义上调用目标方法,之后将结果再利用驱动再返回给 Client 端。


4. Binder 在系统各部分中的表述

即 BInder 在 Client、Server、驱动以及数据传输时的存在形式。
请参考 Android Bander设计与实现 - 设计篇 第 5 点。


5. Binder 线程池

请参考 Android Bander设计与实现 - 设计篇 第 7 点。

基于 Android:IPC之AIDL的学习和总结 这篇文章进行 AIDL 的实践,并在部分示例代码中添加了一些打印日志内容。

public class BookManagerService extends Service {
    //支持并发读写
    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();

    //服务端定义Binder类(IBookManager.Stub)
    private Binder mBinder = new IBookManager.Stub() {
        int count = 0;

        // 在 zzq_process 进程的主线程中调用
        @Override
        public List<Book> getBookList() {
            Log.d("test", "-------- start --------");
            Log.d("test", "currentThread =" + Thread.currentThread());
            Log.d("test", "-------- end --------");
            return mBookList;
        }

        @Override
        public void addBook(Book book) {
            count++;
            mBookList.add(book);
        }
    };

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        // 每次都是返回的同一个 binder 实例,即 mBinder,
        // 此时如果有多个客户端同时引用 mBinder 的方法,那么就会引起冲突,
        // 因此就有 Binder 线程池的存在,通过线程池,就可以实现并发处理
        // 但需要注意的是,在线程池的不同线程中调用的都是同一个 mBinder,
        // 其内部的状态共享,如 count 的值,
        // 只不过是同时调用 mBinder 的方法(方法的本质就是一段指令,达到复用的目的),
        // 因为该方法里面每次都是返回的同一个 binder 实例;
        // 当然,也可以每次都返回一个新的实例,此时线程池依旧存在,因为这是一种通用设计
        // 并不会因为客户端引用的 binder 实例不同而有所区别
        return mBinder;
    }
}

之后在客户端调用的时候,每次调用,日志打印的线程的内容可能不一样:

//第一次打印内容:
currentThread =Thread[Binder:16647_2,5,main]

//第二次打印内容
currentThread =Thread[Binder:16647_3,5,main]

这就间接证明了 Binder 线程池的存在,因为对于 Server 进程 S,可能会有许多 Client 同时发起请求调用同一个 Server 中的 Binder 实体,为了提高效率往往开辟线程池并发处理收到的请求。

另外,对于服务端的进程来说,(在 Android 中)也会有主线程以及 ANR 的概念,因此在主线程中同样不能做耗时的操作,如远程 Service 的 onCreate 就是运行在主线程的。 其实,可以看作是一个新的 App 进程。


另外,补充一个有关于 Binder 的点,在 Android中Parcel的解读 中有介绍到:

Parcel 还包含了一个活动的 IBinder 对象的引用,这个引用导致另一端接收到一个指向这个 IBinder 的代理 IBinder

以及:

Parcel 的一个非同寻常的特性是读写活对象的能力。对于活动对象,它们的内容实际上并没有写入,而是仅写入了一个令牌来引用这个对象。当从 Parcel 中读取这个对象时,你不会获取一个新的对象实例,而是直接得到那个写入的对象。有两种活动对象可操作:Binder对象、FileDescriptor对象。

这里需要注意,即 Parcel 在传递 IBinder 对象的时候,本质上是传递一个 IBinder 对象的引用(仅写入了一个令牌来引用这个对象),而不是得到一个新的对象实例。这样在 Binder 机制中,就可以实现在本地进程中利用本地 Binder 对象来间接引用远程服务中的 Binder 本体。

千里马8年Android系统及应用开发经验,曾担任过美国unokiwi公司移动端技术总监兼架构师,对系统开发,性能优化,应用高级开发有深入的研究,Android开源定制ROM Lineage的贡献者之一,国内首家线下开辟培训Android Framework课程,拥有2年的Android系统培训经验。成为腾讯课堂专业负责android framework课程分享第一人,致力于提高国内android Framework水平Android Framework领域内是国内各大手机终端科技公司需要的人才,应用开发者都对Android系统充满着好奇,其中的binder是重中之重,都说无binder无Android,binde是Android系统的任督二脉。课程水平循序渐进,由中级再到高级,满足各个层次水平的android开发者。1、灵活使用binder跨进程通信,在app端对它的任何api方法等使用自如2、可以单独分析android系统源码中任何binder部分,分析再也没有难度3、掌握binder驱动本质原理,及对应binder驱动怎么进行跨进程通信,及内存等拷贝方式数据等4、对binder从上层的java app端一直到最底层的内核binder驱动,都可以顺利理通5、针对系统开发过程中遇到的binder报错等分析方法,及binder bug案例学习6、针对面试官任何的binder问题都可以对答自如7、socket这种跨进程通信实战使用8、针对android源码中使用的socket源码轻松掌握9、android系统源码中最常见的socketpair中双向跨进程通信10、使用socket实现一个可以让app执行shell命令的程序
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值