尊旭网
当前位置: 尊旭网 > 知识 >

binder机制

时间:2024-11-24 12:07:16 编辑:阿旭

Binder机制概述

Android进程间通讯是通过Binder机制来实现的,Android是基于linux系统因此有必要了解Linux系统进程相关知识.

Linux系统中(其他系统也是这样)不同进程之间不允许直接操作或访问另一进程.也就是进程隔离.

为了保证用户进程不能直接访问内核,操作系统从逻辑上将虚拟空间划分为用户空间和内核空间.内核程序运行在内核空间(kernel space),应用程序运行在用户空间(user space).为了安全,他们之间是隔离的,即使用户程序奔溃了,也不会影响内核.内核空间数据是可以共享的,用户空间不可以.

用户空间访问内核空间只能通过系统调用,系统调用是用户空间访问内核空间的唯一方式,保证所有资源访问在内核控制下,避免了用户对系统资源的越权访问,提高了系统安全性和稳定性.
copy_from_user:将用户空间数据拷贝到内核空间
copy_to_user:将内核空间数据拷贝到用户空间

由于用户进程不能直接访问硬件地址,所以系统提供了一种机制:内存映射(Memory Map).在Linux中通过调用函数mmap实现内存映射,将用户空间一块内存地址映射到内核空间.映射关系建立后,对用户空间内存的修改可以反应到内核空间.内存映射可减少拷贝次数.
如果没有内存映射,用户进程需要访问硬盘文件时,需要在内核空间创建一片页缓存,将硬盘文件数据拷贝到页缓存然后用户进程在拷贝页缓存数据,需要两次拷贝.通过内存映射后硬盘文件可以和内核的虚拟内存直接映射,减少一次拷贝.
如图

inter-process-communication进程间通信,指进程间交换数据的过程.

Linux提供了很多进程间通讯的机制,主要有管道(pipe)、消息队列(Message)、信号(sinal)、信号量(semophore)、套接字(socket)等

内核程序在内核空间分配并开辟一块内核缓冲区,发送进程将数据通过copy_from_user拷贝到内核空间的数据缓冲区,内核空间通过copy_to_user将数据拷贝到接收进程,这样就实现了一次进程间通信.如图

Linux的IPC机制有两个问题:
1.数据通过用户空间->内核空间->用户空间,经过两次拷贝,效率不高
2.接收进程无法预先知道数据大小,只能尽可能大创建数据缓冲区,或通过api消息头获取消息体的大小,浪费了空间或时间.

Android进程间通信通过Binder实现,下面介绍Binder机制通信原理,为什么是Binder

内核程序创建一个数据接收缓存区,同时创建一个内核缓冲区.并建立内核缓冲区和数据接收缓冲区内存映射,以及数据内核缓冲区和接收进程用户空间的映射关系.发送进程通过copy_from_user将数据拷贝到内核数据接收缓冲区,因为内核数据接收缓冲区和内核缓冲区以及接收进程用户空间存在映射关系,相当于将数据发送到了接收进程.完成了一次进程间通信.如图

Binder机制是c/s架构的,由Client、server、ServiceManager和Binder组成.client、server和serviceManager都是独立的进程,由于Linux进程隔离的原因,所以需要借助Binder进行通信.
Binder通信主要有三个步骤:注册服务、获取服务、使用服务.如下图

从上一条Binder实现原理示例图中可以看到,Binder可分为Java Binder、Native Binder和Kernal Binder.应用开发需要了解Java Binder和Navive Binder.这里只介绍Binder基本原理.具体可查看文章结尾的链接.

感谢
https://blog.csdn.net/itachi85/article/details/102713845
https://www.jianshu.com/p/429a1ff3560c
https://blog.csdn.net/carson_ho/article/details/73560642?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522159651217319195188353096%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=159651217319195188353096&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2 all first_rank_ecpm_v3~rank_business_v1-1-73560642.ecpm_v3_rank_business_v1&utm_term=Binder&spm=1018.2118.3001.4187


Binder 总结

binder是Android 中的一种进程间通信机制(IPC机制)

android 是一种基于linux 的系统,linux 系统已经提供了 诸如管道、消息队列、共享内存和socket 等IPC 方式。既然已经存在了如此之多的IPC 机制,为什么binder还会出现?主要是因为上述IPC机制无法对android 而言存在着诸多的不便,主要体现在性能,稳定性和安全性三个方面。

综上,android中使用Binder作为其IPC 机制。

binder 主要是通过内存映射来实现的,一次完整的ipc通讯的过程如下:
1.binder 驱动在内核中创建一块数据接收缓冲区
2.建立一块内核缓冲区
3.建立内核缓冲区和数据接收缓冲区的映射
4.建立内核数据缓冲区和接收进城用户空间的映射
5.发送方将数据发送到内核缓冲区
6.由于映射的存在,就相当于直接将数据发送到了 接收进城中

binder 主要有四部分组成

其中binder client 、 binder service 和 servicemanager 运行在用户空间,而binder 驱动则是运行在内核空间(binder驱动是通过模块挂载的方式,运行在内核空间中的)。

binder 的工作流程其实可以类比为 上网的过程。客户端(binder client) 服务器(binder service) dns(servicemanager) 和 路由器(binder 驱动)

客户端输入网址请求服务器会在路由器的帮助下请求 dns 服务器获取服务器的ip地址,然后利用ip地址和服务器进行通讯。

binder基本的运行如下:

1.首先一个进城通过binder驱动将自己注册为servicemanager

2.service 通过binder 驱动将自己的binder 注册到servicemanager中,以对外使用。在这个过程中,binder驱动会生成该binder 的内核节点,以及该节点的引用,并将这些内容发送给servicemanager,servicemanager会把引用存入表中
3.client 通过binder名称,在binder驱动的帮助下在servicemanager中获取到service 中binder 的引用,从而跨进程通信

