面试必备:Binder进程通信原理

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面试必备:Binder进程通信原理相关的知识,希望对你有一定的参考价值。

参考技术A

先简单概括性地说说Linux现有的所有进程间IPC方式:
管道(Pipe): 在创建时分配一个page大小的内存,缓存区大小比较有限;
消息队列(Message): 信息复制两次,额外的CPU消耗;不适合频繁或信息量大的通信;
共享内存(Share Memory): 无须复制,共享缓冲区直接附加到进程虚拟地址空间,速度快;但进程间的同步问题操作系统无法实现,必须各进程利用同步工具解决。
套接字(Socket): 作为更通用的接口,传输效率低,主要用于不同机器或跨网络的通信。
信号量(Semaphore): 常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此主要作为进程间以及同一进程内不同线程之间的同步手段。
信号(Signal): 不适用于信息交换,更适用于进程中断控制,比如非法内存访问、杀死某个进程等。

从4个角度来展开对Binder的分析
(1)从性能的角度
数据拷贝次数: Binder数据拷贝只需要一次,而管道、消息队列、套接字都需要2次,但共享内存方式一次内存拷贝都不需要;从性能角度看,Binder性能仅次于共享内存。
(2)从稳定性的角度
    Binder是基于C/S架构的,架构清晰明朗,Server端和Client端相对独立,稳定性较好;而共享内存实现方式复杂,没有Client端与Server端之别,需要充分考虑到访问临界资源的并发同步问题,否则可能会出现死锁等问题;从稳定性角度看,Binder架构优越于共享内存。
(3)从安全的角度
    传统Linux IPC的接收方无法获得对方进程可靠的UID/PID,从而无法鉴别对方身份;而android作为一个开放的开源体系,手机安全显得额外重要,传统Linux IPC五任何保护措施,完全由上层协议来确保。
    Android为每个安装好的应用程序分配了自己的UID,故进程的UID是鉴别进程身份的重要标志。前面提到C/S架构, Android系统中对外只暴露Client端,Client端将任务发送给Server端,Server端会根据权限控制策略,判断UID/PID是否满足访问权限,目前权限控制很多时候是通过弹出权限询问对话框,让用户选择是否运行。
    传统IPC只能由用户在数据包里填入UID/PID;另外,可靠的身份标记只有由IPC机制本身在内核中添加。其次传统IPC访问接入点是开发的,无法建立私有通道。从安全角度,Binder的安全性更高。
(4)从语言层面的角度
    大家都知道Linux是基于C语言(面向过程的语言),而Android是基于Java语言(面向对象的语句)。Binder恰恰符合面向对象思想,将进程间通信转化为对某个Binder对象的引用,调用该对象的方法。而其独特之处在于Binder对象是一个可以跨进程引用的对象,它的实体位于一个进程,而它的引用却遍布于系统的各个进程之中。Binder模糊了进程边界,淡化了进程间通信过程,让整个系统仿佛运行于同一个面向对象的程序之中。

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

ServiceManager与实名Binder

Client获得实名Binder的引用
    Server向SM注册了Binder实体及其名字后,Client就可以通过名字获得该Binder的引用了。

匿名Binder
    并不是所有Binder都需要注册给SM广而告之的。Server端可以通过已经建立的Binder连接将创建的Binder实体传给Client,当然这条已经建立的Binder连接必须是通过实名Binder实现。由于这个Binder没有向SM注册名字,所以是个匿名Binder。Client将会收到这个匿名Binder的引用,通过这个引用向位于Server中的实体发送请求。匿名Binder为通信双方建立一条私密通道,只要Server没有把匿名Binder发给别的进程,别的进程就无法通过穷举或猜测等任何方式获得该Binder的引用,向该Binder发送请求。

     传统IPC方式中,数据是怎样从发送端到达接收端的呢? 通常的做法是: 发送方 将准备好的数据存放在缓存区中,通过系统API调用进入 内核 中。 内核服务程序 在内核空间分配内存,将数据从 发送方 缓存区复制到内核缓存区中。 接收方 读数据是也要提供一块缓存区, 内核 将数据从内核缓存区拷贝到 接收方 提供的缓存区中并唤醒接收线程,完成一次数据发送。这种存储-转发机制有两个缺陷:首先是效率低下,需要做两次拷贝(用户空间->内核空间->用户空间)。Linux使用copy_from_user()和copy_to_user()实现这两个跨空间拷贝。其次是接收数据的缓存要由接收方提供,可接收方不知道到底要多大的缓存才够用。只能开辟尽量打的空间或先调用API接收消息头获得消息体大小,再开辟适当的空间接收消息体。两种做法都有不足,不是浪费空间就是浪费时间。
     Binder采用一种全新策略:由Binder驱动负责管理数据接收缓存。 Binder驱动通过实现mmap()来创建数据接收的缓存空间。

    这样Binder的接收方就有了一片大小为MAP_SIZE的接收缓存区。mmap()的返回值是内存映射在用户空间的地址,不过这段空间是由驱动管理,用户不必直接访问(映射类型为PROT_READ,只读映射)。

参考文献
Android Binder机制原理(史上最强理解,没有之一)

以上是关于面试必备:Binder进程通信原理的主要内容,如果未能解决你的问题,请参考以下文章

2022金三银四Android面试必备知识点:Android AMS/WMS/Binder/Hander

10.3android输入系统_必备Linux编程知识_任意进程双向通信(scoketpair+binder)

面试必备考题

转载腾讯面试题——谈一谈Binder的原理和实现一次拷贝的流程

AndroidBAT高级面试合集——Binder 通信原理与机制

AndroidBAT高级面试合集——Binder 通信原理与机制