- 内核程序将内核缓存区中通过 copy_to_user 函数将数据拷贝到数据接收方进程的内存缓存区
通过以上过程,一次 IPC 就完成了,但是这种传统的 IPC 机制有两个问题:
-
性能比较低:整个过程数据的传递需要经历发送方内存缓存区——内核缓存区——接收方内存缓存区的过程
-
接收方进程事先不知道需要开辟多大的内存用于存放数据,因此需要开辟尽可能大的空间或者事先调用 API 来解决这个问题,这两种方式不是浪费空间就是浪费时间。
Binder IPC 原理
为了克服 Linux 传统的 IPC 机制中的不足之处,Android 系统引入了 Binder 机制,从字面上看 Binder 是胶水的意思,在这里,Binder 的职责是在不同的进程之间扮演一个桥梁的角色,让它们之间能够相互通信。从上一小节内容可以了解到,进程间的通信少不了 Linux 内核的支持,而 Binder 并不属于内核的一部分,但是,得益于 Linux 的 LKM(Loadable Kernel Module) 机制:
模块是具有独立功能的程序,它可以被单独编译,但不能独立运行。它在运行时被链接到内核作为内核的一部分在内核空间运行
因此,Binder 作为这种模块存在于内核之中,也称为 Binder 驱动。回顾上一小节的内容,传统 Linux IPC 的过程需要经历两次数据拷贝,Binder 借助 Linux 的另一个特性,只用一次数据拷贝,就能实现 IPC 过程,这就是内存映射:
Binder IPC 机制中涉及到的内存映射通过 mmap() 来实现,mmap() 是操作系统中一种内存映射的方法。内存映射简单的讲就是将用户空间的一块内存区域映射到内核空间,映射关系建立后,用户对这块内存区域的修改可以直接反应到内核空间;反之内核空间对这段区域的修改也能直接反应到用户空间。
内存映射能减少数据拷贝次数,实现用户空间和内核空间的高效互动。两个空间各自的修改能直接反映在映射的内存区域,从而被对方空间及时感知。也正因为如此,内存映射能够提供对进程间通信的支持。
Binder IPC 通信过程如下:
-
Binder 驱动在内核空间创建一个数据接收缓存区
-
然后在内核空间开辟一块内存缓存区并与数据接收缓存区建立映射关系,同时,建立数据接收缓存区与数据接收方的内存缓存区的映射关系
-
数据发送方通过系统调用 copy_from_user 函数将数据从内存缓存区拷贝到内核缓存区,由于内核缓存区通过数据接收缓存区跟数据接收方的内存缓存区存在间接的映射关系,相当于将数据直接拷贝到了接收方的用户空间,这样便完成了一次 IPC 的过程。
Binder 通信模型和通信过程
在进行 Binder IPC 的时候,实际情况比上面介绍的要复杂,Binder 通信模型是基于 C/S 架构的,通信调用方进程称为 Client 进程,被调用方称为 Server 进程,除此之外还需要 ServiceManager 和 Binder 驱动的参与,它们都是通过 open/mmap/iotl 等系统调用来访问设备文件 dev/binder 来实现 IPC 过程的。
其中,Client、Server 和 ServiceManager 运行在用户空间,Binder Driver 运行在内核空间,Client 和 Server 需由用户自己实现,ServiceManager 和 Binder Driver 则由系统提供。
Android Binder 设计与实现 文章中对 Client 和 Server 等角色有详细的描述:
**Binder 驱动:**Binder 驱动就如同路由器一样,是整个通信的核心;驱动负责进程之间 Binder 通信的建立,Binder 在进程之间的传递,Binder 引用计数管理,数据包在进程之间的传递和交互等一系列底层支持。
**ServiceManager 与实名 Binder:**ServiceManager 和 DNS 类似,作用是将字符形式的 Binder 名字转化成 Client 中对该 Binder 的引用,使得 Client 能够通过 Binder 的名字获得对 Binder 实体的引用。注册了名字的 Binder 叫实名 Binder,就像网站一样除了除了有 IP 地址意外还有自己的网址。Server 创建了 Binder,并为它起一个字符形式,可读易记得名字,将这个 Binder 实体连同名字一起以数据包的形式通过 Binder 驱动发送给 ServiceManager ,通知 ServiceManager 注册一个名为“张三”的 Binder,它位于某个 Server 中。驱动为这个穿越进程边界的 Binder 创建位于内核中的实体节点以及 ServiceManager 对实体的引用,将名字以及新建的引用打包传给 ServiceManager。ServiceManger 收到数据后从中取出名字和引用填入查找表。
细心的读者可能会发现,ServierManager 是一个进程,Server 是另一个进程,Server 向 ServiceManager 中注册 Binder 必然涉及到进程间通信。当前实现进程间通信又要用到进程间通信,这就好像蛋可以孵出鸡的前提却是要先找只鸡下蛋!Binder 的实现比较巧妙,就是预先创造一只鸡来下蛋。ServiceManager 和其他进程同样采用 Bidner 通信,ServiceManager 是 Server 端,有自己的 Binder 实体,其他进程都是 Client,需要通过这个 Binder 的引用来实现 Binder 的注册,查询和获取。ServiceManager 提供的 Binder 比较特殊,它没有名字也不需要注册。当一个进程使用 BINDERSETCONTEXT_MGR 命令将自己注册成 ServiceManager 时 Binder 驱动会自动为它创建 Binder 实体(这就是那只预先造好的那只鸡)。其次这个 Binder 实体的引用在所有 Client 中都固定为 0 而无需通过其它手段获得。也就是说,一个 Server 想要向 ServiceManager 注册自己的 Binder 就必须通过这个 0 号引用和 ServiceManager 的 Binder 通信。类比互联网,0 号引用就好比是域名服务器的地址,你必须预先动态或者手工配置好。要注意的是,这里说的 Client 是相对于 ServiceManager 而言的,一个进程或者应用程序可能是提供服务的 Server,但对于 ServiceManager 来说它仍然是个 Client。
最后我想说
为什么很多程序员做不了架构师?
1、良好健康的职业规划很重要,但大多数人都忽略了
2、学习的习惯很重要,持之以恒才是正解。
3、编程思维没能提升一个台阶,局限在了编码,业务,没考虑过选型、扩展
4、身边没有好的架构师引导、培养。所处的圈子对程序员的成长影响巨大。
金九银十面试季,跳槽季,整理面试题已经成了我多年的习惯!在这里我和身边一些朋友特意整理了一份快速进阶为Android高级工程师的系统且全面的学习资料。涵盖了Android初级——Android高级架构师进阶必备的一些学习技能。
附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题(含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)
里面包含不同方向的自学编程路线、面试题集合/面经、及系列技术文章等,资源持续更新中…
式汇总、Gradle知识点、常见算法题汇总。)
[外链图片转存中…(img-xMR10LsE-1643541864822)]
里面包含不同方向的自学编程路线、面试题集合/面经、及系列技术文章等,资源持续更新中…