IPC之Binder

基本上参考Android跨进程通信:图文详解 Binder机制 原理再结合自己的理解记录本次学习

Binder总结

Binder驱动

全部流程表

接下来就来理解并扒一下其中的细节

什么是进程间通讯,为什么要搞一个Binder机制

一个进程空间分为 用户空间和内核空间(kernel)

用户空间:数据不可共享=不可共享空间

内核空间:数据可共享=可共享空间

所有进程共用1各内核空间

内核空间、用户空间、内核态、用户态

对于32位操作系统而言,它的寻址空间(虚拟地址空间)为4G。操作系统将虚拟地址空间划分为两部分、一部分内核空间(最高1G字节),一部分用户空间(3G字节)

内核态:当进程运行在内核空间,CPU执行任何指令,可以自由的访问任何有效地址

用户态:当进程运行在用户空间,只能范文映射其地址空间的页表项中规定的用户态下可访问页面的虚拟地址

为什么要区分内核空间与用户空间

CPU指令分为特权指令和非特权指令,对于危险的指令,比如清内存等,这种指令只允许操作系统及其相关模块使用,普通应用只能使用普通指令,所以Ring3级别被运行在用户态,Ring0级别被成为运行在内核态

用户空间与内核空间的交互

进程内两个空间的交互需要通过系统调用,主要函数

copy_from_user():将用户空间的数据拷贝到内核空间

copy_to_user():将内核空间的数据拷贝到用户空间

进程隔离&跨进程通讯(IPC)

进程间传递数据必须要经过内核空间而 发送到内核 和从内核拿出给另一个进程时需要两次数据的拷贝

Binder的优势

binder机制则只需要1次数据拷贝,它通过Binder驱动负责管理数据接受关村,原理是内存映射mmap系统调用、它连接两个进程,实现了mmap()系统调用,主要负责创建数据接收的缓冲空间&管理数据接收缓存

内存映射 mmap()

(memory map)含义

关联进程中的1个虚拟内存区域 & 1个磁盘上的对象,使二者存在映射关系

这样如果从缓冲区中取数据,就相当于读文件中的相应字节,而将数据存入缓冲区,就相当于写文件中的相应字节,这样不需要read和write也能直接之心I/O了

就是操作内存就可以操作磁盘中的对象了

示意图

如果多个进程虚拟内存区域和同1个共享对象,简历映射关系后,若1个进程对该虚拟区域进行写操作,那么对于也把共享对象映射到其自身虚拟内存区域的进程也是可见的

实现过程

mmap()的返回值是内存映射在用户空间的地址

该函数的作用 = 创建虚拟内存区域 + 与共享对象建立映射关系

/** * 函数原型 */

void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);

/** * 具体使用(用户进程调用mmap())

  * 下述代码即常见了一片大小 = MAP_SIZE的接收缓存区

  & 关联到共享对象中(即建立映射) */

fd = open("/dev/binder", O_RDWR);

mmap(NULL, MAP_SIZE, PROT_READ, MAP_PRIVATE, fd, 0);

  /** * 内部原理 *

  步骤1:创建虚拟内存区域 *

  步骤2:实现地址映射关系,即:进程的虚拟地址空间 ->> 共享对象

  * 注: * a. 此时,该虚拟地址并没有任何数据关联到文件中,仅仅只是建立映射关系

  * b. 当其中1个进程对虚拟内存写入数据时,则真正实现了数据的可见

  */

具体工作流程

Binder 模型

模型原理图

基于CS模型即Client-Server 模式

模型组成角色说明

角色作用备注

Client 进程使用服务 的进程Andoird 客户端 各种App

Server 进程提供服务 的进程服务端 System_server

ServiceManager进程管理Service查找和注册服务

(将字符形式的Binder名字 转化成Client中对该Binder 的引用)

init进程启动的进程,作为Binder IPC通讯的守护进程

功能类似路由器

Binder 驱动一种虚拟设备驱动,是连接Service进程、Client进程和ServiceManager进程的桥梁,它们之间的交互(使用open和ioctl文件操作函数)而非直接交互

1.传递进程间的数据:通过内存映射

2.实现线程控制:采用Binder的线程池,并由Binder驱动自身进行管理

Binder驱动持有每个Service进程在内核空间中的Binder实体,并给Client进程提供Binder实体的引用Proxy

模型原理步骤说明

简单总结

注册服务

系统Server服务进程在启动时会先把所有服务注册到ServiceManager进程

获取服务

