1. 动态内核可加载模块 && 内存映射

正如上一章所说, 跨进程通信是需要内核空间做支持的. 传统的 IPC 机制如 管道, Socket, 都是内核的一部分, 因此通过内核支持来实现进程间通信自然是没问题的.

但是 Binder 并不是 Linux 系统内核的一部分, 那怎么办呢, 这得益于 Linux 的动态内核可加载模块 (Loadable Kernel Module, LKM)的机制

1.1 动态内核可加载模块 (Loadable Kernel Module, LKM)

动态内核可加载模块 (Loadable Kernel Module, LKM)
 
模块是具有独立功能的程序, 它可以被单独编译, 但是不能独立运行. 它在运行时被链接到内核作为内核的一部分运行.

这样 Android 系统就可以通过动态添加一个内核模块运行在内核空间, 用户进程进程之间通过这个内核模块作为桥梁来实现通信.

在 Android 中, 这个运行在内核空间, 负责各个用户进程通过 Binder 实现通信的内核模块就叫做 Binder 驱动 (Binder Driver)

那么在 Android 系统中用户进程之间是如何通过这个内核模块 (Binder Driver)来实现通信的呢? 显然不是和上一章的传统 IPC 通信一样,进行两次 copy 了, 不然Binder 也不有在性能方面的优势了.

1.2 内存映射

Binder IPC 机制中设计到的内存映射通过 mmap() 来实现, mmap() 是操作系统中一种内存映射的方法.

内存映射
 
简单的说就是将用户空间的一块内存区域映射到内核空间.
映射关系建立后, 用户对这块内存区域的修改可以直接反应到内核空间.
反之,内核空间对这段区域的修改也能直接反应到用户空间.

内存映射能减少数据 copy 的次数, 实现用户空间和内核空间的高效互动. 两个空间各自的修改也能直接反应在映射的内存区域, 从而被对方空间及时感知. 也正因为如此, 内存映射能够提供对进程间通信的支持.


2. Binder IPC 实现原理

Binder IPC 正是基于内存映射(mmap()) 来实现的, 但是mmap() 通常是用在有物理介质的文件系统上的.

比如进程中的用户区域是不能直接和物理设备打交道的, 如果想要把磁盘上的数据读取到进程的用户区域, 需要两次 copy (磁盘 -> 内核空间 -> 用户空间). 通常在这种场景下 mmap() 就能发挥作用, 通过在物理介质和用户空间之间建立映射, 减少数据的 copy 次数, 用内存读写代替 I/O 读写, 提高文件读取效率.

而 Binder 并不存在物理介质, 因此 Binder 驱动使用 mmap() 并不是为了在物理介质和用户空间之间映射, 而是用来在内核空间创建数据接收的缓存空间.

一次完整的 Binder IPC 通信过程通常是这样:

  1. 首先 Binder 驱动在内核空间创建一个数据接收缓存区
  2. 接着在内核空间开辟一块内核缓存区.
  3. 建立内核缓存区 和 内核中数据接收缓存区 之间的映射关系.
  4. 建立内核中数据接收缓存区 与 数据接收进程用户空间 的地址的映射关系
  5. 发送方进程通过系统调用 copy_from_user() 将数据 copy 到内核中的内核缓存区, 由于内核缓存区 与 内核数据接收缓存区 存在映射关系, 而内核数据接收缓存区 又与 数据接收进程用户空间 存在映射关系, 所以相当于 内核缓存区 与 数据接收进程用户空间存在映射关系. 因此也就相当于把数据发送到了数据接收进程的用户空间.

这样就完成了一次进程间通信
如下图:


3. Binder 通信模型

介绍完 Binder IPC 的底层通信原理, 接下来我们看看实现层面是如何设计的

一次完成的进程间通信必然至少包含两个进程, 通常我们称通信的双方分别为客户端进程(Client) 和服务端进程(Server), 由于进程隔离机制的存在, 通信双方必然需要借助 Binder 来实现.

