杭州网站建设公司费用,郑州一建集团公司官网,嘉兴seo计费管理,seo优化怎么做文章目录 前言一、Linux传统跨进程通信原理二、Android Binder跨进程通信原理1、动态内核可加载模块2、内存映射3、Binder IPC 实现原理 三、Android Binder IPC 通信模型1、Client/Server/ServiceManager/驱动Binder与路由器之间的角色关系 2、Binder通信过程3、Binder通信中的… 文章目录 前言一、Linux传统跨进程通信原理二、Android Binder跨进程通信原理1、动态内核可加载模块2、内存映射3、Binder IPC 实现原理 三、Android Binder IPC 通信模型1、Client/Server/ServiceManager/驱动Binder与路由器之间的角色关系 2、Binder通信过程3、Binder通信中的代理模式4、Binder 的完整定义 四、Binder机制在Android中的具体实现——实现两个数相加1、定义Client进程需要调用的接口方法2、建立IPCService3、MainActivity中bindService最后将结果显示在TextView中 五、Binder高频面试题1、Binder为何能实现一次copy为什么会出现物理地址和虚拟地址呢2、两个进程间的通信Binder原理 前言
对Binder跨进程通信的原理予以记录 参考博客Binder 原理剖析与使用——西门吹雪 Binder 是一种进程间通信机制基于开源的 OpenBinder 实现OpenBinder 起初由 Be Inc. 开发后由 Plam Inc. 接手。
一、Linux传统跨进程通信原理
Linux传统跨进程通信原理
二、Android Binder跨进程通信原理
Android 系统是基于 Linux 内核的Linux 已经提供了管道、消息队列、共享内存和 Socket 等 IPC 机制。那为什么 Android 还要提供 Binder 来实现 IPC 呢主要是基于性能、稳定性和安全性几方面的原因。如下图 理解了 Linux IPC 相关概念和通信原理接下来我们正式介绍下 Binder IPC 的原理
1、动态内核可加载模块
传统的 IPC 机制如管道、Socket 都是内核的一部分因此通过内核支持来实现进程间通信自然是没问题的。但是 Binder 并不是 Linux 系统内核的一部分那怎么办呢这就得益于 Linux 的动态内核可加载模块Loadable Kernel ModuleLKM的机制模块是具有独立功能的程序它可以被单独编译但是不能独立运行。它在运行时被链接到内核作为内核的一部分运行。这样Android 系统就可以通过动态添加一个内核模块运行在内核空间用户进程之间通过这个内核模块作为桥梁来实现通信。 在 Android 系统中这个运行在内核空间负责各个用户进程通过 Binder 实现通信的内核模块就叫 Binder 驱动Binder Dirver 2、内存映射
Binder IPC 机制中涉及到的内存映射通过 mmap() 来实现mmap() 是操作系统中一种内存映射的方法。内存映射简单的讲就是将用户空间的一块内存区域映射到内核空间。映射关系建立后用户对这块内存区域的修改可以直接反应到内核空间反之内核空间对这段区域的修改也能直接反应到用户空间。 内存映射能减少数据拷贝次数实现用户空间和内核空间的高效互动。两个空间各自的修改能直接反映在映射的内存区域从而被对方空间及时感知。也正因为如此内存映射能够提供对进程间通信的支持。 3、Binder IPC 实现原理
Binder IPC 正是基于内存映射mmap来实现的但是 mmap() 通常是用在有物理介质的文件系统上的。
比如进程中的用户区域是不能直接和物理设备打交道的如果想要把磁盘上的数据读取到进程的用户区域需要两次拷贝磁盘–内核空间–用户空间通常在这种场景下 mmap() 就能发挥作用通过在物理介质和用户空间之间建立映射减少数据的拷贝次数用内存读写取代I/O读写提高文件读取效率。 在 Android 系统中这个运行在内核空间负责各个用户进程通过 Binder 实现通信的内核模块就叫 Binder驱动Binder Dirver。 而 Binder 并不存在物理介质因此 Binder 驱动使用 mmap() 并不是为了在物理介质和用户空间之间建立映射而是用来在内核空间创建数据接收的缓存空间。 一次完整的 Binder IPC 通信过程通常是这样
首先 Binder 驱动在内核空间创建一个数据接收缓存区接着在内核空间开辟一块内核缓存区建立内核缓存区和内核中数据接收缓存区之间的映射关系以及内核中数据接收缓存区和接收进程用户空间地址的映射关系发送方进程通过系统调用 copyfromuser() 将数据 copy 到内核中的内核缓存区由于内核缓存区和接收进程的用户空间存在内存映射因此也就相当于把数据发送到了接收进程的用户空间这样便完成了一次进程间的通信。如下图 说明1Client进程、Server进程 Service Manager 进程之间的交互都必须通过Binder驱动使用 open 和 ioctl文件操作函数而非直接交互
三、Android Binder IPC 通信模型
介绍完 Binder IPC 的底层通信原理接下来我们看看实现层面是如何设计的。
一次完整的进程间通信必然至少包含两个进程通常我们称通信的双方分别为客户端进程Client和服务端进程Server由于进程隔离机制的存在通信双方必然需要借助 Binder 来实现。
1、Client/Server/ServiceManager/驱动
Binder 是基于 C/S 架构的。由一系列的组件组成包括 Client、Server、ServiceManager、Binder 驱动。 Client、Server、Service Manager 运行在用户空间Binder 驱动运行在内核空间。 Service Manager 和 Binder 驱动由系统提供而 Client、Server 由应用程序来实现。 Client、Server 和 ServiceManager 均是通过系统调用 open、mmap 和 ioctl 来访问设备文件 /dev/binder从而实现与 Binder 驱动的交互来间接的实现跨进程通信。
说明2Binder请求的线程管理
Server进程会创建很多线程来处理Binder请求Binder模型的线程管理采用Binder驱动的线程池并由Binder驱动自身进行管理而不是由Server进程来管理的
一个进程的Binder线程数默认最大是16超过的请求会被阻塞等待空闲的Binder线程。所以在进程间通信时处理并发问题时如使用ContentProvider时它的CRUD创建、检索、更新和删除方法只能同时有16个线程同时工作 说明3 Binder驱动 Service Manager进程 属于 Android基础架构即系统已经实现好了而Client 进程 和 Server 进程 属于Android应用层需要开发者自己实现 Client、Server、ServiceManager、Binder 驱动这几个组件在通信过程中扮演的角色就如同互联网中服务器Server、客户端Client、DNS域名服务器ServiceManager以及路由器Binder 驱动之前的关系。
Binder与路由器之间的角色关系 通常我们访问一个网页的步骤是这样的首先在浏览器输入一个地址如http://www.google.com 然后按下回车键。但是并没有办法通过域名地址直接找到我们要访问的服务器因此需要首先访问 DNS 域名服务器域名服务器中保存了 http://www.google.com 对应的 ip 地址 10.249.23.13然后通过这个 ip 地址才能访问到 http://www.google.com 对应的服务器。 Binder 驱动就如同路由器一样是整个通信的核心驱动负责进程之间 Binder 通信的建立Binder 在进程之间的传递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。 2、Binder通信过程
至此我们大致能总结出 Binder 通信过程
首先一个进程使用 BINDERSETCONTEXT_MGR 命令通过 Binder 驱动将自己注册成为 ServiceManagerServer 通过驱动向 ServiceManager 中注册 BinderServer 中的 Binder 实体表明可以对外提供服务。驱动为这个 Binder 创建位于内核中的实体节点以及 ServiceManager 对实体的引用将名字以及新建的引用打包传给 ServiceManagerServiceManger 将其填入查找表。Client 通过名字在 Binder 驱动的帮助下从 ServiceManager 中获取到对 Binder 实体的引用通过这个引用就能实现和 Server 进程的通信。
我们看到整个通信过程都需要 Binder 驱动的接入。下图能更加直观的展现整个通信过程(为了进一步抽象通信过程以及呈现上的方便下图我们忽略了 Binder 实体及其引用的概念)
3、Binder通信中的代理模式
我们已经解释清楚 Client、Server 借助 Binder 驱动完成跨进程通信的实现机制了但是还有个问题会让我们困惑。A 进程想要 B 进程中某个对象object是如何实现的呢毕竟它们分属不同的进程A 进程 没法直接使用 B 进程中的 object。
前面我们介绍过跨进程通信的过程都有 Binder 驱动的参与因此在数据流经 Binder 驱动的时候驱动会对数据做一层转换。
当 A 进程想要获取 B 进程中的 object 时驱动并不会真的把 object 返回给 A而是返回了一个跟 object 看起来一模一样的代理对象 objectProxy这个 objectProxy 具有和 object 一摸一样的方法但是这些方法并没有 B 进程中 object 对象那些方法的能力这些方法只需要把请求参数交给驱动即可。对于 A 进程来说和直接调用 object 中的方法是一样的。当 Binder 驱动接收到 A 进程的消息后发现这是个 objectProxy 就去查询自己维护的表单一查发现这是 B 进程 object 的代理对象。于是就会去通知 B 进程调用 object 的方法并要求 B 进程把返回结果发给自己。当驱动拿到 B 进程的返回结果后就会转发给 A 进程一次通信就完成了。
4、Binder 的完整定义
现在我们可以对 Binder 做个更加全面的定义了
从进程间通信的角度看Binder 是一种进程间通信的机制从 Server 进程的角度看Binder 指的是 Server 中的 Binder 实体对象从 Client 进程的角度看Binder 指的是对 Binder 代理对象是 Binder 实体对象的一个远程代理从传输过程的角度看Binder 是一个可以跨进程传输的对象Binder 驱动会对这个跨越进程边界的对象做一点点特殊处理自动完成代理对象和本地对象之间的转换。
四、Binder机制在Android中的具体实现——实现两个数相加
1、定义Client进程需要调用的接口方法
public interface IPlus extends IInterface {//定义需要实现的接口方法即Client进程需要调用的方法public int add(int a, int b);
}2、建立IPCService
public class IPCService extends Service {public static final String DESCRIPTOR add two int;private final MyAddBinder mBinder new MyAddBinder();public IPCService() {/*** 将(descriptor, plus)作为(key, value)对存入到Binder对象中的一个MapString, IInterface中* 之后binder对象可根据descriptor找到对应IInterface对象的引用进而调用其方法** param plus* param descriptor*/IPlus plus new IPlus() {Overridepublic int add(int a, int b) {return a b;}Overridepublic IBinder asBinder() {return null;}};/*** 1.将add two intplus作为key,value对存入到Binder对象中的一个MapString,IInterface对象中* 2.之后Binder对象可根据add two int通过queryLocalIInterface()获得对应IInterface对象*/mBinder.attachInterface(plus, DESCRIPTOR);}private class MyAddBinder extends Binder {/*** 继承自IBinder接口的执行Client进程所请求的目标的方法子类需要复写该方法* 注运行在Server进程的Binder线程池中当Client进程发起远程请求时远程请求会要求系统底层执行回调该方法* param code Client进程请求方法标识符。即Server进程根据该标识确定所请求的目标方法* param data 目标方法的参数。Client进程传进来的此处就是整数a和b* param reply 目标方法执行后的结果返回给Client进程* param flags* return*/Overrideprotected boolean onTransact(int code, NonNull Parcel data, Parcel reply, int flags)throws RemoteException {if (code 1) {Log.d(TAG, MyBinder Switch块 ----- Process.myPid());data.enforceInterface(DESCRIPTOR);int a data.readInt();int b data.readInt();int result ((IPlus) this.queryLocalInterface(DESCRIPTOR)).add(a, b);reply.writeNoException();reply.writeInt(result);return true;}Log.d(TAG, MyBinder OnTransact() ----- android.os.Process.myPid());return super.onTransact(code, data, reply, flags);}}NullableOverridepublic IBinder onBind(Intent intent) {return mBinder;}
}3、MainActivity中bindService最后将结果显示在TextView中
public class MainActivity extends AppCompatActivity implements View.OnClickListener {public static final String DESCRIPTOR1 add two int;private EditText editText1;private EditText editText2;private TextView resultText;private Button addBtn;private Button subtractBtn;private IBinder mBinder;private final ServiceConnection mServiceConnection new ServiceConnection() {Overridepublic void onServiceConnected(ComponentName name, IBinder service) {mBinder service;Log.d(TAG, 客户端----- android.os.Process.myPid());}Overridepublic void onServiceDisconnected(ComponentName name) {mBinder null;}};Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);bindViews();addBtn.setOnClickListener(this);subtractBtn.setOnClickListener(this);//service方式Intent service new Intent(this, IPCService.class);bindService(service, mServiceConnection, BIND_AUTO_CREATE);}private void bindViews() {editText1 (EditText) findViewById(R.id.edit_arg1);editText2 (EditText) findViewById(R.id.edit_arg2);resultText (TextView) findViewById(R.id.result_arg);addBtn (Button) findViewById(R.id.btn_add);subtractBtn (Button) findViewById(R.id.btn_subtract);}Overridepublic void onClick(View v) {int id v.getId();if (id R.id.btn_add) {add();}}public void add() {int a Integer.parseInt(editText1.getText().toString());int b Integer.parseInt(editText2.getText().toString());if (mBinder ! null) {Parcel _data Parcel.obtain();Parcel _reply Parcel.obtain();int _result -1;try {_data.writeInterfaceToken(DESCRIPTOR1);_data.writeInt(a);_data.writeInt(b);mBinder.transact(1, _data, _reply, 0);_reply.readException();_result _reply.readInt();resultText.setText(_result );Toast.makeText(MainActivity.this, result: _result,Toast.LENGTH_SHORT).show();} catch (RemoteException e) {throw new RuntimeException(e);} finally {_reply.recycle();_data.recycle();}} else {Toast.makeText(MainActivity.this, 未连接服务端或服务端异常,Toast.LENGTH_SHORT).show();}}
}五、Binder高频面试题
1、Binder为何能实现一次copy
Binder的一次copy是利用了mmap内存映射文件目的是开辟物理地址内存映射文件是在堆和栈的空余空间 mmap是在linux中的api可以通过mmap去开辟物理地址空间。 MMU(Memeory Mananger Unit)将mmap开辟的物理内存地址转化成虚拟内存地址 物理地址内存条上的地址 虚拟地址实际上是MMU提供的虚拟地址 Binder采用的是C/S模式的其中提供服务的进程成为Server进程访问服务的进程成为Client进程Server和Client进程通信要依靠运行在内核空间的Binder驱动程序来进行
Service组件在开启时会将自己注册到一个Service Manager里以便于Client进程在ServiceManager中找到它因此ServiceManager也称为Binder进程间通信的上下文管理者同时它也需要和普通的Server进程和Client进程通信所以也是可以看做一个特殊的Service组件 注Binder通信机制中的Service组件和Android中的Service组件是不同的概念Service Manager里注册服务是在进程间的注册 为什么会出现物理地址和虚拟地址呢
由于现在的程序app大小都很大了如果全部加载到内存中去运行需要很多内存而手机的内存是有限的又由于当你在运行一个app的时候不是所有的代码都会被加载到内存中去运行只会加载一部分正在活动的代码为了满足程序局部性原则这时出现的物理地址和虚拟地址刚好能解决能省下不用的内存空间供其他app使用 物理地址内存条上的地址 虚拟地址MMU提供的虚拟的地址 MMUMemory Management Unit)内存管理单元涉及到一个转换物理地址和虚拟地址 为什么会存在MMU因为app的整体大小假如是100M但是实际的活跃代码在内存中只有1M其他的代码处于磁盘中所以cpu在运行代码的时候不可能说只给1M的内存让cpu在里面运行所以需要给一个MMU中间件让CPU感觉运行在512M的内存中。
MMU里有页表页表里保存有效位地址有效位为0表示未缓存为1表示已缓存只要有效位是1就肯定有地址
虚拟地址和物理地址如果虚拟地址中没有的话MMU就会读取磁盘并拿出来在内存中开辟一块新的空间并在MMU中存放物理地址和对应的虚拟地址cpu就会拿到虚拟地址
2、两个进程间的通信Binder原理
Binder的通信是进程A调用copy_form_user到内核空间内核空间同进程B建立了链接copy_to_user会将数据传给B进程而进程间的通信大小是1M-8Kcopy到内核空间8K是由于有请求头等信息一页是4K需要是4的整数倍 页cpu为了高效执行以及内存管理每次需要拿一个页的代码这个页表示一块连续的储存空间。常见的4kb也称为块。 假如页的大小为P那么在虚拟内存中VP就称为虚拟页从虚拟内存中拿到的一页的代码放在物理内存中那么物理内存中也得有一个同样大小可以页存放虚拟页的代码物理内存中的页称为物理页PP
在任何时刻虚拟页都有以下三种状态中的一种且以下状态都是在MMU中体现
未分配的VM还未分配页或者未创建未分配的页还没有任何数据与代码与他们关联因此也就不占任何磁盘已缓存的当前缓存在物理内存中已分配页未缓存的当前未缓存在屋里内存中已分配页