歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Android系統的Binder機制

Android系統的Binder機制

日期:2017/3/1 10:46:26   编辑:Linux編程
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,(其實應該是三種,丟掉了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機制的其他部分。

Copyright © Linux教程網 All Rights Reserved