3.1 Client/Server/ServiceManager/Binder驱动

BInder 是基于 C/S 架构. 是由一些列组件组成. 包括 Client, Server, ServiceManager, Binder 驱动.

  • Client, Server, ServiceManager 运行在用户空间
  • Binder 驱动运行在内核空间.
  • ServiceManager, Binder 驱动由系统提供.
  • Client, Service 由应用程序来实现

Client, Server, ServiceManager 都是通过系统调用 open, mmap,ioctl 来访问设备文件 /dev/binder, 从而实现与 Binder 驱动的交互来间接的实现跨进程通信.

3.1.1 Binder 驱动

Binder 驱动就如如同路由器一样, 是整个通信的核心. 驱动负责进程之间 Binder 通信的建立 / 传递, Binder 引用计数管理, 数据包在进程之间的传递和交互等一系列底层支持.

3.1.2 ServiceManager 与 实名 Binder

ServiceManager 作用是将字符形式的 Binder 名字转化成 Client 中对该 Binder 的引用, 使得 Client 能够通过 Binder 的名字获得对 Binder 实体的引用.

注册了名字的 Binder 叫实名 Binder, 就像网站一样除了 IP 地址以外还有自己的网址.
Server 创建了 Binder, 并为它起一个字符形式, 可读易记的名字, 将这个 BInder 实体连同名字一起以数据包的形式通过 Binder 驱动 发送给 ServiceManager, 通知 ServiceManager 注册一个名字为 "张三"的 Binder, 它位于某个 Server 中, 驱动为这个穿越进程边界的 BInder 创建位于内核中的实体节点以及 ServiceManager 对实体的引用, 将名字以及新建的引用打包传给 ServiceManager, ServiceManager 收到数据后从中取出名字和引用填入查找表.

ServiceManager 是一个进程, Server 又是一个另外的进程, Server 向 ServiceManager 中注册 BInder 必然涉及到进程间通信. 当实现进程间通信又要用到进程间通信, 这就好像蛋可以孵出鸡的前提确实要先找只鸡下蛋! Binder 的实现比较巧妙, 就是预先创造一只鸡来下蛋. ServiceManager 和其他进程同样采用 Binder 通信, ServiceManager 是 Server 端, 有自己的 Binder 实体, 其他进程都是 Client, 需要通过这个 Binder 的引用来实现 Binder 的注册, 查询和获取. ServiceManager 提供的 Binder 比较特殊, 它没有名字也不需要注册. 当一个进程使用 BINDERSETCONTEXT_MGR 命令将自己注册成 ServiceManager 时 Binder 驱动会自动为它创建 Binder 实体(这就是那只预先造好的那只鸡). 其实这个 Binder 实体的引用在所有 Client 中都固定为 0 , 而无需通过其他手段获得. 也就是说, 一个 Server 想要向 ServiceManager 注册自己的 Binder 就必须通过这个 0 号引用和 ServiceManager 的 Binder 通信. 这里说的 Client 是相对于 ServiceManager 而言的, 一个进程或者应用程序可能是提供服务的 Server, 但是对于 ServiceManager 来说它仍然是个 Client.

Server 向 ServiceManager 中注册了 Binder 以后, Client 就能通过名字获得 Binder 的引用. Client 也利用保留的 0 号引用向 ServiceManager 请求访问某个 Binder. 比如,Client 申请访问名字叫"张三"的 Binder 引用. ServiceManager 收到这个请求后从请求数据包中取出 Binder 名称, 在查找表里找到对应的条目, 取出对应的 Binder 引用, 作为回复发送给发起请求的 Client. 从面相对象的角度看, Server 中的 Binder 实体现在有两个引用: 一个位于 ServiceManager 中, 一个位于发起请求的 Client 中. 如果后面会有更多的 Client 请求该 Binder, 系统中就会有更多的引用指向这个 Binder, 就像 Java 中一个对象有多个引用一样.


4. Binder 通信中的代理模式