Client进程发起请求传入获取的服务名-》Binder驱动将请求-》SeviceManager进程找到所需服务后通过Binder驱动返回给Client进程,此时返回的是代理对象 除非Client与Server在同一进程

使用服务

Binder驱动:为跨进程做准备实现内存映射(根据ServiceManager进程里的Service信息找到对应的Server进程,实现内核缓存区 和 Server 进程用户空间地址 同时映射到 同1个接收缓存区中)

Client进程:将参数写入Parcel对象中打包(序列化),通过BinderProxy的transact()将数据发送内核缓存区(此时Binder驱动由于存在映射关系相当于给到Binder驱动),等待Server返回replay,并挂起Client当前线程

Sever进程:收到驱动通知后,调用Binder对象的onTransact()将Parcel解包(反序列化)并执行要client要调用目标方法并返回replay,随后把replay返回给Client,此时Client线程被唤醒 完成了一次服务的使用

流程如下图

获取服务

Client获取服务时,会去ServiceManager获取服务名对应的IBinder,随后通过asInterface获取服务

static publicINotificationManagergetService()

    {

if (sService!= null) {

returnsService;

        }

IBinderb=ServiceManager.getService("notification");

sService=INotificationManager.Stub.asInterface(b);

returnsService;

    }

而asInterface会去获本地对象,否则就给到Binder代理类

    public static BookManager asInterface(IBinder binder){

        if (binder == null)

            return null;

        //寻找Binder本地对象

IInterface iin = binder.queryLocalInterface(DESCRIPTOR);

        if (iin != null && iin instanceof BookManager){

            return (BookManager) iin;

        }

        return new Proxy(binder);

/**

        * 如果server端(service所在的进程)和client端处于同一个进程 那么此时不需要跨进程通信 直接调用Stub的方法即可(Stub实例化时已经实现了具体的方法)

        * 反之,则返回proxy对象  从构造方法看 该对象持有着远程Binder引用  如果调用Stub.proxy接口 那么将会是IPC调用  即通过transact方法与服务端进行通信

        * 从字面意思看stub是服务端实现的存根(Stub工作在server进程),

        * proxy则是stub的代理 proxy工作在client进程

        */

    }

使用服务

步骤1: Client进程 将参数(整数a和b)发送到Server进程

// 1. Client进程 将需要传送的数据写入到Parcel对象中

// data = 数据 = 目标方法的参数(Client进程传进来的,此处就是整数a和b) + IInterface接口对象的标识符descriptor

  android.os.Parcel data = android.os.Parcel.obtain();

  data.writeInt(a);

  data.writeInt(b);

  data.writeInterfaceToken("add two int");;

  // 方法对象标识符让Server进程在Binder对象中根据"add two int"通过queryLocalIInterface()查找相应的IInterface对象(即Server创建的plus),Client进程需要调用的相加方法就在该对象中

  android.os.Parcel reply = android.os.Parcel.obtain();

  // reply:目标方法执行后的结果(此处是相加后的结果)

// 2. 通过 调用代理对象的transact() 将 上述数据发送到Binder驱动

  binderproxy.transact(Stub.add, data, reply, 0)

  // 参数说明:

    // 1. Stub.add:目标方法的标识符(Client进程 和 Server进程 自身约定,可为任意)

    // 2. data :上述的Parcel对象

    // 3. reply:返回结果

    // 0:可不管

// 注:在发送数据后,Client进程的该线程会暂时被挂起

// 所以,若Server进程执行的耗时操作,请不要使用主线程,以防止ANR

// 3. Binder驱动根据 代理对象 找到对应的真身Binder对象所在的Server 进程(系统自动执行)

// 4. Binder驱动把 数据 发送到Server 进程中,并通知Server 进程执行解包(系统自动执行)

步骤2:Server进程根据Client进要求 调用 目标方法(即加法函数)

// 1. 收到Binder驱动通知后,Server 进程通过回调Binder对象onTransact()进行数据解包 & 调用目标方法

  public class Stub extends Binder {

          // 复写onTransact()

          @Override

          boolean onTransact(int code, Parcel data, Parcel reply, int flags){

          // code即在transact()中约定的目标方法的标识符

          switch (code) {

                case Stub.add: {

                  // a. 解包Parcel中的数据

                      data.enforceInterface("add two int");

                        // a1. 解析目标方法对象的标识符

                      int  arg0  = data.readInt();

                      int  arg1  = data.readInt();

                      // a2. 获得目标方法的参数


                      // b. 根据"add two int"通过queryLocalIInterface()获取相应的IInterface对象(即Server创建的plus)的引用,通过该对象引用调用方法

                      int  result = this.queryLocalIInterface("add two int") .add( arg0,  arg1);


                        // c. 将计算结果写入到reply

                        reply.writeInt(result);


                        return true;

                  }

          }

      return super.onTransact(code, data, reply, flags);

      // 2. 将结算结果返回 到Binder驱动

步骤3:Server进程 将目标方法的结果(即加法后的结果)返回给Client进程

  // 1. Binder驱动根据 代理对象 沿原路 将结果返回 并通知Client进程获取返回结果

  // 2. 通过代理对象 接收结果(之前被挂起的线程被唤醒)

    binderproxy.transact(Stub.ADD, data, reply, 0);

    reply.readException();;

    result = reply.readInt();

          }

}

