Binder機制總結

在Linux系統裏面,進程之間是相互隔離的,也就是說進程之間的各個數據是互相獨立,互不影響,而若是一個進程崩潰了,也不會影響到另外一個進程。node

Android系統其底層是採用Linux做爲基底,上層採用包含虛擬機的Java層以及Native層,經過系統調用(Syscall)連通系統的內核空間與用戶空間。用戶空間主要採用C++和Java代碼,經過JNI技術打通用戶空間的Java層和Native層(C++/C)。linux

每一個Android的進程,只能運行在本身進程所擁有的虛擬地址空間。對於用戶空間,不一樣進程之間彼此是不能共享的,而內核空間倒是可共享的。Client進程向Server進程通訊,偏偏是利用進程間可共享的內核內存空間來完成底層通訊工做的,Client端與Server端進程每每採用ioctl等方法跟內核空間的驅動進行交互。真正通訊的核心環節仍是在Binder Driver。git

用戶空間/內核空間:

Linux Kernel是操做系統的核心,獨立於普通的應用程序,能夠訪問受保護的內存空間,也有訪問底層硬件設備的全部權限。緩存

對於Kernel這麼一個高安全級別的東西,顯然是不允許其它的應用程序隨便調用或訪問的,因此須要對Kernel提供必定的保護機制,這個保護機制用來告訴那些應用程序,你只能夠訪問某些許可的資源,不準可的資源是拒絕被訪問的,因而就把Kernel和上層的應用程序抽像的隔離開,分別稱之爲Kernel Space和User Space。安全

用戶空間訪問內核空間的惟一方式就是系統調用;經過這個統一入口接口,全部的資源訪問都是在內核的控制下執行,以避免致使對用戶程序對系統資源的越權訪問,從而保障了系統的安全和穩定。網絡

爲何要使用Binder

Linux現有的全部進程間IPC方式:數據結構

  1. 管道:在建立時分配一個page大小的內存,緩存區大小比較有限;
  2. 消息隊列:信息複製兩次,額外的CPU消耗;不合適頻繁或信息量大的通訊;
  3. 共享內存:無須複製,共享緩衝區直接付附加到進程虛擬地址空間,速度快;但進程間的同步問題操做系統沒法實現,必須各進程利用同步工具解決; 4.套接字:做爲更通用的接口,傳輸效率低,主要用於不通機器或跨網絡的通訊;
  4. 信號量:常做爲一種鎖機制,防止某進程正在訪問共享資源時,其餘進程也訪問該資源。所以,主要做爲進程間以及同一進程內不一樣線程之間的同步手段。
  5. 信號: 不適用於信息交換,更適用於進程中斷控制,好比非法內存訪問,殺死某個進程等;

Android使用的Linux內核擁有着很是多的跨進程通訊機制,好比管道,System V,Socket等;爲何還須要單獨搞一個Binder出來呢?主要有兩點,性能和安全。在移動設備上,普遍地使用跨進程通訊確定對通訊機制自己提出了嚴格的要求;Binder相對出傳統的Socket方式,更加高效;另外,傳統的進程通訊方式對於通訊雙方的身份並無作出嚴格的驗證,只有在上層協議上進行架設;好比Socket通訊ip地址是客戶端手動填入的,均可以進行僞造;而Binder機制從協議自己就支持對通訊雙方作身份校檢,於是大大提高了安全性。這個也是Android權限模型的基礎。架構

爲何 Android 要採用 Binder 做爲 IPC 機制?併發

Binder是什麼

  • 一般意義下,Binder指的是一種通訊機制;咱們說AIDL使用Binder進行通訊,指的就是Binder這種IPC機制。
  • 對於Server進程來講,Binder指的是Binder本地對象
  • 對於Client來講,Binder指的是Binder代理對象,它只是Binder本地對象的一個遠程代理;對這個Binder代理對象的操做,會經過驅動最終轉發到Binder本地對象上去完成;對於一個擁有Binder對象的使用者而言,它無須關心這是一個Binder代理對象仍是Binder本地對象;對於代理對象的操做和對本地對象的操做對它來講沒有區別。
  • 對於傳輸過程而言,Binder是能夠進行跨進程傳遞的對象;Binder驅動會對具備跨進程傳遞能力的對象作特殊處理:自動完成代理對象和本地對象的轉換。在驅動中,Binder本地對象的表明是一個叫作binder_node的數據結構,Binder代理對象是用binder_ref表明的

Binder架構

binder在framework層,採用JNI技術來調用native(C/C++)層的binder架構,從而爲上層應用程序提供服務。在native層中,binder是C/S架構,分爲Bp端(BpBinder - Client)和Bn端(BBinder - Server)。app

  • 圖中紅色表明整個framework層binder架構(C/S架構);Binder類表明Server端,BinderProxy類代碼Client端;
  • 圖中藍色表明Native層Binder架構相關組件,分爲Bp端(BpBinder - Client)和Bn端(BBinder - Server);
  • Binder 在 framework 層進行了封裝,經過 JNI 技術調用 Native(C/C++)層的 Binder 架構。
  • Binder 在 Native 層以 ioctl 的方式與 Binder 驅動通信。

上層framework層的Binder邏輯是創建在Native層架構基礎之上的,核心邏輯都是交予Native層方法來處理。也只有真正理解了Native層Binder架構,才能算掌握的Binder。

Client進程只不過是持有了Server端的代理;代理對象協助驅動完成了跨進程通訊。

native層的Binder通訊採用C/S架構,包含client、server、serviceManager、Binder驅動,client / server / serviceManager 位於用戶空間,Binder驅動位於內核空間。

  • Native層的ServiceManager(C++),用於管理系統中的各類服務,是整個Binder通訊機制的大管家。

  • BpBinder(客戶端)BBinder(服務端) 都是從IBinder類中派生而來的務。

    client端:BpBinder.transact()來發送事務請求;
      server端:BBinder.onTransact()會接收到相應事務。
    複製代碼

Binder原理

Native層的ServiceManager是整個Binder通訊機制的大管家,是Android進程間通訊機制Binder的守護進程, 要掌握Binder機制,首先須要瞭解系統是如何首次啓動ServiceManager。當Service Manager啓動以後,Client端和Server端通訊時都須要先獲取Service Manager接口,才能開始通訊服務。

圖中的Client、Server、ServiceManager之間交互都是虛線表示,是因爲它們彼此之間不是直接交互的,而是都經過與Binder Driver進行交互的,從而實現IPC通訊方式。其中Binder驅動位於內核空間,Client、Server、ServiceManager位於用戶空間。

Client、Server、ServiceManager都位於不一樣的進程之中,是基於Binder機制通訊,那麼一樣也是C/S架構,則圖中的3大步驟都有相應的Client端與Server端。這3大過程每一次都是一個完整的Binder IPC過程。

一、註冊服務:首先AMS註冊到ServiceManager。該過程:AMS所在進程(system_server)是客戶端,ServiceManager是服務端。

二、獲取服務:Client進程使用AMS前,須先向ServiceManager中獲取AMS的代理類AMP。該過程:AMP所在進程(app process)是客戶端,ServiceManager是服務端。

三、使用服務: app進程根據獲得的代理類AMP,即可以直接與AMS所在進程交互。該過程:AMP所在進程(app process)是客戶端,AMS所在進程(system_server)是服務端。

  • 首先須要註冊服務端,只有註冊了服務端,客戶端纔有通信的目標,服務端經過 ServiceManager 註冊服務,註冊的過程就是向 Binder 驅動的全局鏈表 binder_procs 中插入服務端的信息(binder_proc 結構體,每一個 binder_proc 結構體中都有 todo 任務隊列),而後向 ServiceManager 的 svcinfo 列表中緩存一下注冊的服務。

  • 有了服務端,客戶端就能夠跟服務端通信了,通信以前須要先獲取到服務,拿到服務的代理,也能夠理解爲引用。好比下面的代碼://獲取WindowManager服務引用

    WindowManager wm = (WindowManager)getSystemService(getApplication().WINDOW_SERVICE);

    獲取服務端的方式就是經過 ServiceManager 向 svcinfo 列表中查詢一下返回服務端的代理,svcinfo 列表就是全部已註冊服務的通信錄,保存了全部註冊的服務信息。

  • 有了服務端的引用咱們就能夠向服務端發送請求了,經過 BinderProxy 將咱們的請求參數發送給 ServiceManager,經過共享內存的方式使用內核方法 copy_from_user() 將咱們的參數先拷貝到內核空間,這時咱們的客戶端進入等待狀態,而後 Binder 驅動向服務端的 todo 隊列裏面插入一條事務,執行完以後把執行結果經過 copy_to_user() 將內核的結果拷貝到用戶空間(這裏只是執行了拷貝命令,並無拷貝數據,binder只進行一次拷貝),喚醒等待的客戶端並把結果響應回來,這樣就完成了一次通信。

