跳至主要內容

IPC Binder 之杂谈

Someone大约 6 分钟AndroidkernelAndroidBinder

Abstract

进程间通信(IPC) 在系统中非常重要,目前 Android 的 binder 方案已经属于行业内非常优秀的实践案例;但是作为软件从业人员,如果仅仅满足于现有架构或者技术的优势,这是远远不够的。所以本文有几个重要的目的:

  1. 第一是从对比、实践、设计的角度去看看 binder 有什么缺陷和改进点
  2. 第二是研究一下业界最新的技术是如何优化 IPC 的,或者说有没有什么新的思路借鉴?
  3. 第三是一些杂谈

Binder 优缺点

优点

对于 binder 具体的分析可以看我的另一篇博文《Research on Binder》,在此对具体的技术细节就不进行赘述,总的来看,binder 的优点包括以下几个:

第一,binder 只需要进行一次内存拷贝;

第二,binder 的使用非常简单(在上层 service 看来,只需要一个客户端、一个服务端,C/S 架构,超级简单)

第三,binder 很安全。binder 没有打破进程隔离的属性,并且在上层的 C/S 之间也有丰富的鉴权机制

最后,个人认为最重要的:Google 开发 binder 已经有很多年了,期间做了无数精妙的优化(这句话是想说,一个项目有很多优秀的从业者持续优化)

主要就是因为这几个优点,导致了 binder 在 Android 中的地位不可撼动。

缺点

但是随着对 binder 技术的稍微深入,我们发现,binder 的设计本质上是一个 trade-off: 牺牲了一部分性能,换取了安全和架构上的简洁,这也就是我们要说的 binder 的缺点:

第一,性能不是最优。虽然 binder 只需要一次拷贝,但是就是因为这次拷贝,使得 binder 的性能还存在 overhead, 具体来说:能不能避免这次拷贝?肯定能!但是为什么 Google 没做?我们上面说过,做了取舍;

第二,通信的信息大小存在限制。从 binder 的实现来看,因为使用了 mmap, 所以要在 kernel space 和 user space 之间做映射,这意味着需要在内核的地址空间占用临时映射区的空间,一个进程占一些,那进程多了,映射空间不够用怎么办?所以说 Android 严格限制了一个进程可以映射的内存大小,来规避这个问题;这也就是 Binder 的第二个缺点;

除此之外,Binder 本身比较复杂,如果需要 debug, 这会是一场灾难(当然,所有的进程间通信都是);

还有一点,吹毛求疵一下:几乎无可扩展性;为 Android 专门设计的,想迁移到诸如微内核等上面?不存在的。

其他选择:dIPC

dIPC 是一篇论文的实现:《Direct Inter-Process Communication (dIPC): Repurposing the CODOMs Architecture to Accelerate IPC》,这篇论文的研究可以看我的另一篇文章。

简单来说,这篇文章为了解决 IPC(或者 Binder)性能问题,将需要进行进程间通信的进程分配到了一个共享页表中,然后使用自定义的隔离算法来保证安全性。

dIPC 设计理念

dIPC 的设计第一次让人看到时会有比较惊艳的感觉:我在此引用作者的 Abstract 中的一个内容:

In current architectures, page tables are the fundamental mechanism that allows contemporary OSs to isolate user processes, binding each thread to a specific page table.

作者刚开始就对现有的页表机制做了抨击!做创新、做学术就应该如此,敢于挑战权威和质疑现有的所有实现,这样才是进步之道。不管作者如何实现,这都为我们提供了一个新的思路:打破页表的限制!

那么作者是如何做的呢?作者复用了他之前的一个研究:CODOMs, 这个研究主要针对于单个进程之间的组件隔离,将多个组件按照页表,区分为多个域,每个域之间确定隔离策略。比如说可以进行通信和数据交互的域,我给配置读写权限;如果没有关系的域,就配置隔离策略。需要注意,这不是一个纯粹的软件策略,从页表上动手,意味着我们硬件也可以出手了,这就意味着:软硬件协同的机会来了!

// some image

我们截取论文中的一张图片进行简单介绍:在 dIPC 中,当两个进程要进行通信的时候,kernel 会负责创建(或者说自动生成)一个 proxy 对象,这个 proxy 对象拥有对客户端的读权限和服务端的写权限,这是在用户态可以配置的;如此一来就在不用拷贝数据到内核地址空间的情况下完成了进程间通信。

dIPC 优点

dIPC 优点十分明显,Binder 中令人诟病的一次拷贝没有了!这肯定会带来巨大的性能提升。除此之外,dIPC 的通信是不需要进过 kernel 的,这也就意味着不需要陷入内核,不需要系统调用等!事实上,系统调用的时间在轻量级通信中的占比还是很大的。

还有一个优点是论文作者引以为傲的:做了一个很完善的隔离策略,安全并且灵活。我们在此不进行赘述了,这也是作者的工作量所在。

dIPC 缺点

既然说作者做出了一个安全策略,那么是否真的安全?安全度达标吗?这个是要持有疑问的;

除此之外,这个算法的实现还是比较复杂的(相比于 Binder,其实简单太多了,但是难就难在,说服别人接受你这个技术,并且应用起来,是一件很困难的事情);

还有一个不利因素:需要硬件配合。没有较好的软硬件协同能力,这事情就会很难。(🧨🧨这点我不是非常确定,从理论上来说,修改页表只需操作系统介入即可);

还有一点就是 CODOMs 为了解决获取隔离权限时候的查表操作,做了一个硬件 cache, 方便快速查权限;这也是一个较为损耗面积的修改。

Axx

该技术相比 binder 是更加高效,主要是解决了 binder 线程调度造成的性能瓶颈;但是相比于 dIPC, 并没有避免一次拷贝操作。(在此不详细展开)

总结

我们初步研究分析了一下三种进程间通信的方式,本文行为比较随性,较为学术的文章可以参考具体技术的架构和代码分析,如有不到之处,还请各位大佬不吝赐教。