閱讀673 返回首頁    go 阿裏雲 go 技術社區[雲棲]


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涉及到的類結構關係。

  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的構造函數:

template<typename INTERFACE>
inline BpInterface<INTERFACE>::BpInterface(const sp<IBinder>& remote)
    : BpRefBase(remote)
{
} 

  BpInterface繼承自BpRefBase,在BpInterface的初始化列表中調用了父類BpRefBase的構造函數,將IBinder remote傳了過去。再看BpRefBase的構造函數:

BpRefBase::BpRefBase(const sp<IBinder>& o)
    : mRemote(o.get()), mRefs(NULL), mState(0)
{
    extendObjectLifetime(OBJECT_LIFETIME_WEAK);

    if (mRemote) {
        mRemote->incStrong(this);           // Removed on first IncStrong().
        mRefs = mRemote->createWeak(this);  // Held for our entire lifetime.
    }
}

  直接將BpInterface傳過來的IBinder remote保存到了成員mRemote中,而remote()函數就直接返回了這個mRemote對象。

  通過BpInterface的構造函數保存了BpBinder對象,那麼BpInterface的構造函數是什麼時候調用的,而作為構造函數參數傳遞進去的BpBinder又是什麼時候構造的?以ServiceManager為例,實名Binder需要通過addService向ServiceManager注冊,這也是進程間通信,那麼我們就需要獲得ServiceManager的BpBinder,即BpInterface的子類BpServiceManager對象,來看一下BpServiceManager的獲取方法:

sp<IServiceManager> defaultServiceManager()
{
    if (gDefaultServiceManager != NULL) return gDefaultServiceManager;

    {
        AutoMutex _l(gDefaultServiceManagerLock);
        if (gDefaultServiceManager == NULL) {
            gDefaultServiceManager = interface_cast<IServiceManager>(
                ProcessState::self()->getContextObject(NULL));
        }
    }

    return gDefaultServiceManager;
}

  單例模式,看以上代碼的紅色部分,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中。

sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& caller)
{
    return getStrongProxyForHandle(0);
}

sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
    sp<IBinder> result;

    AutoMutex _l(mLock);

    handle_entry* e = lookupHandleLocked(handle);

    if (e != NULL) {
        // We need to create a new BpBinder if there isn't currently one, OR we
        // are unable to acquire a weak reference on this current one.  See comment
        // in getWeakProxyForHandle() for more info about this.
        IBinder* b = e->binder;
        if (b == NULL || !e->refs->attemptIncWeak(this)) {
            b = new BpBinder(handle);
            e->binder = b;
            if (b) e->refs = b->getWeakRefs();
            result = b;
        } else {
            // This little bit of nastyness is to allow us to add a primary
            // reference to the remote proxy when this team doesn't have one
            // but another team is sending the handle to us.
            result.force_set(b);
            e->refs->decWeak(this);
        }
    }

    return result;
}

  handle是0,lookupHandleLocked的返回結果會是NULL,所以會執行紅色部分新建一個BpBinder,defaultServiceManager中紅色部分可以簡化為:

gDefaultServiceManager = interface_cast<IServiceManager>(new BpBinder(0));

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

template<typename INTERFACE>
inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj)
{
    return INTERFACE::asInterface(obj);
}

  INTERFACE即為IServiceManager,繼承自IInterface的類都會聲明DELCARE_META_INTERFACE與IMPLEMENT_META_INTERFACE,看一下IMPLEMENT_META_INTERFACE的實現:

#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME)                       \
    const android::String16 I##INTERFACE::descriptor(NAME);             \
    const android::String16&                                            \
            I##INTERFACE::getInterfaceDescriptor() const {              \
        return I##INTERFACE::descriptor;                                \
    }                                                                   \
    android::sp<I##INTERFACE> I##INTERFACE::asInterface(                \
            const android::sp<android::IBinder>& obj)                   \
    {                                                                   \
        android::sp<I##INTERFACE> intr;                                 \
        if (obj != NULL) {                                              \
            intr = static_cast<I##INTERFACE*>(                          \
                obj->queryLocalInterface(                               \
                        I##INTERFACE::descriptor).get());               \
            if (intr == NULL) {                                         \
                intr = new Bp##INTERFACE(obj);                          \
            }                                                           \
        }                                                               \
        return intr;                                                    \
    }                                                                   \
    I##INTERFACE::I##INTERFACE() { }                                    \
    I##INTERFACE::~I##INTERFACE() { }      

  在IMPLEMENT_META_INTERFACE宏中實現了asInterface,上述紅色代碼中,obj即傳進來的BpBinder(0),最上麵的圖的注釋中說了BpBinder的queryLocalInterface返回NULL,所以會執行藍色代碼,INTERFACE是Servicemanager,所以會新建一個BpServiceManager對象。BpServiceManager對象有了,對過其asBinder方法返回的BpBinder對象就可以與Server進行通信了。

 

  Client有了代理對像BpInterface,那麼怎麼通過這個代理對象與Server進行通信呢?標準方法是下麵這樣:

remote()->transact(SET_MASTER_VOLUME, data/*parcel*/, &reply/*parcel*/);

  前麵已經說了,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對應的處理:

case BINDER_SET_CONTEXT_MGR:
                if (binder_context_mgr_node != NULL) {
                        printk(KERN_ERR "binder: BINDER_SET_CONTEXT_MGR already set\n");
                        ret = -EBUSY;
                        goto err;
                }
                if (binder_context_mgr_uid != -1) {
                        if (binder_context_mgr_uid != current->cred->euid) {
                                printk(KERN_ERR "binder: BINDER_SET_"
                                       "CONTEXT_MGR bad uid %d != %d\n",
                                       current->cred->euid,
                                       binder_context_mgr_uid);
                                ret = -EPERM;
                                goto err;
                        }
                } else
                        binder_context_mgr_uid = current->cred->euid;
                binder_context_mgr_node = binder_new_node(proc, NULL, NULL);
                if (binder_context_mgr_node == NULL) {
                        ret = -ENOMEM;
                        goto err;
                }
                binder_context_mgr_node->local_weak_refs++;
                binder_context_mgr_node->local_strong_refs++;
                binder_context_mgr_node->has_strong_ref = 1;
                binder_context_mgr_node->has_weak_ref = 1;
                break;

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

最後更新:2017-04-03 05:39:11

  上一篇:go C 十字鏈表
  下一篇:go HTML 基礎