Carson帶你學Android:全面剖析Binder跨進程通信原理


前言

  • 如果你接觸過 跨進程通信 (IPC)吐葱,那么你對Binder一定不陌生
  • 雖然 網(wǎng)上有很多介紹 Binder的文章摘刑,可是存在一些問題:淺顯的討論Binder機制 或 一味講解 Binder源碼志鞍、邏輯不清楚鼓蜒,最終導致的是讀者們還是無法形成一個完整的Binder概念
  • 本文采用 清晰的圖文講解方式,按照 大角度 -> 小角度 去分析Binder泻帮,即:
    1. 先從 機制肝陪、模型的角度 去分析 整個Binder跨進程通信機制的模型
    2. 再 從源碼實現(xiàn)角度,分析 BinderAndroid中的具體實現(xiàn)

從而全方位地介紹 Binder刑顺,希望你們會喜歡。

請盡量在PC端而不要在移動端看饲常,否則圖片可能看不清蹲堂。


目錄

目錄

1. Binder到底是什么?

  • 中文即 粘合劑贝淤,意思為粘合了兩個不同的進程

  • 網(wǎng)上有很多對Binder的定義柒竞,但都說不清楚:Binder是跨進程通信方式、它實現(xiàn)了IBinder接口播聪,是連接 ServiceManager的橋梁blabla朽基,估計大家都看暈了,沒法很好的理解

  • 我認為:對于Binder的定義离陶,在不同場景下其定義不同

定義

在本文的講解中稼虎,按照 大角度 -> 小角度 去分析Binder,即:

  • 先從 機制招刨、模型的角度 去分析 整個Binder跨進程通信機制的模型

其中霎俩,會詳細分析模型組成中的 Binder驅(qū)動

  • 再 從源碼實現(xiàn)角度,分析 BinderAndroid中的具體實現(xiàn)

從而全方位地介紹 Binder,希望你們會喜歡打却。


2. 知識儲備

在講解Binder前杉适,我們先了解一些Linux的基礎(chǔ)知識

2.1 進程空間劃分

  • 一個進程空間分為 用戶空間 & 內(nèi)核空間(Kernel),即把進程內(nèi) 用戶 & 內(nèi)核 隔離開來
  • 二者區(qū)別:
    1. 進程間柳击,用戶空間的數(shù)據(jù)不可共享猿推,所以用戶空間 = 不可共享空間
    2. 進程間,內(nèi)核空間的數(shù)據(jù)可共享捌肴,所以內(nèi)核空間 = 可共享空間

所有進程共用1個內(nèi)核空間

  • 進程內(nèi) 用戶空間 & 內(nèi)核空間 進行交互 需通過 系統(tǒng)調(diào)用蹬叭,主要通過函數(shù):
  1. copy_from_user():將用戶空間的數(shù)據(jù)拷貝到內(nèi)核空間
  2. copy_to_user():將內(nèi)核空間的數(shù)據(jù)拷貝到用戶空間
示意圖

