本文基于上一篇文章android binder入门(二)——手动实现,进一步讲解原理
1、优势
android是在Linux基础上进行开发出的一套系统,Linux进程间通信包括几种方式:共享内存、管道、socket、消息队列、binder。
共享内存:优点就是无需进行拷贝数据,客户端和服务端都可以直接对内存中的数据进行操作,这样操作读取速度很快,但是需要很大的内存,这方面在手机上实现是很困难的,并且安全性无法保证,任何app都可以操作这部分数据,导致数据很乱
管道、socket、消息队列:操作过程都是,服务端先将数据copy到内核缓存区,客户端需要数据操作的时候,也要将内核缓存区的数据copy到自己的内存空间内,这就进行了两次copy,消耗性能很大,尤其是android手机,对性能要求更高,完全不符合标准
binder:copy数据就是介于上面两者之间,服务端将内存中的数据copy到内核缓存区,同时对外提供操作的地址映射,客户端就可以通过映射对数据进行操作,无需copy到自己的内存空间,这样就进行了一次copy,节约了性能。并且ServiceManager给每个binder设置了一个标志UID,保证了只能访问对应id的binder,保证了数据的安全性。
所以android系统才选择了binder作为IPC进程间的通信方式。
2、通信模型
根据上面的模型,分为用户空间和内核空间:
内核空间就是binder驱动,由系统去实现。
用户空间中ServiceManager实际就是一个统计作用,内部存储各个binder和对应的映射关系,服务端的binder注册都是服务端将binder数据包通过binder驱动发送到ServiceManager中,驱动需要在数据包中添加内核中的节点和内核引用,ServiceManager就可以从数据包中取出binder名字和引用添加到一张表中。
客户端启动服务的时候,也是需要通过binder驱动到ServiceManager中查找对应名字的binder,然后获取到对应binder对象和内核空间的引用,然后就可以通过binder对象操作内核空间存储的数据。
在Java层面的代码执行过程就是,
1、服务端通过Stub构造函数中this.attachInterface(this,DESCRIPTOR)
向binder的ServiceManager中注册匿名binder。
2、客户端启动服务端的Service,获取到服务端创建的binder对象,拥有了可以和服务端通信的能力,并且可以执行服务端提供的方法
3、客户端调用服务端提供的方法,通过Proxy的实现方法调用获取到的binder执行对应服务端的方法,达到跨进程通信,最后具体的操作都是在Service中的binder(抽象类Stub的实现变量,具体执行过程要在重写的方法内部)变量中执行
目前只能是拾人牙慧,进行简单的介绍一下,至于内部具体的很多问题,目前都不是很了解,尤其是深入的c语言的实现还需要继续去学习了。binder机制学习还是很消耗时间的,并且很枯燥。
感谢网上大佬的奉献,参考:
https://zhuanlan.zhihu.com/p/35519585
https://blog.csdn.net/universus/article/details/6211589