Binder通訊簡述

前面說過Binder機制的3大步驟(註冊服務、獲取服務、使用服務)都有相應的client端和server端。

Client進程經過RPC與Server通訊,能夠簡單地劃分爲三層,驅動層、IPC層、業務層。demo()即是Client端和Server共同協商好的統一方法;handle、RPC數據、代碼、協議這4項組成了IPC層的數據,經過IPC層進行數據傳輸;而真正在Client和Server兩端創建通訊的基礎設施即是Binder Driver。

例如,當名爲BatteryStatsService的Server向ServiceManager註冊服務的過程當中,IPC層的數據組成爲:Handle=0,RPC代碼爲ADD_SERVICE_TRANSACTION,RPC數據爲BatteryStatsService,Binder協議爲BC_TRANSACTION。

Binder通訊協議

Binder協議包含在IPC數據中,分爲兩類:

  • BINDER_COMMAND_PROTOCOL:binder請求碼,以」BC_「開頭,簡稱BC碼,用於從IPC層傳遞到Binder Driver層;

  • BINDER_RETURN_PROTOCOL :binder響應碼,以」BR_「開頭,簡稱BR碼,用於從Binder Driver層傳遞到IPC層;

Binder IPC通訊至少是兩個進程的交互:

  • client進程執行binder_thread_write,根據BC_XXX命令,生成相應的binder_work;

  • server進程執行binder_thread_read,根據binder_work.type類型,生成BR_XXX,發送到用戶空間處理。

Binder通訊過程

其中binder_work.type共有6種類型:

  • BINDER_WORK_TRANSACTION //最多見類型
  • BINDER_WORK_TRANSACTION_COMPLETE
  • BINDER_WORK_NODE
  • BINDER_WORK_DEAD_BINDER
  • BINDER_WORK_DEAD_BINDER_AND_CLEAR
  • BINDER_WORK_CLEAR_DEATH_NOTIFICATION

一次完整的通信模型以下:

  • BC碼,用於從IPC層傳遞到Binder Driver層;
  • BR碼,用於從Binder Driver層傳遞到IPC層;

oneway與非oneway: 都是須要等待Binder Driver的迴應消息BR_TRANSACTION_COMPLETE。 主要區別在於oneway的通訊收到BR_TRANSACTION_COMPLETE則返回,而不會再等待BR_REPLY消息的到來。另外,oneway的binder IPC則接收端沒法獲取對方的pid。

Binder內存模型

虛擬進程地址空間(vm_area_struct)和虛擬內核地址空間(vm_struct)都映射到同一塊物理內存空間。當Client端與Server端發送數據時,Client(做爲數據發送端)先從本身的進程空間把IPC通訊數據copy_from_user拷貝到內核空間,而Server端(做爲數據接收端)與內核共享數據,再也不須要拷貝數據,而是經過內存地址空間的偏移量,便可獲悉內存地址,整個過程只發生一次內存拷貝。通常地作法,須要Client端進程空間拷貝到內核空間,再由內核空間拷貝到Server進程空間,會發生兩次拷貝。

對於進程和內核虛擬地址映射到同一個物理內存的操做是發生在數據接收端,而數據發送端仍是須要將用戶態的數據複製到內核態。到此,可能有讀者會好奇,爲什麼不直接讓發送端和接收端直接映射到同一個物理空間,那樣就連一次複製的操做都不須要了,0次複製操做那就與Linux標準內核的共享內存的IPC機制沒有區別了,對於共享內存雖然效率高,可是對於多進程的同步問題比較複雜,而管道/消息隊列等IPC須要複製2兩次,效率較低。這裏就不先展開討論Linux現有的各類IPC機制跟Binder的詳細對比,總之Android選擇Binder的基於速度和安全性的考慮。

下面這圖是從Binder在進程間數據通訊的流程圖,從圖中更能明瞭Binder的內存轉移關係。

Binder線程池建立

Android系統啓動完成後,ActivityManager、PackageManager等各大服務都運行在system_server進程裏,app進程若是須要使用系統服務須要經過binder,來完成進程之間的通訊。但無論是system_server進程,仍是app進程,都是經過zygote fork 出來的,而這個fork的過程,也就是建立新進程時會執行onZygoteInit(),以啓動binder線程池。

Binder線程的建立:在其所在進程的建立過程當中,Binder線程也隨之產生。Java層進程的建立都是經過Process.start()方法,向Zygote進程發出建立進程的socket消息,Zygote收到消息後會調用Zygote.forkAndSpecialize()來fork出新進程,在新進程中會調用到RuntimeInit.nativeZygoteInit方法,該方法通過jni映射,最終會調用到app_main.cpp中的onZygoteInit()中。

每一個應用進程只容許啓動一個binder線程池,且本次建立的是binder主線程(isMain=true),而且主線程是不會退出的;其他binder線程池中的線程都是由Binder驅動根據IPC通訊需求來控制建立的,當線程執行binder_thread_read的過程當中,發現當前沒有空閒線程,且沒有達到上限,則建立新的binder線程。

每次由Zygote fork出新進程的過程當中,伴隨着建立binder線程池,調用spawnPooledThread來建立binder主線程。當線程執行binder_thread_read的過程當中,發現當前沒有空閒線程,沒有請求建立線程,且沒有達到上限,則建立新的binder線程。

Binder進程與線程

對於底層Binder驅動,經過binder_procs鏈表記錄全部建立的binder_proc結構體,binder驅動層的每個binder_proc結構體都與用戶空間的一個用於binder通訊的進程一一對應,且每一個進程有且只有一個ProcessState對象,這是經過單例模式來保證的。在每一個進程中能夠有不少個線程,每一個線程對應一個IPCThreadState對象,IPCThreadState對象也是單例模式,即一個線程對應一個IPCThreadState對象,在Binder驅動層也有與之相對應的結構,那就是Binder_thread結構體。在binder_proc結構體中經過成員變量rb_root threads,來記錄當前進程內全部的binder_thread。

Binder線程池:每一個Server進程在啓動時會建立一個binder線程池,並向其中註冊一個Binder線程;以後Server進程也能夠向binder線程池註冊新的線程,或者Binder驅動在探測到沒有空閒binder線程時會主動向Server進程註冊新的的binder線程。對於一個Server進程有一個最大Binder線程數限制,默認爲16個binder線程。對於全部Client端進程的binder請求都是交由Server端進程的binder線程來處理的。

Binder傳輸過程

Binder IPC機制,就是指在進程間傳輸數據(binder_transaction_data),一次數據的傳輸,稱爲事務(binder_transaction)。對於多個不一樣進程向同一個進程發送事務時,這個同一個進程或線程的事務須要串行執行,在Binder驅動中爲binder_proc和binder_thread都有todo隊列。

也就是說對於進程間的通訊,就是發送端把binder_transaction節點,插入到目標進程或其子線程的todo隊列中,等目標進程或線程不斷循環地從todo隊列中取出數據並進行相應的操做。

在Binder驅動層,每一個接收端進程都有一個todo隊列,用於保存發送端進程發送過來的binder請求,這類請求能夠由接收端進程的任意一個空閒的binder線程處理;接收端進程存在一個或多個binder線程,在每一個binder線程裏都有一個todo隊列,也是用於保存發送端進程發送過來的binder請求,這類請求只能由當前binder線程來處理。binder線程在空閒時進入可中斷的休眠狀態,當本身的todo隊列或所屬進程的todo隊列有新的請求到來時便會喚醒,若是是由所需進程喚醒的,那麼進程會讓其中一個線程處理響應的請求,其餘線程再次進入休眠狀態。

Binder死亡回調

死亡通知是爲了讓Bp端(客戶端進程)進能知曉Bn端(服務端進程)的生死狀況,當Bn端進程死亡後能通知到Bp端。

Binder驅動

Binder Driver是Android專有的,但底層的驅動架構與Linux驅動是同樣。運行在內核空間的,負責各個用戶進程經過Binder進行通訊的內核模塊。

  • 將misc設備(字符驅動設備)註冊在設備目錄/dev下,做爲虛擬字符設備,用戶經過dev/binder訪問它;
  • 但它並無直接操做硬件,只是對設備內存的處理。
  • 工做在內核態,提供了驅動設備的初始化(binder_init)、打開(binder_open)、映射(binder_mmap)、數據操做(binder_ioctl)等標準文件操做。

用戶態的程序調用Kernel層驅動是須要陷入內核態,進行系統調用(syscall),好比打開Binder驅動方法的調用鏈爲: open-> __open() -> binder_open()。當用戶空間調用open()/mmap()/ioctl()方法,最終會調用binder驅動的binder_open()/binder_mmap()/binder_ioctl(),從用戶態進入內核態。

binder核心方法:

  • binder_init :註冊misc設備,建立dev/binder設備節點

  • binder_open *:打開binder驅動設備,獲取Binder Driver的文件描述符。建立binder_proc對象,並把當前進程等信息保存到binder_proc對象,該對象管理IPC所需的各類信息並擁有其餘結構體的根結構體;再把binder_proc對象保存到文件指針filp,以及把binder_proc加入到全局鏈表binder_procs。

  • binder_mmap :首先在內核虛擬地址空間,申請一塊與用戶虛擬內存相同大小的內存;而後再申請1個page大小的物理內存,再將同一塊物理內存分別映射到內核虛擬地址空間和用戶虛擬內存空間,從而實現了用戶空間的Buffer和內核空間的Buffer同步操做的功能。

  • binder_ioctl :負責在兩個進程間收發IPC數據和IPC reply數據。

serviceManager

ServiceManager是Binder IPC通訊過程當中的守護進程,是由init進程經過解析init.rc文件而建立的,其所對應的可執行程序/system/bin/servicemanager,進程名爲/system/bin/servicemanager。servicemanager的核心工做就是註冊服務和查詢服務。

ServiceManger集中管理系統內的全部服務,經過權限控制進程是否有權註冊服務,經過字符串名稱來查找對應的Service; 因爲ServiceManger進程跟全部向其註冊的服務創建死亡通知, 那麼當服務所在進程死亡後, 都將告知ServiceManager. 每一個Client經過查詢ServiceManager可獲取Server進程的狀況,下降全部Client進程直接檢測會致使負載太重。

  • ServiceManager 分爲 framework 層和 native 層,framework 層只是對 native 層進行了封裝方便調用,圖上展現的是 native 層的 ServiceManager 啓動過程。

ServiceManager的啓動流程

ServiceManager#main()

一、經過系統調用陷入內核,打開Binder設備驅動,在Binder驅動層建立一個binder_proc對象,同時放入全局鏈表binder_procs,再經過ioctl()檢驗當前binder版本與Binder驅動層的版本是否一致;調用mmap()進行內存映射,同理mmap()方法通過系統調用,對應於Binder驅動層的binder_mmap()方法,該方法會在Binder驅動層建立Binder_buffer對象,並放入當前binder_proc的proc->buffers鏈表。

二、註冊成爲binder服務的大管家,通知binder驅動使其成爲守護進程:binder_become_context_manager;

三、驗證selinux權限,判斷進程是否有權註冊或查看指定服務;

四、進入無限循環,處理client端發來的請求:binder_loop;

ServiceManager的獲取過程

當進程註冊服務(addService)或 獲取服務(getService)的過程以前,都須要先調用defaultServiceManager()方法來獲取gDefaultServiceManager對象。對於gDefaultServiceManager對象(單例),若是存在則直接返回;若是不存在則建立該對象。

gDefaultServiceManager的建立過程,可分解爲如下3個步驟:

defaultServiceManager 等價於 new BpServiceManager(new BpBinder(0));defaultServiceManager()返回的是BpServiceManager對象,用於跟servicemanager進程通訊;