2.2 進程隔離 & 跨進程通信( IPC )

  • 進程隔離
    為了保證 安全性 & 獨立性,一個進程 不能直接操作或者訪問另一個進程哭靖,即Android的進程是相互獨立具垫、隔離的

  • 跨進程通信( IPC
    即進程間需進行數(shù)據(jù)交互、通信

  • 跨進程通信的基本原理

示意圖

a. 而Binder的作用則是:連接 兩個進程试幽,實現(xiàn)了mmap()系統(tǒng)調(diào)用筝蚕,主要負責 創(chuàng)建數(shù)據(jù)接收的緩存空間 & 管理數(shù)據(jù)接收緩存
b. 注:傳統(tǒng)的跨進程通信需拷貝數(shù)據(jù)2次,但Binder機制只需1次铺坞,主要是使用到了內(nèi)存映射起宽,具體下面會詳細說明

2.5 內(nèi)存映射

具體請看文章:操作系統(tǒng):圖文詳解 內(nèi)存映射


3. Binder 跨進程通信機制 模型

3.1 模型原理圖

Binder 跨進程通信機制 模型 基于 Client - Server 模式

示意圖

3.2 模型組成角色說明

示意圖

此處重點講解 Binder驅(qū)動作用中的跨進程通信的原理:

  • 簡介
示意圖
  • 跨進程通信的核心原理

關(guān)于其核心原理:內(nèi)存映射,具體請看文章:操作系統(tǒng):圖文詳解 內(nèi)存映射

示意圖

3.3 模型原理步驟說明

示意圖

3.4 額外說明

說明1:Client進程济榨、Server進程 & Service Manager 進程之間的交互 都必須通過Binder驅(qū)動(使用 openioctl文件操作函數(shù))坯沪,而非直接交互

原因:

  1. Client進程、Server進程 & Service Manager進程屬于進程空間的用戶空間擒滑,不可進行進程間交互
  2. Binder驅(qū)動 屬于 進程空間的 內(nèi)核空間腐晾,可進行進程間 & 進程內(nèi)交互

所以,原理圖可表示為以下:

虛線表示并非直接交互

示意圖
說明2: Binder驅(qū)動 & Service Manager進程 屬于 Android基礎(chǔ)架構(gòu)(即系統(tǒng)已經(jīng)實現(xiàn)好了)丐一;而Client 進程 和 Server 進程 屬于Android應用層(需要開發(fā)者自己實現(xiàn))

所以藻糖,在進行跨進程通信時,開發(fā)者只需自定義Client & Server 進程 并 顯式使用上述3個步驟库车,最終借助 Android的基本架構(gòu)功能就可完成進程間通信

示意圖
說明3:Binder請求的線程管理
  • Server進程會創(chuàng)建很多線程來處理Binder請求
  • Binder模型的線程管理 采用Binder驅(qū)動的線程池巨柒,并由Binder驅(qū)動自身進行管理

而不是由Server進程來管理的

  • 一個進程的Binder線程數(shù)默認最大是16,超過的請求會被阻塞等待空閑的Binder線程柠衍。

所以洋满,在進程間通信時處理并發(fā)問題時,如使用ContentProvider時珍坊,它的CRUD(創(chuàng)建牺勾、檢索、更新和刪除)方法只能同時有16個線程同時工作


  • 至此阵漏,我相信大家對Binder 跨進程通信機制 模型 已經(jīng)有了一個非常清晰的定性認識
  • 下面禽最,我將通過一個實例腺怯,分析Binder跨進程通信機制 模型在 Android中的具體代碼實現(xiàn)方式

即分析 上述步驟在Android中具體是用代碼如何實現(xiàn)的


4. Binder機制 在Android中的具體實現(xiàn)原理

  • Binder機制在 Android中的實現(xiàn)主要依靠 Binder類,其實現(xiàn)了IBinder 接口

下面會詳細說明

  • 實例說明:Client進程 需要調(diào)用 Server進程的加法函數(shù)(將整數(shù)a和b相加)

即:

  1. Client進程 需要傳兩個整數(shù)給 Server進程
  2. Server進程 需要把相加后的結(jié)果 返回給Client進程
  • 具體步驟
    下面川无,我會根據(jù)Binder 跨進程通信機制 模型的步驟進行分析

步驟1:注冊服務

  • 過程描述
    Server進程 通過Binder驅(qū)動 向 Service Manager進程 注冊服務
  • 代碼實現(xiàn)
    Server進程 創(chuàng)建 一個 Binder 對象
  1. Binder 實體是 Server進程 在 Binder 驅(qū)動中的存在形式
  2. 該對象保存 ServerServiceManager 的信息(保存在內(nèi)核空間中)
  3. Binder 驅(qū)動通過 內(nèi)核空間的Binder 實體 找到用戶空間的Server對象
  • 代碼分析
    
    Binder binder = new Stub();
    // 步驟1:創(chuàng)建Binder對象 ->>分析1

    // 步驟2:創(chuàng)建 IInterface 接口類 的匿名類
    // 創(chuàng)建前呛占,需要預先定義 繼承了IInterface 接口的接口 -->分析3
    IInterface plus = new IPlus(){

          // 確定Client進程需要調(diào)用的方法
          public int add(int a,int b) {
               return a+b;
         }

          // 實現(xiàn)IInterface接口中唯一的方法
          public IBinder asBinder(){ 
                return null ;
           }
};
          // 步驟3
          binder.attachInterface(plus,"add two int");
         // 1. 將(add two int懦趋,plus)作為(key,value)對存入到Binder對象中的一個Map<String,IInterface>對象中
         // 2. 之后晾虑,Binder對象 可根據(jù)add two int通過queryLocalIInterface()獲得對應IInterface對象(即plus)的引用,可依靠該引用完成對請求方法的調(diào)用
        // 分析完畢仅叫,跳出


<-- 分析1:Stub類 -->
    public class Stub extends Binder {
    // 繼承自Binder類 ->>分析2

          // 復寫onTransact()
          @Override
          boolean onTransact(int code, Parcel data, Parcel reply, int flags){
          // 具體邏輯等到步驟3再具體講解帜篇,此處先跳過
          switch (code) { 
                case Stub.add: { 

                       data.enforceInterface("add two int"); 

                       int  arg0  = data.readInt();
                       int  arg1  = data.readInt();

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

                        reply.writeInt(result); 

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

}
// 回到上面的步驟1,繼續(xù)看步驟2

<-- 分析2:Binder 類 -->
 public class Binder implement IBinder{
    // Binder機制在Android中的實現(xiàn)主要依靠的是Binder類诫咱,其實現(xiàn)了IBinder接口
    // IBinder接口:定義了遠程操作對象的基本接口笙隙,代表了一種跨進程傳輸?shù)哪芰?    // 系統(tǒng)會為每個實現(xiàn)了IBinder接口的對象提供跨進程傳輸能力
    // 即Binder類對象具備了跨進程傳輸?shù)哪芰?
        void attachInterface(IInterface plus, String descriptor);
        // 作用:
          // 1. 將(descriptor坎缭,plus)作為(key,value)對存入到Binder對象中的一個Map<String,IInterface>對象中
          // 2. 之后竟痰,Binder對象 可根據(jù)descriptor通過queryLocalIInterface()獲得對應IInterface對象(即plus)的引用,可依靠該引用完成對請求方法的調(diào)用

        IInterface queryLocalInterface(Stringdescriptor) 掏呼;
        // 作用:根據(jù) 參數(shù) descriptor 查找相應的IInterface對象(即plus引用)

        boolean onTransact(int code, Parcel data, Parcel reply, int flags)坏快;
        // 定義:繼承自IBinder接口的
        // 作用:執(zhí)行Client進程所請求的目標方法(子類需要復寫)
        // 參數(shù)說明:
        // code:Client進程請求方法標識符。即Server進程根據(jù)該標識確定所請求的目標方法
        // data:目標方法的參數(shù)憎夷。(Client進程傳進來的莽鸿,此處就是整數(shù)a和b)
        // reply:目標方法執(zhí)行后的結(jié)果(返回給Client進程)
         // 注:運行在Server進程的Binder線程池中;當Client進程發(fā)起遠程請求時拾给,遠程請求會要求系統(tǒng)底層執(zhí)行回調(diào)該方法

        final class BinderProxy implements IBinder {
         // 即Server進程創(chuàng)建的Binder對象的代理對象類
         // 該類屬于Binder的內(nèi)部類
        }
        // 回到分析1原處
}

<-- 分析3:IInterface接口實現(xiàn)類 -->

 public interface IPlus extends IInterface {
          // 繼承自IInterface接口->>分析4
          // 定義需要實現(xiàn)的接口方法祥得,即Client進程需要調(diào)用的方法
         public int add(int a,int b);
// 返回步驟2
}

<-- 分析4:IInterface接口類 -->
// 進程間通信定義的通用接口
// 通過定義接口,然后再服務端實現(xiàn)接口蒋得、客戶端調(diào)用接口级及,就可實現(xiàn)跨進程通信。
public interface IInterface
{
    // 只有一個方法:返回當前接口關(guān)聯(lián)的 Binder 對象窄锅。
    public IBinder asBinder();
}
  // 回到分析3原處

注冊服務后,Binder驅(qū)動持有 Server進程創(chuàng)建的Binder實體

步驟2:獲取服務

  • Client進程 使用 某個 service前(此處是 相加函數(shù))缰雇,須 通過Binder驅(qū)動 向 ServiceManager進程 獲取相應的Service信息
  • 具體代碼實現(xiàn)過程如下:
示意圖

此時入偷,Client進程與 Server進程已經(jīng)建立了連接

步驟3:使用服務

Client進程 根據(jù)獲取到的 Service信息(Binder代理對象),通過Binder驅(qū)動 建立與 該Service所在Server進程通信的鏈路械哟,并開始使用服務

  • 過程描述

    1. Client進程 將參數(shù)(整數(shù)a和b)發(fā)送到Server進程
    2. Server進程 根據(jù)Client進程要求調(diào)用 目標方法(即加法函數(shù))
    3. Server進程 將目標方法的結(jié)果(即加法后的結(jié)果)返回給Client進程
  • 代碼實現(xiàn)過程

步驟1: Client進程 將參數(shù)(整數(shù)a和b)發(fā)送到Server進程

// 1. Client進程 將需要傳送的數(shù)據(jù)寫入到Parcel對象中
// data = 數(shù)據(jù) = 目標方法的參數(shù)(Client進程傳進來的疏之,此處就是整數(shù)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對象中根據(jù)"add two int"通過queryLocalIInterface()查找相應的IInterface對象(即Server創(chuàng)建的plus)暇咆,Client進程需要調(diào)用的相加方法就在該對象中

  android.os.Parcel reply = android.os.Parcel.obtain();
  // reply:目標方法執(zhí)行后的結(jié)果(此處是相加后的結(jié)果)

// 2. 通過 調(diào)用代理對象的transact() 將 上述數(shù)據(jù)發(fā)送到Binder驅(qū)動
  binderproxy.transact(Stub.add, data, reply, 0)
  // 參數(shù)說明:
    // 1. Stub.add:目標方法的標識符(Client進程 和 Server進程 自身約定锋爪,可為任意)
    // 2. data :上述的Parcel對象
    // 3. reply:返回結(jié)果
    // 0:可不管

// 注:在發(fā)送數(shù)據(jù)后丙曙,Client進程的該線程會暫時被掛起
// 所以,若Server進程執(zhí)行的耗時操作其骄,請不要使用主線程亏镰,以防止ANR


// 3. Binder驅(qū)動根據(jù) 代理對象 找到對應的真身Binder對象所在的Server 進程(系統(tǒng)自動執(zhí)行)
// 4. Binder驅(qū)動把 數(shù)據(jù) 發(fā)送到Server 進程中,并通知Server 進程執(zhí)行解包(系統(tǒng)自動執(zhí)行)

步驟2:Server進程根據(jù)Client進要求 調(diào)用 目標方法(即加法函數(shù))

// 1. 收到Binder驅(qū)動通知后拯爽,Server 進程通過回調(diào)Binder對象onTransact()進行數(shù)據(jù)解包 & 調(diào)用目標方法
  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中的數(shù)據(jù)
                       data.enforceInterface("add two int"); 
                        // a1. 解析目標方法對象的標識符

                       int  arg0  = data.readInt();
                       int  arg1  = data.readInt();
                       // a2. 獲得目標方法的參數(shù)
                      
                       // b. 根據(jù)"add two int"通過queryLocalIInterface()獲取相應的IInterface對象(即Server創(chuàng)建的plus)的引用索抓,通過該對象引用調(diào)用方法
                       int  result = this.queryLocalIInterface("add two int") .add( arg0,  arg1); 
                      
                        // c. 將計算結(jié)果寫入到reply
                        reply.writeInt(result); 
                        
                        return true; 
                  }
           } 
      return super.onTransact(code, data, reply, flags); 
      // 2. 將結(jié)算結(jié)果返回 到Binder驅(qū)動


步驟3:Server進程 將目標方法的結(jié)果(即加法后的結(jié)果)返回給Client進程

  // 1. Binder驅(qū)動根據(jù) 代理對象 沿原路 將結(jié)果返回 并通知Client進程獲取返回結(jié)果
  // 2. 通過代理對象 接收結(jié)果(之前被掛起的線程被喚醒)

    binderproxy.transact(Stub.ADD, data, reply, 0);
    reply.readException();毯炮;
    result = reply.readInt()逼肯;
          }
}
  • 總結(jié)
    下面,我用一個原理圖 & 流程圖來總結(jié)步驟3的內(nèi)容
