天天看點

Android系統的Binder機制之一——Service Manager

Android雖然建構在Linux上面,但是在IPC(程序間)機制方面,沒有利用Linux提供IPC機制,而是自己實作了一套輕量級的IPC機制——binder機制。并且Android Binder機制之上,Android架構提供了一套封裝,可以實作對象代理(在本地程序中代理遠端程序的對象)。本文簡單分析一下Android Binder機制。

Binder情景分析

    一個IPC通訊我們可以了解成用戶端-伺服器模式,是以我們先在這裡分析一下典型的Binder應用模式:

1、用戶端通過某種方式(後文會詳細介紹)得到伺服器端的代理對象。從用戶端角度看來代理對象和他的本地對象沒有什麼差别。它可以像其他本地對象一樣調用其方法,通路其變量。

2、用戶端通過調用伺服器代理對象的方法向伺服器端發送請求。

3、代理對象把使用者請求通過Android核心(Linux核心)的Binder驅動發送到伺服器程序。

4、伺服器程序處理使用者請求,并通過Android核心(Linux核心)的Binder驅動傳回處理結果給用戶端的伺服器代理對象。

5、用戶端收到伺服器端的傳回結果。

    如果你對COM或者Corba熟悉的話,以上的情景是否會讓你聯想到什麼呢?沒錯!都是對象代理。以上的情景,在Android中經常會被用到。如果你還沒有注意到這點兒,那麼本文非常适合你。

Binder機制的組成

1、Binder驅動

    binder是核心中的一個字元驅動裝置位于:/dev/binder。這個裝置是Android系統IPC的核心部分,用戶端的服務代理用來通過它向伺服器(server)發送請求,伺服器也是通過它把處理結果傳回給用戶端的服務代理對象。我們隻需要知道它的功能就可以了,本文我們的重點不在這裡,是以後面不會專門介紹這部分,因為很少會有人會顯示打開這個裝置去開發Android程式。如果想深入了解的話,請研究核心源碼中的binder.c。

2、Service Manager

    負責管理服務。對應于第一步中,用戶端需要向Service Manager來查詢和獲得所需要服務。伺服器也需要向Service Manager注冊自己提供的服務。可以看出Service Manager是服務的大管家。

3、服務(Server)

    需要強調的是這裡服務是指的是System Server,而不是SDK server,請參考《(轉)高煥堂——Android架構底層結構知多少?》關于兩種Server的介紹(其實應該是三種,丢掉了init調用的server,在init.rc中配置)。

4、用戶端

    一般是指Android系統上面的應用程式。它可以請求Server中的服務。

5、對象代理

    是指在用戶端應用程式中生成的Server代理(proxy)。從應用程式角度看代理對象和本地對象沒有差别,都可以調用其方法,方法都是同步的,并且傳回相應的結果。

大内總管——Service Manager

    Android系統Binder機制的總管是Service Manager,所有的Server(System Server)都需要向他注冊,應用程式需要向其查詢相應的服務。可見其作用是多麼的重要,是以本文首先介紹Service Manager。

    通過上面介紹我們知道Service Manager非常重要,責任重大。那麼怎樣才能成為Service Manager呢?是不是誰都可以成為Service Manager呢?怎樣處理server的注冊和應用程式的查詢和擷取服務呢?為了回答這些問題先檢視,Android中Service Manager的源碼,其源碼位于:

frameworks/base/cmds/servicemanager/service_manager.c      

我們發現了main函數,說明他自己就是一個程序,在init.rc中我們發現:

service servicemanager /system/bin/servicemanager
    user system
    critical
    onrestart restart zygote
    onrestart restart media      

說明其是Android核心程式,開機就會自動運作。

    下面我們在研究一下它的代碼,main函數很簡單:

int main(int argc, char **argv)
{
    struct binder_state *bs;
    void *svcmgr = BINDER_SERVICE_MANAGER;

    bs = binder_open(128*1024);

    if (binder_become_context_manager(bs)) {
        LOGE("cannot become context manager (%s)/n", strerror(errno));
        return -1;
    }

    svcmgr_handle = svcmgr;
    binder_loop(bs, svcmgr_handler);
    return 0;
}      

我們看到它先調用binder_open打開binder裝置(/dev/binder),其次它調用了binder_become_context_manager函數,這個函數使他自己變為了“Server大總管”,其代碼如下:

int binder_become_context_manager(struct binder_state *bs)
{
    return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}      

也就是通過ioctl向binder裝置聲明“我就是server大總管”。

    Service Manager作為一個Server大總管,本身也是一個server。既然是一個server就要時刻準備為用戶端提供服務。最好Service Manager調用binder_loop進入到循環狀态,并提供了一個回調函數,等待使用者的請求。注意他的Service Manager的用戶端既包括應用程式(查詢和擷取服務),也包括Server(注冊服務)。

    Service Manager的客戶怎樣才能請求其服務呢?答案是上文我們提到的情景一樣。客戶需要在自己程序中建立一個伺服器代理。現在沒有地方去查詢服務,那麼怎樣它的客戶怎樣生成他的服務代理對象呢?答案是binder裝置(/devbinder)為每一個服務維護一個句柄,調用binder_become_context_manager函數變為“Server大總管”的服務,他的句柄永遠是0,是一個“衆所周知”的句柄,這樣每個程式都可以通過binder機制在自己的程序空間中建立一個

Service Manager代理對象了。其他的服務在binder裝置在裝置中的句柄是不定的,需要向“Server大總管”查詢才能知道。

    現在我們需要研究Server怎樣注冊服務了,還是在其源碼中,我們可以看到在其服務處理函數中(上文提到binder_loop函數注冊給binder裝置的回調函數)有如下代碼:

case SVC_MGR_ADD_SERVICE:
        s = bio_get_string16(msg, &len);
        ptr = bio_get_ref(msg);
        if (do_add_service(bs, s, len, ptr, txn->sender_euid))
            return -1;
        break;      

有server向binder裝置寫入請求注冊Service時,Service Manager的服務處理回調函數将會被調用。我們在仔細看看do_add_service函數的實作:

int do_add_service(struct binder_state *bs,
                   uint16_t *s, unsigned len,
                   void *ptr, unsigned uid)
{
    struct svcinfo *si;
//    LOGI("add_service('%s',%p) uid=%d/n", str8(s), ptr, uid);

    if (!ptr || (len == 0) || (len > 127))
        return -1;

    if (!svc_can_register(uid, s)) {
        LOGE("add_service('%s',%p) uid=%d - PERMISSION DENIED/n",
             str8(s), ptr, uid);
        return -1;
    }

    si = find_svc(s, len);
    if (si) {
        if (si->ptr) {
            LOGE("add_service('%s',%p) uid=%d - ALREADY REGISTERED/n",
                 str8(s), ptr, uid);
            return -1;
        }
        si->ptr = ptr;
    } else {
        si = malloc(sizeof(*si) + (len + 1) * sizeof(uint16_t));
        if (!si) {
            LOGE("add_service('%s',%p) uid=%d - OUT OF MEMORY/n",
                 str8(s), ptr, uid);
            return -1;
        }
        si->ptr = ptr;
        si->len = len;
        memcpy(si->name, s, (len + 1) * sizeof(uint16_t));
        si->name[len] = '/0';
        si->death.func = svcinfo_death;
        si->death.ptr = si;
        si->next = svclist;
        svclist = si;
    }

    binder_acquire(bs, ptr);
    binder_link_to_death(bs, ptr, &si->death);
    return 0;
}      

我們看到首先檢查是否有權限注冊service,沒權限就對不起了,出錯傳回;然後檢查是否已經注冊過,注冊過的service将不能再次注冊。然後構造一個svcinfo對象,并加入一個全局連結清單中svclist中。最後通知binder裝置:有一個service注冊進來。

    我們再來看看用戶端怎樣通過Service Manager獲得Service,還是在服務處理函數中(上文提到binder_loop函數注冊給binder裝置的回調函數)有如下代碼:

case SVC_MGR_GET_SERVICE:
    case SVC_MGR_CHECK_SERVICE:
        s = bio_get_string16(msg, &len);
        ptr = do_find_service(bs, s, len);
        if (!ptr)
            break;
        bio_put_ref(reply, ptr);
        return 0;      

    我們可以看到通過do_find_service查找Service如果查找到的話,寫入reply中傳回給用戶端。

    本文我們簡單分析了一下Service Manager,後續我們會繼續分析Android binder機制的其他部分。

繼續閱讀