前言
本文參考Carson_Ho的《圖文詳解 Android Binder跨進(jìn)程通信的原理》。
1 什么是Binder双藕?
說(shuō)到Binder,有些人可能會(huì)說(shuō)Binder是Android在解決進(jìn)程通信間通信的技術(shù)遂鹊,是一個(gè)類實(shí)現(xiàn)了Ibinder接口栋艳,是一個(gè)虛擬設(shè)備驅(qū)動(dòng)等等,其實(shí)這些說(shuō)法都沒(méi)有錯(cuò)戳气,如果把這些說(shuō)法歸結(jié)起來(lái)可以有以下的定義:
可以這樣說(shuō):站在不同的角度上链患,Binder有不同的定義。
2知識(shí)鋪墊
在Linux中將一個(gè)進(jìn)程空間分為兩部分:用戶空間和內(nèi)核控件瓶您。
用戶空間:用戶空間的數(shù)據(jù)不可以共享
內(nèi)核空間:內(nèi)核空間的數(shù)據(jù)可以共享
那么一個(gè)進(jìn)程中的兩部部分如何進(jìn)行通信呢?其實(shí)是有copy_from_user和copy_to_user來(lái)實(shí)現(xiàn)的麻捻。
copy_from_user:將用戶空間的數(shù)據(jù)拷貝到內(nèi)核空間
copy_to_user:將內(nèi)核空間中的數(shù)據(jù)拷貝到用戶空間
那么Linux中的進(jìn)程間通信的原理可以由下圖來(lái)表示
這里需要注意的是:所有的進(jìn)程共用一個(gè)內(nèi)核空間
2.1 進(jìn)程隔離和IPC
進(jìn)程隔離:為了保證進(jìn)程的獨(dú)立性&安全性,Android不允許進(jìn)程之間直接相互訪問(wèn)或通信呀袱,保證了了每一個(gè)是相互隔離贸毕,獨(dú)立的。
IPC:進(jìn)程間通信
那么IPC是如何實(shí)現(xiàn)的呢夜赵?
進(jìn)程間的通信原理如圖所示明棍,
1.發(fā)送進(jìn)程通過(guò)系統(tǒng)調(diào)用方法copy_from_user將需要傳遞的數(shù)據(jù)拷貝到內(nèi)核空間的緩存區(qū)中。
2.內(nèi)核服務(wù)程序喚醒接收進(jìn)程的接收線程寇僧,通過(guò)系統(tǒng)調(diào)用(copy_to_user)將內(nèi)核緩存區(qū)中的數(shù)據(jù)拷貝到接收進(jìn)程的用戶空間摊腋。
3.拷貝完成沸版,實(shí)現(xiàn)了進(jìn)程間的通信。
這里要說(shuō)明的有以下兩點(diǎn):
1.進(jìn)程間無(wú)法直接進(jìn)行數(shù)據(jù)的交互兴蒸,數(shù)據(jù)的交互需要通過(guò)內(nèi)核空間進(jìn)行视粮。
2.Linux的進(jìn)程間通信調(diào)用了兩次數(shù)據(jù)拷貝的系統(tǒng)調(diào)用。
3.接收數(shù)據(jù)的緩沖區(qū)大小由接收進(jìn)程決定类咧。
缺點(diǎn):
1.接收進(jìn)程的接收緩沖區(qū)大小不定馒铃。
2.效率低下,單向數(shù)據(jù)通信需要進(jìn)行兩次數(shù)據(jù)拷貝(用戶->內(nèi)核->用戶)
2.2 Binder模型
Android為了解決Linux中進(jìn)程間通信中的缺點(diǎn)痕惋,提出了Binder模型機(jī)制区宇,該模型機(jī)制的原理如下圖所示:
Binder模型屬于C/S模式,各部分的說(shuō)明如下:
角色 | 說(shuō)明 | 備注 |
---|---|---|
Client進(jìn)程 | 使用服務(wù)的進(jìn)程 | Andorid客戶端 |
Server進(jìn)程 | 提供服務(wù)的進(jìn)程 | 服務(wù)器端 |
ServiceManager | 管理服務(wù)進(jìn)程的注冊(cè)和查詢(將客戶端提供的Bidner字符串轉(zhuǎn)換為相應(yīng)服務(wù)的Bidner引用) | 相當(dāng)于一個(gè)DNS服務(wù)器 |
Binder驅(qū)動(dòng) | 虛擬的設(shè)備驅(qū)動(dòng)值戳,將Client進(jìn)程议谷,Server進(jìn)程和ServiceManager進(jìn)程聯(lián)系起來(lái) 具體作用:1傳遞進(jìn)程服務(wù)消息(服務(wù)進(jìn)程注冊(cè),客戶進(jìn)程獲取服務(wù))2.傳遞進(jìn)程間需要傳遞的數(shù)據(jù)堕虹,通過(guò)內(nèi)存映射3.實(shí)現(xiàn)線程控制:采用binder連接池卧晓,并由binder驅(qū)動(dòng)自身管理 | binder驅(qū)動(dòng)持有所有服務(wù)進(jìn)程在內(nèi)核進(jìn)程中的binder實(shí)體,在客戶進(jìn)程使用時(shí)將對(duì)應(yīng)的binder字符串轉(zhuǎn)換為binder引用赴捞。 |
在Binder模型中的重要角色就是Binder驅(qū)動(dòng)逼裆,它將其余的各個(gè)部分聯(lián)系起來(lái),形成了Bidner跨進(jìn)程通信模型赦政。那么Binder驅(qū)動(dòng)是如何工作的呢胜宇?
2.2.1 Binder驅(qū)動(dòng)
定義:
核心原理:
過(guò)程說(shuō)明:
1創(chuàng)建內(nèi)核空間的數(shù)據(jù)緩沖區(qū)
2并根據(jù)需要映射的接收進(jìn)程的信息通過(guò)地址映射實(shí)現(xiàn)內(nèi)核緩沖區(qū)和接收進(jìn)程用戶空間地址映射到同一個(gè)接收緩沖區(qū)(這時(shí)只是進(jìn)行接收緩沖區(qū)的創(chuàng)建和完成地址映射,并沒(méi)有進(jìn)行數(shù)據(jù)的讀寫操作)
3.發(fā)送進(jìn)程通過(guò)copy_from_user將需要傳輸?shù)臄?shù)據(jù)發(fā)送到內(nèi)核緩沖區(qū)中
4由于內(nèi)核緩沖區(qū)和接收進(jìn)程用戶空間地址映射到了同一個(gè)接收緩沖區(qū)恢着,相當(dāng)于將信息發(fā)送到了接收進(jìn)程用戶地址空間桐愉。即實(shí)現(xiàn)了進(jìn)程間通信。
優(yōu)點(diǎn):
1.傳輸效率高掰派,單項(xiàng)通信數(shù)據(jù)拷貝次數(shù)少(1次)从诲,內(nèi)核空間與用戶空間通過(guò)共享對(duì)象直接交互。
2.為接收進(jìn)程創(chuàng)建了大小不確定的接收緩沖區(qū)靡羡。
2.2.2Binder模型工作過(guò)程說(shuō)明
步驟 | 過(guò)程說(shuō)明 | 備注 |
---|---|---|
服務(wù)進(jìn)程注冊(cè)服務(wù) | 1.服務(wù)進(jìn)程向binder驅(qū)動(dòng)發(fā)送注冊(cè)服務(wù)請(qǐng)求2.binder驅(qū)動(dòng)將請(qǐng)求轉(zhuǎn)發(fā)到serviceManager3.serviceManager進(jìn)程添加服務(wù)進(jìn)程(注冊(cè)服務(wù)進(jìn)程) | 此時(shí)serviceManager擁有了服務(wù)進(jìn)程的信息 |
客戶進(jìn)程獲取服務(wù) | 1.客戶進(jìn)程向binder驅(qū)動(dòng)發(fā)送獲取服務(wù)請(qǐng)求2.binder驅(qū)動(dòng)將請(qǐng)求轉(zhuǎn)發(fā)給serviceManager3.serviceManager查找客戶進(jìn)程需要的服務(wù)進(jìn)程信息4.serviceManager將找到的服務(wù)進(jìn)程信息通過(guò)binder驅(qū)動(dòng)傳遞給客戶進(jìn)程系洛。 | 此時(shí)客戶進(jìn)程與服務(wù)進(jìn)程建立了聯(lián)系 |
客戶進(jìn)程使用服務(wù)(1) | binder驅(qū)動(dòng)為跨進(jìn)程調(diào)用做準(zhǔn)備(系統(tǒng)調(diào)用mmap) | 1.binder驅(qū)動(dòng)創(chuàng)建接收緩沖區(qū)2.通過(guò)serviceManager提供的服務(wù)進(jìn)程信息地址映射將服務(wù)進(jìn)程用戶空間地址與內(nèi)核緩沖區(qū)映射到同一個(gè)接收緩沖區(qū)(即Binder驅(qū)動(dòng)創(chuàng)建的接收緩沖區(qū)) |
客戶進(jìn)程使用服務(wù)(2) | 客戶進(jìn)程將參數(shù)傳遞到服務(wù)進(jìn)程 | 1.服務(wù)進(jìn)程調(diào)用copy_from_user將數(shù)據(jù)傳遞到內(nèi)核緩沖區(qū)(當(dāng)前進(jìn)程被掛起)2.內(nèi)核緩沖區(qū)與服務(wù)進(jìn)程用戶空間地址映射到同一個(gè)接收緩沖區(qū),相當(dāng)于參數(shù)被傳遞到了服務(wù)進(jìn)程的用戶空間3.binder驅(qū)動(dòng)通知服務(wù)進(jìn)程進(jìn)行解包 |
客戶進(jìn)程使用服務(wù)(3) | 服務(wù)進(jìn)程根據(jù)客戶進(jìn)程要求調(diào)用目標(biāo)方法 | 1.收到binder解包通知后亿眠,server從binder線程池中取出線程碎罚,進(jìn)行解包和方法調(diào)用2.將方法結(jié)果寫到自己的共享內(nèi)存中 |
客戶進(jìn)程使用服務(wù)(4) | 服務(wù)進(jìn)程將目標(biāo)方法的結(jié)果返回給客戶進(jìn)程 | 1.將結(jié)果寫入映射的用戶空間地址指向的內(nèi)存區(qū)域中2.由于內(nèi)核緩沖區(qū)與服務(wù)進(jìn)程用戶空間地址通過(guò)地址映射映射到同一個(gè)接收緩沖區(qū),相當(dāng)于目標(biāo)方法的結(jié)果傳遞到了內(nèi)核緩沖區(qū)纳像,3.Binder驅(qū)動(dòng)通知客戶進(jìn)程取出結(jié)果4.客戶進(jìn)程通過(guò)系統(tǒng)調(diào)用copy_to_user將內(nèi)核緩沖區(qū)的目標(biāo)結(jié)果拷貝到客戶進(jìn)程用戶空間 |
優(yōu)點(diǎn):
1.傳輸效率高荆烈,單項(xiàng)通信數(shù)據(jù)拷貝次數(shù)少(1次),內(nèi)核空間與用戶空間通過(guò)共享對(duì)象直接交互。
2.為接收進(jìn)程創(chuàng)建了大小不確定的接收緩沖區(qū)憔购。
2.3額外說(shuō)明:
(1)客戶進(jìn)程宫峦,服務(wù)進(jìn)程,ServiceManager進(jìn)程均屬于用戶空間不能直接相互通信玫鸟。Binder驅(qū)動(dòng)屬于內(nèi)核空間导绷,可以進(jìn)行用戶空間/內(nèi)核空間交互。
(2)Binder和ServiceManager屬于Android基礎(chǔ)架構(gòu)不需要開(kāi)發(fā)者去實(shí)現(xiàn)屎飘,開(kāi)發(fā)者需要關(guān)心的是自定義客戶進(jìn)程和服務(wù)進(jìn)程妥曲,借助基礎(chǔ)架構(gòu)就能實(shí)現(xiàn)跨進(jìn)程通信。
(3)服務(wù)進(jìn)程會(huì)創(chuàng)建多個(gè)線程來(lái)處理Binder請(qǐng)求(默認(rèn)16個(gè))钦购,Binder模型的線程管理 采用Binder驅(qū)動(dòng)的線程池檐盟,并由Binder驅(qū)動(dòng)自身進(jìn)行管理(不是服務(wù)進(jìn)程進(jìn)行管理)
Binder機(jī)制 在Android中的具體實(shí)現(xiàn)原理
Binder機(jī)制在Android中主要依靠Binder類進(jìn)行實(shí)現(xiàn),該類實(shí)現(xiàn)了IBinder接口押桃。
實(shí)例說(shuō)明:Client進(jìn)程 需要調(diào)用 Server進(jìn)程的加法函數(shù)(將整數(shù)a和b相加)
Client進(jìn)程提供相加的數(shù)字a和b給服務(wù)進(jìn)程
Server進(jìn)程負(fù)責(zé)將兩個(gè)數(shù)相加并將結(jié)果返回給Client進(jìn)程
1.Server進(jìn)程注冊(cè)服務(wù)
過(guò)程描述
Server進(jìn)程 通過(guò)Binder驅(qū)動(dòng) 向 Service Manager進(jìn)程 注冊(cè)服務(wù)
代碼實(shí)現(xiàn)
Server進(jìn)程 創(chuàng)建 一個(gè) Binder 對(duì)象
該Binder對(duì)象是Server進(jìn)程在Binder驅(qū)動(dòng)中的存在形式
該Binder對(duì)象保存著Server進(jìn)程和ServiceManager的信息(保存在內(nèi)核空間中)
當(dāng)客戶進(jìn)程獲取服務(wù)時(shí)葵萎,通過(guò)該Binder找到對(duì)應(yīng)的服務(wù)進(jìn)程
以下代碼是系統(tǒng)根據(jù)AIDL文件生成的接口代碼:
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: D:\\code\\BinderServerDemo\\app\\src\\main\\aidl\\com\\zhqy\\binderserverdemo\\AddInterface.aidl
*/
package com.zhqy.binderserverdemo;
// Declare any non-default types here with import statements
public interface AddInterface extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.zhqy.binderserverdemo.AddInterface {
private static final java.lang.String DESCRIPTOR = "com.zhqy.binderserverdemo.AddInterface";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.zhqy.binderserverdemo.AddInterface interface,
* generating a proxy if needed.
*/
public static com.zhqy.binderserverdemo.AddInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.zhqy.binderserverdemo.AddInterface))) {
return ((com.zhqy.binderserverdemo.AddInterface) iin);
}
return new com.zhqy.binderserverdemo.AddInterface.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_add: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.add(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.zhqy.binderserverdemo.AddInterface {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public int add(int a, int b) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(a);
_data.writeInt(b);
mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
public int add(int a, int b) throws android.os.RemoteException;
}
下面對(duì)相關(guān)方法進(jìn)行說(shuō)明:
DESCRIPTOR:Binder的唯一標(biāo)識(shí),一般用類名進(jìn)行標(biāo)識(shí)
asInterface(android.os.IBinder obj):用于將Binder對(duì)象轉(zhuǎn)換成客戶進(jìn)程需要的AIDL接口類型的對(duì)象唱凯,該方法區(qū)分所在的進(jìn)程羡忘,如果服務(wù)進(jìn)程和客戶進(jìn)程在同一個(gè)進(jìn)程中則返回服務(wù)端Stub對(duì)象本身,否則返回的是服務(wù)進(jìn)程Stub的代理類Stub.proxy對(duì)象磕昼。
asBinder:返回Binder對(duì)象卷雕。
onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags):這個(gè)方法在運(yùn)行在服務(wù)進(jìn)程的binder線程池中,當(dāng)客戶進(jìn)程發(fā)起獲取服務(wù)時(shí)票从,獲取服務(wù)的請(qǐng)求會(huì)由系統(tǒng)底層封裝后交給此方法執(zhí)行爽蝴。其中code表示調(diào)用的是哪一個(gè)目標(biāo)方法,data為執(zhí)行該方法所需要的參數(shù)(如果有的話),reply為執(zhí)行完該目標(biāo)方法時(shí)纫骑,如果有返回值的話將返回值寫入reply。返回值表示該方法是否調(diào)用成功九孩,如果返回false則客戶進(jìn)程調(diào)用失敗先馆。
注冊(cè)服務(wù)后,Binder驅(qū)動(dòng)持有 Server進(jìn)程創(chuàng)建的Binder實(shí)體躺彬。
proxy#add(int a, int b):該方法會(huì)在客戶進(jìn)程中調(diào)用煤墙,當(dāng)客戶端獲取服務(wù)時(shí),首先會(huì)創(chuàng)建輸入行Parcel對(duì)象_data和輸出項(xiàng)_reply和返回值對(duì)象_result宪拥,將遠(yuǎn)程過(guò)程調(diào)用(RPC)所需要的參數(shù)寫入_data中仿野,然后執(zhí)行transact方法進(jìn)行遠(yuǎn)程過(guò)程調(diào)用(客戶進(jìn)程被掛起),服務(wù)進(jìn)程執(zhí)行onTransact方法她君,當(dāng)服務(wù)進(jìn)程返回目標(biāo)方法執(zhí)行的結(jié)果后脚作,喚醒客戶進(jìn)程的transact方法,將服務(wù)進(jìn)程返回的執(zhí)行結(jié)果寫入_result,最后返回_result球涛。
2.客戶進(jìn)程獲取服務(wù)
過(guò)程:客戶進(jìn)程在使用服務(wù)進(jìn)程的目標(biāo)方法時(shí)劣针,須通過(guò)Binder驅(qū)動(dòng)從ServiceManager中獲取服務(wù)進(jìn)程信息
過(guò)程描述 | 代碼實(shí)現(xiàn) | 說(shuō)明 |
---|---|---|
客戶進(jìn)程向Binder驅(qū)動(dòng)發(fā)送獲取服務(wù)請(qǐng)求,并寫入需要獲得的服務(wù)名稱 | 從客戶進(jìn)程組件中執(zhí)行bindService | |
Binder驅(qū)動(dòng)將客戶進(jìn)程發(fā)送的請(qǐng)求轉(zhuǎn)發(fā)到ServiceManager亿扁,ServiceManager查找客戶進(jìn)程需要的服務(wù)進(jìn)程的Bidner實(shí)體的引用信息返回給binder驅(qū)動(dòng) | 服務(wù)進(jìn)程中的onBind執(zhí)行并返回binder的代理對(duì)象--BinderProxy | 該代理對(duì)象類即上面代碼所定義的代理類 |
Binder驅(qū)動(dòng)將代理對(duì)象返回給客戶進(jìn)程 | 客戶進(jìn)程調(diào)用ServiceConnection中的onServiceConnected接收代理對(duì)象轉(zhuǎn)化的AIDL接口對(duì)象 | 客戶進(jìn)程獲得是服務(wù)進(jìn)程binder類的代理類對(duì)象捺典,并不是真正的服務(wù)進(jìn)程的binder對(duì)象,客戶進(jìn)程通過(guò)操作該代理對(duì)象通過(guò)binder驅(qū)動(dòng)最終由服務(wù)進(jìn)程的bidner對(duì)象執(zhí)行操作 |
此時(shí)服務(wù)進(jìn)程與客戶進(jìn)程建立了聯(lián)系
3.客戶進(jìn)程調(diào)用服務(wù)
當(dāng)客戶進(jìn)程與服務(wù)進(jìn)程建立起聯(lián)系后从祝,客戶進(jìn)程通過(guò)操作服務(wù)進(jìn)程的代理對(duì)象類BinderProxy通過(guò)binder驅(qū)動(dòng)來(lái)執(zhí)行服務(wù)進(jìn)程中的相關(guān)方法
過(guò)程描述 | 代碼描述 | 說(shuō)明 |
---|---|---|
客戶進(jìn)程提供目標(biāo)方法的參數(shù)通過(guò)Binder驅(qū)動(dòng)發(fā)送到服務(wù)進(jìn)程進(jìn)行解包 | 客戶進(jìn)程執(zhí)行代理對(duì)象類的目標(biāo)方法(add(int a,int b)),首先創(chuàng)建輸出型parcel對(duì)象—_data,輸入型parcel對(duì)象_reply,結(jié)果_result(類型為AIDL允許的類型)襟己,將目標(biāo)方法需要的參數(shù)屑放入_data并執(zhí)行transact進(jìn)行遠(yuǎn)程過(guò)程調(diào)用(RPC),客戶進(jìn)程被掛起。Binder驅(qū)動(dòng)通知服務(wù)進(jìn)程進(jìn)行解包 | |
服務(wù)進(jìn)程接收Bidner驅(qū)動(dòng)的解包通知進(jìn)行解包并執(zhí)行目標(biāo)方法 | 服務(wù)進(jìn)程首先從進(jìn)程池中取出Binder對(duì)象并調(diào)用onTransact牍陌,首先根據(jù)code判定需要執(zhí)行那個(gè)方法擎浴,再?gòu)腳data中取出目標(biāo)方法需要的參數(shù)(如果有的話),然后執(zhí)行該目標(biāo)方法呐赡,將目標(biāo)方法的返回值寫入_result | |
服務(wù)進(jìn)程將目標(biāo)方法的返回值返回給客戶進(jìn)程 | 當(dāng)目標(biāo)方法執(zhí)行完后退客,會(huì)返回true通知binder驅(qū)動(dòng)方法調(diào)用成功,這是binder驅(qū)動(dòng)喚醒客戶進(jìn)程链嘀,從代理對(duì)象中獲取目標(biāo)方法的執(zhí)行結(jié)果并寫入_result萌狂,最終將_result進(jìn)行返回。這時(shí)客戶進(jìn)程就獲取到了服務(wù)進(jìn)程目標(biāo)方法執(zhí)行的結(jié)果怀泊。 |
整個(gè)過(guò)程如下圖所示: