android binder机制及其源码解析 之 第一节 概述

来源:互联网 发布:协议号和端口号的区别 编辑:程序博客网 时间:2024/05/17 05:00
binder 是什么?

binder就是IPC的一种,不知道IPC? 那就谷歌吧,万能的谷歌。
在linux的系统,我们知道是以进程为单位分配和管理资源的。进程是运行在独立的空间里的,这个空间是封闭的,互相独立的。随即问题出来了,在一巨复杂的操作系统中,进程之间要协作完成任务怎么办?这就必须涉及到进程的资源共享和信息的互通。这就要求操作系统提供进程间的IPC。我们知道linux中进程间的通信机制有许多中:命名管道,共享内存,套接字,信号等等若干。但是这些东西统统的不会出现在android中。嘿嘿,因为android提供一种算是全新的通信机制:binder。为什么说是算是全新的呢?因为它是基于openbinder的,是openbinder的阉割版。哈哈......,看到这里,大家就知道什么是binder了。但是为什么是binder呢?

为什么是binder

为甚是binder,而不是以上linux的种种?很明显,android跑到什么设备上,一说大家就都明了:嵌入式设备上。它只有有限的资源。binder的高效,简洁,快速。消耗更少的内存等等,这些优点此时显得尤为重要。当然,还有其他更重要的优势。但是更为重要的是谷歌,谷歌选择了它。其实我个人认为dbus也不错。

binder 驱动初步

在这个专题中,我并不会首先阐述binder机制和整个运行框架,因为这个东西网上讲得很清楚了。非常非常的清楚。我会在之后的部分涉及。因为我是做驱动的。所以我自己倾向于从底层讲起,从binder驱动讲起。由下而上。会分析一些重要的函数。这个出发点是因为我曾经研读这份代码的时候非常希望有这一样一份详细的代码说明文档,这是一件多么幸福的事。但是我发现万能的互联网上居然很少有这样的资料。我想我的后来者或同样感兴趣的同学应该也有同样的想法或困惑。所以我想我把我知道的一些东西共享出来,跟大家分享。当然我讲得并不是全都是对的,因此欢迎所有的人批评和指正。

越说越远了。说正题。为了完成IPC,binder采用了AIDL来描述进程间的接口。在实际的实现中,binder是作为一个字符型设备而存在的,设备节点是/dev/binder. 它遵循了标准的linux设备驱动模型。实现代码在如下路径:kernel/drivers/staging/binder.h   kernel/drivers/staging/binder.c 。 在驱动实现的过程中是通过binder_ioctrl与用户空间交互数据的。我们知道ioctrl的cmd参数可以用来区分不同的请求。binder_thread_write函数的用来发送请求和返回结果。binder_thread_read函数用于读取结果。关于binder的对象和引用这方面的知识我在此不多讲了,大家可以去网上查查。但是我会引用另外一篇博文来阐述binder驱动。(以下引号里的内容为引用他人博文内容,地址为http://www.baidubuqing.com/node/212)

现代操作系统里,一个进程的地址空间是确定的,地址是没有二义性的,进程里的一个指针就对应一个内存地址,不可能同时对应到多个地址,给定一个指针,就能获得想要的东西。但跨进程的情况下,事情就完全不一样了,不同进程的线性地址空间都是一样的,一个进程里的指针放到另一个进程里,就是完全不同的东西了。要实现跨进程的指针,就必须通过操作系统层,只有在系统底层才能将一个进程里的地址映射到另一个进程里的地址,这也就是binder驱动所做的事情。跨进程的指针(以下直接记为binder)有两种存在方式,一是在本地进程里的存在,一是在远程进程里的存在,驱动所做的其实就是实现这两种存在方式的转换。当进程A要使用一个活在进程B里的binder时,驱动根据这个binder在进程A中的表示,找到这个binder的本地进程表示,获取其所在进程和实际指针,然后让它来完成进程A的需求。
由于binder有着这两种不同的存在方式,写程序时得区分binder是不是本进程的指针,这就给用户带来了很多麻烦,失去了跨进程指针的本来意义。为了让用户接口统一,不论是用本地的还是用远程的指针,都使用一套接口,c++/java粉墨登场。在这两种高级语言里,这个跨进程的指针实际上是一个对象的指针,所以binder事实上就升级成了跨进程的对象,用户只需要调用对象的函数就能使用binder,不必关心这个对象是活在本进程的还是活在别的进程中。实现上,提炼这个对象的基本操作成基本接口,分别用两个子类继承之,一个用来实现本地对象,一个用来实现远程对象,用户使用时用基本接口就行了,不必关心对象所在进程,底下的细节由c++的binder库来完成。另一方面,由于JNI的存在,android应用程序赖以生存的java层binder实际上是通过调用c++实现的。因此,android的binder,一半功劳属于binder驱动(kernel/drivers/staging/android/binder.c),而另一半功劳则属于c++的binder库(framework/base/libs/binder),这个库的具体细节以后有空再写。

binder的两种存在方式,一是本地进程里的存在方式,在本地进程里来看,很简单,用他自己的一个指针就能表示,但是从驱动角度来看,光一个进程内指针是不够的,驱动需要区分所有进程的指针,必须再加上一个参数,由于进程本身是操作系统独一无二的,所以驱动里用进程和进程内指针这两个参数就能唯一代表一个binder。而binder在远程进程中的存在,驱动里也是用二元组来表示的,第一维依旧是进程,驱动必须知道这个表示是为哪个远程进程维护的,第二维是一个数,叫做ref, handle, desc都行(以下记作desc,免得混淆),它是一个从0开始编排的数字,它只跟这个binder在这个进程中的出场顺序有关,进程中出现的第一个非本地(远程)的binder被记住0,第二个被记住1,以此类推,跟这个binder实际所在的进程,实际的指针都没关系,一个进程里的所有远程binder是统一排序的。



看完上面的东西就直接进入我要涉及到的一些重点内容:重要的数据结构和函数的说明。



原创粉丝点击