我们已经解释清楚 Client, Server 借助 Binder 驱动完成跨进程通信的实现机制了, 但是还有个问题需要弄清楚, 比如 A 进程想要 B 进程中的某个对象(object) 是如何实现的呢, 毕竟它们属于不同的进程, A 进程没办法直接使用 B 进程中的 object.

前面我们说过跨进程通信的过程都有 Binder 驱动的参与, 因此在数据流经 Binder 驱动的时候 Binder 驱动会对数据做一层转换.

我们在 Client端,向 ServiceManager 获取具体的 Server 端的 Binder 引用的时候,会首先进过 Binder 驱动,Binder 驱动它并不会把真正的 Server 的 Binder 引用返回给 Client 端,而是返回一个代理的 java 对象,该对象具有跟 Server 端的 Binder 引用相同的方法签名,这个对象为 ProxyObject,他具有跟 Server 的 Binder 实例一样的方法,只是这些方法并没有 Server 端的能力,这些方法只需要把请求参数交给 Binder 驱动即可. 对于 Client 端来说和直接调用 Server 中的方法是一样的.


5. Binder 通信过程

了解了上面之后, 我们大致可以推算出 Binder 的通信过程

1. 注册 ServiceManager

一个进程使用 BINDERSETCONTEXT_MGR 命令通过 Binder 驱动将自己注册成为 ServiceManager.

2. 注册 Server

Server 通过 Binder 驱动向 ServiceManager 中注册 Binder (Server 中 Binder 实体), 表明可以对外提供服务.
Binder 驱动为这个 Binder 创建位于内核中的实体节点以及 ServiceManager 对实体的引用, 然后将名字及新建的引用打包传给 ServiceManager, 最后 ServiceManager 将其填入到查找表.

3. Client 获取 Server 的 Binder 引用

Client 请求获得 Server 的 Binder 引用, Binder 驱动在接收到 Client 的请求的时候, 就去 ServiceManager 中查询, 查询到后会创建一个 Server 端 Binder 对象的 ProxyObject (Binder 的代理对象), 并将该 ProxyObject 返回给 Client.

4. Client 与 Server 通信

Client 收到 Binder 驱动返回的 ProxyObject 后, 通过 ProxyObject 调用其中的方法. ProxyObject 的方法再去调用 Binder 驱动, Binder 驱动会去查询自己维护的表单, 发现这是某个 Server 端 Binder 的代理对象, 就会通知 Server 端调用具体的方法,(会把 Client 端调用 ProxyObject 中方法的参数传过去. ), 并要求 Server 把结果返回给自己. 当 Binder 驱动拿到结果后, 就会转发给 Client 端.


6. Binder 的完整定义

  • 从进程间通信的角度看, Binder 是一种进程间通信的机制
  • 从 Server 进程的角度看, Binder 指的是 Server 中的 Binder 实体对象.
  • 从 Client 进程的角度看, Binder 指的是对 Binder 代理对象, 是 Binder 实体对象的一个远程代理.
  • 从传输过程的角度看, Binder 是一个可以跨进程通信的对象. Binder 驱动会对这个跨越进程边界的对象

