天天看點

Binder In Native

關于binder的設計思想與driver層實作細節可以看這個:android binder設計與實作 - 設計篇,這裡首先簡要概括一下。

  service的每個binder實體位于service所屬的程序種中,binder實體在驅動中被表示為binder_node,并通過成員refs指向了驅動中所有對這個binder實體的引用,binder引用在驅動被表示為binder_ref,并通過成員node指向所引用的binder實體。

  每個使用binder的程序都會在它的processstate的構造函數中打開binder裝置,當打開binder設定時會調用驅動的binder_open,在binder_open中,會為使用binder的程序建立一個binde_proc節點,binder_proc的成員nodes索引了這個程序建立的所有binder實體,refs_by_desc與refs_by_node則是分别以這個程序引用的binder實體的引用号與引用的實體在核心中的内在位址為索引建構的紅黑樹。這樣每個程序都可以通過自己的binder_proc節點檢索到所有自己建立的binder實體與所有對其他binder實體的引用。

  匿名binder要通過實名binder傳遞,而實名binder要向servicemanager注冊。是以首先一定要有程序通過調用ioctl(bs->fd, binder_set_context_mgr, 0)成為servicemanager,當有程序申請成為servicemanager時,驅動就通過binder_new_node建立核心中的第一個binder_node節點。

  資料在驅動中以binder_transaction_date結構傳輸,binder_transaction_data的成員ptr.buffer指向要發送的資料的記憶體位址,在程序間也可以傳送binder實體或引用,比如發送匿名binder,當binder實體或引用在資料中傳輸時,就需要一個方法将binder實體或引用在資料中的位置指出來,ptr.offsets就指向binder偏移數組,offsets_size指明了binder偏移數組的大小,通過這兩個成員驅動就可以找到傳輸的資料中的所有binder實體或引用。binder實體或引用在傳遞時被表示為flat_binder_object,flat_binder_object的type域表示傳輸的binder的類型,type_binder_(weak)_type表示傳輸的是binder實體,type_binder_(weak)_handle表示的是binder引用,binder_type_fd表示檔案binder。

  client要向service發送請求,一定要獲得對service的binder實體的引用,client向service發送請求時,以引用号指明要向哪個service發送請求,引用号0表示向servicemanager發送請求。

  一般情況下,如果client要向某一service進行一個請求,首先會通過向引用号為0的binder引用發送get_service請求獲得自己需要的service在的引用,然後再向這個引用即這個引用對應的service發送請求。

  驅動會将所有發送到引用号為0的請求轉發至servicemanager,當一個程序向0号引用即servicemanager請求某一個service時,servicemanager會檢測一個表查找client請求的service是否已向自己注冊,當binder實體向servicemanager注冊時,servicemanager會将binder實體的名字與引用存入一個查找表中,如果已經注冊,就将service所注冊的binder引用傳回給請求的程序。

  當servicemanager将某一程序請求的service的binder引用發送給這一程序時,由于傳送的是引用,是以flat_binder_object的type的值是type_binder_(weak)_handle,驅動通過binder_transaction_date的ptr.offsets和offsets_size知道了傳回資料中包含binder實體或引用,然後通過這兩個成員找出資料中的binder實體或引用,通過flat_binder_object的type成員知道了傳回資料中包含的是binder引用,然後建立一個對service的binder實體的引用并同時儲存到binder實體在驅動中的節點binder_node的refs成員與client程序的binder_proc中。

  client得到了service的引用就可以以這個引用向service發送請求了,資料包是binder_transaction_date結構體,其成員target是一個聯合,target.handle表示client對service的引用号,target.ptr表示binder實體在service程序中的記憶體位址,當client向service發送請求時填充target.handle域,驅動根據client所屬的binder_proc節點與引用号handle獲得client對service的binder實體的引用binder_ref,然後通過binder_ref的node成員獲得service的binder實體在核心中的節點binder_node,然後将client的請求添加到service程序的等待隊列或service程序某一線程的等待隊列,service就可以處理client的請求了。

  接下來看下native層對binder的使用。

  binder被實作為一個字元裝置,應用程式通過ioctl調用與binder驅動程式進行通信。首先看實作一個servicedemo涉及到的類結構關系。