一、 ProcessState::self():用於獲取ProcessState對象(單例),每一個進程有且只有一個ProcessState對象(一個進程只能打開binder設備一次,其中ProcessState的成員變量mDriverFD記錄binder驅動的fd,用於訪問binder設備。),若是存在則直接返回,不存在則建立,ProcessState的建立過程:

  • 調用open(),打開/dev/binder驅動設備;

  • 再利用mmap(),建立大小爲1M-8K的內存地址空間,映射內核的地址空間,用來接收事物;

  • 設定當前進程最大的最大併發Binder線程個數爲16。

二、getContextObject(): 獲取BpBinder對象,對於handle=0的BpBinder對象(在整個Binder系統中handle=0表明ServiceManager所對應的BBinder),若是存在則直接返回,不存在才建立。BpBinder經過handler來指向所對應BBinder, 。

三、interface_cast():用於獲取BpServiceManager對象,

註冊服務

  • 註冊服務: do_add_service():先檢查權限,檢查selinux權限是否知足;服務檢索,根據服務名來查詢匹配的服務;當查詢到已存在同名的服務,則先清理該服務信息,再將當前的服務加入到服務列表svclist(記錄服務名和handle信息)

  • 註冊 MediaPlayerService 服務端,咱們經過 ServiceManager 的 addService() 方法來註冊服務。

  • 首先 ServiceManager 向 Binder 驅動發送 BC_TRANSACTION 命令(ioctl 的命令,BC 能夠理解爲 binder client 客戶端發過來的請求命令)攜帶 ADD_SERVICE_TRANSACTION 命令,同時註冊服務的線程進入等待狀態 waitForResponse()。 Binder 驅動收到請求命令向 ServiceManager 的 todo 隊列裏面添加一條註冊服務的事務。事務的任務就是建立服務端進程 binder_node 信息並插入到 binder_procs 鏈表中。

  • 事務處理完以後發送 BR_TRANSACTION 命令,ServiceManager 收到命令後向 svcinfo 列表中添加已經註冊的服務。最後發送 BR_REPLY 命令喚醒等待的線程,通知註冊成功。

獲取服務

  • 查詢服務: do_find_service():查詢到目標服務,並返回該服務所對應的handle。從svclist服務列表中,根據服務名遍歷查找是否已經註冊。當服務已存在svclist,則返回相應的服務名,不然返回NULL。當找到服務的handle, 則調用bio_put_ref(reply, handle),將handle封裝到reply

  • 獲取服務的過程與註冊相似,相反的過程。經過 ServiceManager 的 getService() 方法來註冊服務。

  • 首先 ServiceManager 向 Binder 驅動發送 BC_TRANSACTION 命令攜帶 CHECK_SERVICE_TRANSACTION 命令,同時獲取服務的線程進入等待狀態 waitForResponse()。

  • Binder 驅動收到請求命令向 ServiceManager 的發送 BC_TRANSACTION 查詢已註冊的服務,查詢到直接響應 BR_REPLY 喚醒等待的線程。若查詢不到將與 binder_procs 鏈表中的服務進行一次通信再響應。

使用服務

  • 咱們在使用 Binder 時基本都是調用 framework 層封裝好的方法,AIDL 就是 framework 層提供的傻瓜式是使用方式。假設服務已經註冊完,咱們來看看客戶端怎麼執行服務端的方法。

  • 首先咱們經過 ServiceManager 獲取到服務端的 BinderProxy 代理對象,經過調用 BinderProxy 將參數,方法標識(例如:TRANSACTION_test,AIDL中自動生成)傳給 ServiceManager,同時客戶端線程進入等待狀態。

  • ServiceManager 將用戶空間的參數等請求數據複製到內核空間,並向服務端插入一條執行執行方法的事務。事務執行完通知 ServiceManager 將執行結果從內核空間複製到用戶空間,並喚醒等待的線程,響應結果,通信結束。

參考:

Gityuan的 Binder系列 (基於 Android 6.0)

一篇文章瞭解相見恨晚的 Android Binder 進程間通信機制

Binder學習指南

老羅的 Android進程間通訊(IPC)機制Binder簡要介紹和學習計劃 系列

相關文章
相關標籤/搜索