首页 技术 正文
技术 2022年11月6日
0 收藏 774 点赞 612 浏览 4127 个字

终于还是得写一篇关于Binder的文章了。从最初接触Android到花大把时间研究Android源码,Binder一直是分析道路的拦路虎。看了几本最流行的Android源码分析书籍,每次基本上都不能把Binder相关知识看完、读透。好在一直没有放弃,第一次理解不了就跳过,下一次重新读,每次读都有新的收获。现在是时候整理整理了。

  我理解的Binder是什么?一种IPC(跨进程通信)的实现方式。注意“跨进程”,表明数据从一个进程“流向”了另一个进程。首先要了解为什么跨进程那么难?因为应用程序的地址空间都是虚地址,通过映射到物理内存空间。假如应用程序都直接在物理内存空间操作,那应用程序跨进程就非常方便了,直接传递物理地址就好了。但是,不同应用程序使用的是通过内存管理器作映射的虚拟内存空间,至于映射到哪个物理内存区域,这是未知的,从而使得跨进程传递数据变得不那么方便,但也不是没有办法。Linux中典型的管道、消息队列、共享内存等都可以实现跨进程通信。Android采用了Binder方式,自然有它的优势,所谓“灵活、方便”,这个优势还在慢慢体会中。

  要深刻理解android的Binder机制,有几点是需要理解的:

  1. android操作系统内部,可以看成一种基于C/S架构的服务提供机制。由Client发出某种服务请求,由Service执行服务并返回结果。比如一个APP需要使用照相机就需要向MediaService提出服务请求,而这两个进程之间的通信,就是通过Binder来完成,android中存在大量的这种C/S结构。
  2. 进程与类的区别:一个进程可以提供多种服务(可以通过多线程的方式,也可以是逻辑判断提供哪种服务),比如MediaService是android中的一个进程,提供了AudioFlinger服务、AudioPolicyService服务、MediaPlayerService服务等。在分析的过程中,要时刻明白当前进程执行的代码是什么。
  3. 通信与业务要区分开来:Binder说到底是一种通信方式,那么谁使用它呢?那肯定是上层代码。这里说是“上层代码”,是因为代码框架的层次结构是分析重点。这些上层代码可能在不同进程中执行。

如果看过Service实现,或者各种类的继承关系,它们是有一定的层次关系的。现在采用“从下往上”的逐层分析,在看源码时,个人发现“从下往上”往往可以取得就好的效果。结合《深入理解android 卷1》第六章来分析。

  • Binder是一个“设备”

  有设备就离不开设备驱动,Binder实际上就是一个包含驱动、协议的伪设备,通过常用的对设备的操作,如open、ioctl、mmap等,可以操作Binder这个“设备”。其中关键点为:通过mmap可以把一块虚拟地址与物理地址映射起来,这时候,A进程与Binder共享同一块物理内存,如果另一个B进程也通过Binder共享了这块内存,那么A、B两进程就共享了同一块内存——这就是不同应用程序共享内存的(通过Binder)的原理。还有有一句话就是,如果到这里,两个进程之间已经完全可以通过这个Binder来通信了,当然实现细节肯定很麻烦。故要做封装!!根据上面所说的第3点,这里仍然在描述通信细节,还没有涉及到业务。Binder驱动本身比较复杂,不作分析。可以简单化它:通过它可以作内存共享即可——打开Binder设备、执行mmap、通过Binder驱动发送请求、获取结果。

  • ProcessState和IPCThreadState

  既然要做封装,那么这一层应该是封装对Binder设备的操作细节,分析代码也的确是这样。从抽象的角度来说,封装肯定是为了更简便的操作,如果把Binder的打开、执行mmap等等结果封装好,那上层只需要调用相应的接口就好了,达到封装的目的。

  首先得明白,ProcessState和IPCThreadState是两个类,好像是废话,但它俩的名字起得让人“浮想联翩”,老是让我把它们当做进程、线程。我们知道,一个应用程序对应一个Linux进程,OK,知道这个就好了,更多可访问:http://blog.csdn.net/mirkerson/article/details/38128637 。ProcessState和IPCThreadState就是为了进程/线程执行Binder更加方便而作的封装。