Binder In Native

  refbase是android實作指針管理的類,牽扯到引用計數的都繼承自這個類,然後通過sp,wp實作強引用計數與弱引用計數的管理。

  binder使用client-server的通信方式,要實作一個server,需要先定義一套接口,client與server同時實作這套接口,server端完成實際的功能,client端隻是對server端功能調用的封裝,由于這套接口需要跨程序調用,需要對所有接口一一編号,server端根據接口編号決定調用什麼函數。在上圖中對接口的定義就是iservicedemo。

  要實作程序間通信,首先需要定義通信的協定,然後向應用程式提供通信的接口,binder driver定義了通信協定,ibinder,bpbinder,bbinder承擔了通信接口的工作,ibinder定義了通信的接口,bpbinder是client通路服務端的代理對象,負責打開binder裝置并與binder裝置通信,bbinder作為服務端與binder裝置通信的接口。client通過bpbinder連接配接binder driver,然後binder driver通過bbinder與server通信,進而完成程序間通信。

  iservicedemo定義了client與server通信的接口,需要client與server同時實作,我們已經知道,client通過bpbinder與server的bbinder進行通信,那麼client端怎麼得到bpbinder,server端怎麼得到bbinder呢?從上圖可以看到,iservicedemo繼承自iinterface,其實iinterface就定義了一個方法asbinder,傳回一個ibinder對象的指針,應該是通過這個方法獲得bpbinder與bbinder對象了。看asbinder實作可以知道,asbinder直接調用了onasbinder,onasbinder是一個虛方法,是以是調用子類的具體實作。我們發現,iinterface有兩個子類bpinterface與bninterface,在這兩個類中都實作了onasbinder,在bpinterface中,onasbinder傳回了remote(),remote()其實是傳回一個bpbinder對象,後面會看到。在bninterface中,onasbinder直接傳回this指針,而bninterface繼承自bbinder,是以bninterface的onasbinder傳回了一個bbinder對象,bpbinder與bbinder都有了,client就可以與server通信了。

  前面說到remote()傳回一個bpbinder對象,那麼這個對象是如何傳回的呢?從上圖看到,bninterface是繼承自bbinder的,但是bpinterface并沒有繼承自bpbinder,但是我們發現,bpinterface的構造函數接收一個ibinder類型的參數,我們看一下bpinterface的構造函數:

  bpinterface繼承自bprefbase,在bpinterface的初始化清單中調用了父類bprefbase的構造函數,将ibinder remote傳了過去。再看bprefbase的構造函數:

  直接将bpinterface傳過來的ibinder remote儲存到了成員mremote中,而remote()函數就直接傳回了這個mremote對象。

  通過bpinterface的構造函數儲存了bpbinder對象,那麼bpinterface的構造函數是什麼時候調用的,而作為構造函數參數傳遞進去的bpbinder又是什麼時候構造的?以servicemanager為例,實名binder需要通過addservice向servicemanager注冊,這也是程序間通信,那麼我們就需要獲得servicemanager的bpbinder,即bpinterface的子類bpservicemanager對象,來看一下bpservicemanager的擷取方法:

  單例模式,看以上代碼的紅色部分,processstate代表程序對象,每個程序隻有一個,在processstate::self()中通過單例模式傳回每個程序的processstate的唯一執行個體,在processstate的函數函數中通過open調用打開了binder裝置,并通過mmap建立了記憶體映射。open引起binder driver中的binder_open被調用,binder_open中建立binder_proc節點,初始化todo隊列與wait隊列,并将binder_proc節點儲存在binder_open第二個參數struct

file *flip的flip->private_data中及binder_procs中。

  handle是0,lookuphandlelocked的傳回結果會是null,是以會執行紅色部分建立一個bpbinder,defaultservicemanager中紅色部分可以簡化為:

  bpbinder有了,我們在前面也知道了bpbinder會做為參數傳遞給bpinterface的構造函數,那麼bpinterface的構造函數是什麼時候調用的?從以上代碼看,應該是interface_cast了,将參數bpbinder轉化為了bpinterface的子類bpservicemanager,再來看interface_cast的實作。

  interface即為iservicemanager,繼承自iinterface的類都會聲明delcare_meta_interface與implement_meta_interface,看一下implement_meta_interface的實作:

  在implement_meta_interface宏中實作了asinterface,上述紅色代碼中,obj即傳進來的bpbinder(0),最上面的圖的注釋中說了bpbinder的querylocalinterface傳回null,是以會執行藍色代碼,interface是servicemanager,是以會建立一個bpservicemanager對象。bpservicemanager對象有了,對過其asbinder方法傳回的bpbinder對象就可以與server進行通信了。

  client有了代理對像bpinterface,那麼怎麼通過這個代理對象與server進行通信呢?标準方法是下面這樣:

  前面已經說了,client通過bpbinder經由binder驅動、bbinder與server端通信,從這裡看确實是這樣,remote()傳回bpbinder對象,調用bpbinder的transact來與server通信,transact是定義在ibinder中的,bpbinder與bbinder都實作了這個方法。

  在bpbinder::transact的實作中,直接調用了ipcthreadstate::transact,前面說過processstate代表程序對象,每個程序有一個,在processstate的構造函數會打與binder裝置并進行mmap,而這裡的ipcthreadstate就表示線程對象,使用lts(local thread storage)每個線程有一個ipcthreadstate對象,binder通信是線程與線程的通信,這裡我們能通過ipcthreadstate::transact與server端進行通信。

  ipcthreasstate::transact方法首先調用writetransactiondate将請求資料封裝進binder_transaction_data結構并寫入parcel mout中。然後調用waitforresponse。

  waitforresponse會調用talkwithdriver,talkwithdriver通過ioctl(driverfd,binder_wirte_read,&binder_write_read)與binder驅動進行通信,當server處理完請求後talkwithdriver成功傳回,然後waitforresponse中讀取binder driver傳回的指令并執行相應的動作。

  在server中,binder thread的jointhreadpool中會調用taklwithdriver等待client請求,當有請求到來時talkwithdriver傳回,讀取command,調用executecommand處理請求。在executecommand中調用bbinder的transact處理請求,bbinder::transact會調用虛方法ontransact來完成具體功能,具體實作就是bnservicemanager::ontransact或bnservicedemo::ontransact等等。一般會有一個類繼承自bnxxxxx完成具體功能,在bnxxxxx的ontransact中會調用完成相應功能的接口,由于是虛方法,就會調用到具體實作類。

   注冊上下文管理者--servicemanager

  通過 ioctl(bs->fd, binder_set_context_mgr, 0); 一個程序可以注冊成為上下檔案管理者,在servicemanager就是執行這條ioctl請求。

  ioctl調用會執行binder driver的binder_ioctl函數,binder_ioctl根據第二個參數cmd執行相應的同作,看下binder_set_context_mgr對應的處理:

  很簡單,就是通過binder_new_node擷取到一個binder_node儲存到全局變量binder_context_mgr_node中,同時儲存了uid,隻能有一個context_manager。

繼續閱讀