1.通过上述的工作流程可知,servicemanager 其实是一个特殊的进程,service 和 client 和 servicemanager 之间的通讯其实也是进程间的通讯,而这里的进程间通信其实也是使用的binder通信。这里的设计十分巧妙,servicemanager 是service 端,其他所有进程都是它的client 端,servicemanager提供了一个非常特殊的binder,他不需要注册也没有名字,其他进程可以直接获取到该binder 和servicemanager 进行通讯。

2.binder中还使用了代理模式,client 端所获取的service 的binder引用并不是一个真的binder对象,而是一个service端binder 的代理,调用binder中方法的时候通过对service进行请求然后获取返回结果。

android binder通信机制其实可以看作是一次简单的上网过程

1.客户端输入网址(发送端请求跨进程通信)
2.通过路由器查询dns 服务器 根据域名获取ip地址(通过binder驱动,获取servicemanager 中实名binder的引用)
3.根据返回的ip地址,通过路由器连接服务器(根据获取到service端的binder 代理 client端和service端进行通信 )


深入理解Binder

之前一直对 Binder 理解不够透彻,仅仅知道一些皮毛,所以最近抽空深入理解一下,并在这里做个小结。 Binder 是 Android 系统中实现 IPC (进程间通信)的一种机制。Binder 原意是“胶水、粘合剂”,所以可以想象它的用途就是像胶水一样把两个进程紧紧“粘”在一起,从而可以方便地实现 IPC 。 那么为什么会有进程通信呢?这是因为在 Linux 中进程之间是隔离的,也就是说 A 进程不知道有 B 进程的存在,相应的 B 进程也不知道 A 进程的存在。A 、B 两进程的内存是不共享的,所以 A 进程的数据想要传给 B 进程就需要用到 IPC 。 在这里再科普一下进程空间的知识点:进程空间可以分为用户空间和内核空间。简单的说,用户空间是用户程序运行的空间,而内核空间就是内核运行的空间了。因为像内核这么底层、至关重要的东西肯定是不会简单地让用户程序随便调用的,所以需要把内核保护起来,就创造了内核空间,让内核运行在内核空间中,这样就不会被用户空间随便干扰到了。两个进程之间的用户空间是不共享的,但是内核空间是共享的。 所以到这里,有些同学会有个大胆的想法,两个进程间的通信可以利用内核空间来实现啊,因为它们的内核空间是共享的,这样数据不就传过去了嘛。但是接着又来了一个问题:为了保证安全性,用户空间和内核空间也是隔离的。那么如何把数据从发送方的用户空间传到内核空间呢? 针对这个问题提供了 系统调用 来解决,可以让用户程序调用内核资源。系统调用是用户空间访问内核空间的唯一方式,保证了所有的资源访问都是在内核的控制下进行的,避免了用户程序对系统资源的越权访问,提升了系统安全性和稳定性(这段话来自 《写给 Android 应用工程师的 Binder 原理剖析》 )。我们平时的网络、I/O操作其实都是通过系统调用在内核空间中运行的(也就是 内核态 )。 至此,关于 IPC 我们有了一个大概的实现方案:A 进程的数据通过系统调用把数据传输到内核空间(即copy_from_user),内核空间再利用系统调用把数据传输到 B 进程(即 copy_to_user)。这也正是目前 Linux 中传统 IPC 通信的实现原理,可以看到这其中会有两次数据拷贝。 (图片来自于 《写给 Android 应用工程师的 Binder 原理剖析》 ) Linux 中的一些 IPC 方式: 通过上面的讲解我们可以知道,IPC 是需要内核空间来支持的。Linux 中的管道、socket 等都是在内核中的。但是在 Linux 系统里面是没有 Binder 的。那么 Android 中是如何利用 Binder 来实现 IPC 的呢? 这就要讲到 Linux 中的 动态内核可加载模块 。动态内核可加载模块是具有独立功能的程序,它可以被单独编译,但是不能独立运行。它在运行时被链接到内核作为内核的一部分运行。这样,Android 系统就可以通过动态添加一个内核模块运行在内核空间,用户进程之间通过这个内核模块作为桥梁来实现通信。(这段话来自 《写给 Android 应用工程师的 Binder 原理剖析》 )在 Android 中,这个内核模块也就是 Binder 驱动。 另外,Binder IPC 原理相比较上面传统的 Linux IPC 而言,只需要一次数据拷贝就可以完成了。那么究竟是怎么做到的呢? 其实 Binder 是借助于 mmap (内存映射)来实现的。mmap 用于文件或者其它对象映射进内存,通常是用在有物理介质的文件系统上的。mmap 简单的来说就是可以把用户空间的内存区域和内核空间的内存区域之间建立映射关系,这样就减少了数据拷贝的次数,任何一方的对内存区域的改动都将被反应给另一方。 所以,Binder 的做法就是建立一个虚拟设备(设备驱动是/dev/binder),然后在内核空间创建一块数据接收的缓存区,这个缓存区会和内存缓存区以及接收数据进程的用户空间建立映射,这样发送数据进程把数据发送到内存缓存区,该数据就会被间接映射到接收进程的用户空间中,减少了一次数据拷贝。具体可以看下图理解 (图片来自于 《写给 Android 应用工程师的 Binder 原理剖析》 ) Binder 的优点 在整个 Binder 通信过程中,可以分为四个部分: 其中 Client 和 Server 是应用层实现的,而 Binder 驱动和 ServiceManager 是 Android 系统底层实现的。 具体流程如下: (Binder通信过程示意图来自于 《写给 Android 应用工程师的 Binder 原理剖析》 )