原理圖
流程圖

5. 優(yōu)點

對比 LinuxAndroid基于Linux)上的其他進程通信方式(管道桃煎、消息隊列篮幢、共享內(nèi)存、
信號量为迈、Socket)三椿,Binder 機制的優(yōu)點有:

示意圖


6. 總結(jié)

  • 本文主要詳細講解 跨進程通信模型 Binder機制 ,總結(jié)如下:
定義

特別地曲尸,對于從模型結(jié)構(gòu)組成的Binder驅(qū)動來說:

示意圖
  • 整個Binder模型的原理步驟 & 源碼分析
示意圖

Carson帶你學Android:頁面活動-Activity
Carson帶你學Android:廣播-BroadcastReceiver
Carson帶你學Android:服務-Service
Carson帶你學Android:內(nèi)存承載器-ContentProvider


歡迎關(guān)注Carson_Ho的簡書

不定期分享關(guān)于安卓開發(fā)的干貨另患,追求短纽乱、平、快昆箕,但卻不缺深度鸦列。


請點贊!因為你的鼓勵是我寫作的最大動力鹏倘!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末薯嗤,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子纤泵,更是在濱河造成了極大的恐慌骆姐,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件捏题,死亡現(xiàn)場離奇詭異玻褪,居然都是意外死亡,警方通過查閱死者的電腦和手機公荧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進店門带射,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人循狰,你說我怎么就攤上這事窟社∪祝” “怎么了?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵灿里,是天一觀的道長关炼。 經(jīng)常有香客問我,道長钠四,這世上最難降的妖魔是什么盗扒? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮缀去,結(jié)果婚禮上侣灶,老公的妹妹穿的比我還像新娘。我一直安慰自己缕碎,他們只是感情好褥影,可當我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著咏雌,像睡著了一般凡怎。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上赊抖,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天统倒,我揣著相機與錄音,去河邊找鬼氛雪。 笑死房匆,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的报亩。 我是一名探鬼主播浴鸿,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼弦追!你這毒婦竟也來了岳链?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤劲件,失蹤者是張志新(化名)和其女友劉穎掸哑,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體零远,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡苗分,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了遍烦。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片俭嘁。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡躺枕,死狀恐怖服猪,靈堂內(nèi)的尸體忽然破棺而出供填,到底是詐尸還是另有隱情,我是刑警寧澤罢猪,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布近她,位于F島的核電站,受9級特大地震影響膳帕,放射性物質(zhì)發(fā)生泄漏粘捎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一危彩、第九天 我趴在偏房一處隱蔽的房頂上張望攒磨。 院中可真熱鬧,春花似錦汤徽、人聲如沸娩缰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽拼坎。三九已至,卻和暖如春完疫,著一層夾襖步出監(jiān)牢的瞬間泰鸡,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工壳鹤, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留盛龄,地道東北人。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓器虾,卻偏偏與公主長得像讯嫂,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子兆沙,可洞房花燭夜當晚...
    茶點故事閱讀 42,877評論 2 345

推薦閱讀更多精彩內(nèi)容