Binder驱动加载过程中有哪些重要步骤

优点

对比 Linux (Android基于Linux)上的其他进程通信方式(管道、消息队列、共享内存、

信号量、Socket),Binder 机制的优点有:

IBinder、BpBinder、BBinder、Stub、IInterface

名称描述

IBinder一个接口类,代表了一种跨进程通讯的能力,实现这个接口,这个对象就能跨进程传输

BpBinder代理Binder当clicnt调用service后,会得到一个BpXXXService,它将str参数打包到Parcel中,然后调用remote()->transact,remote()返回的就是一个BpBinder,所以实际上调用的是BpBinder的transact

BBinder服务端Binder,当Service接收到client请求后,会调用BBinder的taransact方法会调回子类实现的虚拟方法onTransact,该方法实现是在BnXXXservice中实现,所以BBinder就是接受传递过来的信息,解包数据,并调用XXXservice真正的实现

IInerfaceAIDL文件中定义的接口,表示Server进程对象具备哪些能力

Stub一个抽象类,其中由asInterface和onTransact关键函数组成

其中asInterface 传入binder对象,如果同一进程

序列化与反序列化

为什么要序列化与反序列化?

因为对象内的各种数据不能直接跨进程传递,所以需要将java对象转换成字节序列(序列化)等到了接收方便要从字节序列中恢复出Java对象(反序列化)

序列化有哪些好处?

实现了数据的持久化,通过序列化可以把数据永久的保存在硬盘上

Serializable与Parcelable的区别

SerializableParcelable

原理通过I/O读写存储再磁盘上,同各国反射解析出对象描述、属性的描述,以HandleTable 来解析存储信息,然后生成二进制、存储、传输主要用于Binder传输,将数据写入共享内存,其他进程通过Parcel可以从这块共享内存中读出字节流

存储介质使用I/O读写存储在硬盘上直接在内存中读取,内存中的结构是一块连续的内存,会根据需要的内存大小自动扩展

来源JavaAndroid

效率通过反射,需要大量的I/O操作自己实现封送和解封,效率更快

使用序列化:

要创建某些OutputStream对象

将其封装到ObjectOutputStream对象内

此后只需调用writeObject()即可完成对象的序列化

忘记关闭资源:objectOutputStream.close(), outputStream .close();

反序列化:

通过readObject()获取资源

通过IPC传递的对象都要继承于Parcelable接口,并实现 writeToParcel,它会获取对象的当前状态并将其写入 Parcel。为类添加CREATOR静态字段,该字段是实现Parcelable.Creator接口的对象。最后,创建声明 Parcelable 类的.aidl文件

Binder中ServiceManager的作用

ServiceManage使用客户端可以获取binder实例对象的引用

AIDL

什么是AIDL

AIDL是Android提供的接口定义语言,简化Binder的使用,会生成一个服务代理端对象的代理类,可以理解成服务端代码,与Client客户端中要链接的binder相关如服务连接asInterface、transact、序列化,以及支持IPC的目标方法等

使用步骤

(1)建立aidl的服务方:

a.建立一个含有activity的service,其实也可以直接建立一个service

b.建立aidl文件,声明接口以及方法,建立完成后ADT会自动生成一个aild接口的实现java文件,在gen下面.

c.建立service继承类,内部实现aidl的stub.且在onbind中进行返回.

b.AndroidManifest.xml文件中配置AIDL服务,需要注意的是<action>标签中android:name的属性值就是客户端要引用该服务的ID,也就是Intent类的参数值。

(2)建立客户断完成跨进程的调用.

a.建立android project内部会自动创建一个activity,在activity中bind服务.bind服务成功后,onServiceConnected将会回调返回给service给定的ibind.

b.onServiceConnected根据返回的内容,实现跨进程的调用.

参考文章:

https://blog.csdn.net/carson_ho/article/details/73560642

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容