ProcessState::ProcessState()
: mDriverFD(open_driver())//打开Binder以及对Binder的相关映射操作,不细看
, mVMStart(MAP_FAILED)
, mManagesContexts(false)
, mBinderContextCheckFunc(NULL)
, mBinderContextUserData(NULL)
, mThreadPoolStarted(false)
, mThreadPoolSeq(1)
{
if (mDriverFD >= 0) {
// XXX Ideally, there should be a specific define for whether we
// have mmap (or whether we could possibly have the kernel module
// availabla).
#if !defined(HAVE_WIN32_IPC)
// mmap the binder, providing a chunk of virtual address space to receive transactions.
mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
if (mVMStart == MAP_FAILED) {
// *sigh*
ALOGE("Using /dev/binder failed: unable to mmap transaction memory.\n");
close(mDriverFD);
mDriverFD = -1;
}
#else
mDriverFD = -1;
#endif
} LOG_ALWAYS_FATAL_IF(mDriverFD < 0, "Binder driver could not be opened. Terminating.");
}

  通过上面的分析可知,对Binder的封装果然是在ProcessState里完成,实际上是使用IPCThreadState来完成——这里直接给出结果:每个进程有一个ProcessState实例,每个线程有一个IPCThreadState实例,具体的通信由每个线程自己与Binder完成,(因为采用了单例模式)。

  • IBinder、BpBinder和BBinder

  刚才看到了ProcessState对Binder的封装,可是这个封装还不够。因为ProcessState只是对Binder的打开、mmap等作了封装,具体数据应该怎么传入Binder,传入什么样的数据等还需要更细粒度的封装,这就是IBinder。IBinder提供了子类BpBinder和BBinder的通用部分,BpBinder和BBinder就用这个通用部分来通信。到目前为止,还是在分析通信行为,没有涉及业务。

查看BpBinder的transact实现:

status_t BpBinder::transact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
// Once a binder has died, it will never come back to life.
if (mAlive) {
status_t status = IPCThreadState::self()->transact(//的确是调用了IPCThreadState来与Binder通信
mHandle, code, data, reply, flags);
if (status == DEAD_OBJECT) mAlive = 0;
return status;
} return DEAD_OBJECT;
}

  为了不复杂化,到这里只需要知道IBinder作为基类,“制定”了Client和Server端的通信的接口,具体由BpBinder和BBinder来实现。

  • 业务层

  面向对象中一个重要的继承,就是为了将通用部分单独成为一个类(抽象类、接口),然后继承,这样子类都有基类的变量或者方法。业务层也是这个逻辑,Client和Server两端分别实现通用部分(假如是接口),再按照接口中的方法分别实现自己的逻辑,这样就简洁多了。以MediaService为例,它的通用部分是IServiceManager,Client端为BpServiceManager,Server端为BnServiceManager。一直不明白,BpServiceManager和BnServiceManager在不同进程中执行,为什么一定要设计成具有IServiceManager这样一个基类呢?其实,如果没有基类,通信也完全可以的,只不过需要更多、更复杂一点的数据来保证,对方能够明白我想要的执行动作是什么。

走到这里,通信层,业务层都已经说明完了。小结:

  1. 类名中带有“Binder”的类是为了通信而实现的
  2. 业务层利用通信层实施通信
  • 通信层与业务层的粘合

从上图中可以看到,实际上的类继承关系比上面论述的稍微复杂一点。这里把我两个关键点就可以把不同层给粘合在一起:

  1. BnServiceManager直接继承了BBinder,表示BnServiceManager可以直接参与Binder通信。
  2. BpServiceManager所继承的BpRefBase有一个mRemote的IBinder类型变量,在实际代码中会指向BpBinder,这样BpServiceManager就可以通过BpBinder来与BBinder通信了。

到这里,就把Binder通信的框架分析完毕。

总结来说:将通信与业务层分离,专注实现各自逻辑,并恰当的粘合在一起,成为一个完整的基于Binder的跨进程通信。

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,085
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,560
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,409
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,182
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,819
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,902