Linux的IPC机制(三):Binder相关推荐

  1. Linux 进程间通信-IPC 机制

    Linux 平台上在 Kernel 协调下完成进程之间的相互通信,有多种进程间通信 -- Inter Process Communication(IPC)方式. 1. IPC 分类 按照功能用途来看有 ...

  2. linux各种IPC机制

    原帖发表在IBM的developerworks网站上,是一个系列的文章,作者郑彦兴,通过讲解和例子演示了Linux中几种IPC的使用方式,我觉得很好,在这里做一个保留,能看完的话Linux IPC的基 ...

  3. Linux的IPC机制(二):Socket/管道/消息队伍/信号量

    每个进程的用户地址空间都是独立的,一般而言是不能互相访问的,但内核空间是每个进程都共享的,所以进程之间要通信必须通过内核. Linux 内核提供了不少进程间通信的机制,我们来一起瞧瞧有哪些? 管道 如 ...

  4. Linux的IPC机制(一):共享内存

    0. 共享内存 比喻 本质 多个进程访问同一个逻辑内存 直接访问内存,不用read()/write()非常方便 1. POSIX 共享内存 资料:unpv22e-ch13 查看: man shm_ov ...

  5. Binder相关面试总结(一):为什么Android要采用Binder作为IPC机制?

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Nwy9SoNo-1609925310525)(//upload-images.jianshu.io/upload_ima ...

  6. 为什么Android要采用Binder作为IPC机制?

    作者:Gityuan 链接:https://www.zhihu.com/question/39440766/answer/89210950 来源:知乎 著作权归作者所有,转载请联系作者获得授权. 在开 ...

  7. Linux内核IPC命名空间 (一)(用户层信号量机制实现)

    Linux内核IPC机制 在Linux内核中IPC全名称之为进程间通信机制,共分为多种,如下: 信号量 共享内存 消息队列 管道 信号 套接字通信 每一种实现都有其独特特性,一下博客中我们会讲解其中的 ...

  8. Android Binder IPC机制

    之前有很多人问过我TaintDroid的内容,所以一点点吧涉及的思路整理一下. 今天又看了一遍,确实需要很多的技术支持以及扎实的功底,不管是Java的还是Android,从系统底层一直到顶层,涉及的知 ...

  9. Android的IPC机制Binder

    第一部分 Binder的组成  1.1 驱动程序部分驱动程序的部分在以下的文件夹中: Java代码  kernel/include/linux/binder.h kernel/drivers/andr ...

最新文章

  1. 【linux】ubuntu14.04升级dbus到1.13.8,杯具了,无法进入桌面
  2. Mysql,再见吧!select * !
  3. 下划线转驼峰,并且首字母大写
  4. python怎么学最快-浅谈:从为什么学习python到如何学好python
  5. 记录一次react项目配置过程
  6. 论文浅尝 | Wordly Wise(WoW) - 用于语音视觉知识问答的跨语言知识融合模型
  7. SpringCloud工作笔记040--- XMLHTTP中setRequestHeader()方法解析
  8. 谷雨钜献 | 用深度学习理解遥感图像,识别效率提升90倍,PaddlePaddle中科院遥感地球所...
  9. 解决问题:UserWarning: Matplotlib is currently using agg, which is a non-GUI backend.
  10. php-cs-fixer不起作用,使用 PHP-CS-Fixer 规范PHP代码
  11. 机械设计专业课程C语言,c语言项目总结ppt[工作范文](20页)-原创力文档
  12. 海湾5000汉字编码app
  13. pymysql操作MySQL数据库表-----爬虫豆瓣top250电影并存入数据库
  14. 有哪些简洁的人生建议?
  15. 【宏定义】#define 的使用方法
  16. 算力网络发展中的若干关键技术问题分析
  17. 云宏超融合+英特尔® =?
  18. Vultr 教程目录
  19. 用公式提取多个Excel文件的数据
  20. 复杂场景下的权限系统该怎么玩?ABAC权限模型帮你搞定它!

热门文章

  1. 如何将风险应用加入白名单_将微信服务器、API接口的IP列表加入宝塔防火墙IP白名单...
  2. oracle 越南字符,ORACLE 12.2RAC之问题 ora.chad OFFLINE
  3. arduino项目_quot;Arduino智能车项目quot;实战班开班!
  4. python读取大文件csv内存溢出_Python,内存错误,csv文件太大
  5. HALCON基于灰度值的模板匹配
  6. pointnet 结果可视化_PointNet论文复现及代码详解
  7. JavaScript获取浏览器的显示区域大小信息
  8. 如何使用canvas绘图
  9. 《敏捷软件开发:原则、模式与实践(C#版.修订版)》—第1章1.4节参考文献
  10. Mybatis XML文件的异常