1. 程式人生 > >Android如何進行程序間通訊之——Binder

Android如何進行程序間通訊之——Binder


一. 為什麼會有Binder

首先我們來看一句話:Binder是Android中使用最廣泛的IPC(程序間呼叫)機制。所以說白了,Binder的存在是為了Android系統中的跨程序函式(包括服務等)呼叫。這是作業系統的基本功能之一。在Android系統中的具體表現形式就是應用程序與系統服務程序之間的互動,或者是使用者程序與使用者程序之間的互動,又或者是系統程序與系統程序之間的互動。例如Activity Manager Service(AMS)對應用的Activity,Service以及Broadcast的管理。

二. Binder的原理及實現

1)Binder的基本組成

Binder基本上是由Binder驅動,Service Manager,Binder Client以及Binder Server組成

。其實Binder機制執行過程和網路TCP/IP協議的網路執行過程比較類似。Binder驅動相當於路由器,Service Manager相當於DNS,Binder Client相當於客戶端,負責向伺服器發起請求,也就是向Binder Server程序發起服務請求,Binder Server相當於伺服器,負責處理客戶端發過來的請求,也就是處理Binder Client發過來的服務請求。

智慧指標。Binder的實現中,還廣泛地應用到了智慧指標。智慧指標可以自動的進行釋放,較好地解決了1)指標沒有得到初始化,2)指標沒被釋放,3)野指標等問題。其實現比較簡單,將指標封裝到類中,同時在智慧指標模板類中加入了計數引用。這裡要區分下強指標和弱指標。

強指標和弱指標的出現是為了防止迴圈引用導致記憶體空間得不到釋放。

對於強指標,當其強指標計數為0時,不管弱指標計數為多少,都可以將該強指標釋放掉。對於弱指標,當其要訪問物件時,必須要升級為強指標才能進行訪問。

資料傳遞載體Parcel。在同一個程序間物件的傳遞可以通過引用或者指標傳遞,但是程序間傳遞物件,指標或引用是行不通的。所以我們需要將資料進行封裝,從程序A傳遞到程序B。

Parcel負責資料的打包和重組。打包和重組要遵從相同的協議(協議要配套),才能正確地進行資料傳遞。此外Parcel底層的資料讀寫操作都是通過原生代碼(c++)實現的。

  • Parcel資料儲存方式
通過類結構組織起來,其成員包括,指向資料的指標,Parcel資料塊儲存容量,資料讀取遊標等。
  • 具體示例(String和Binder)

writeString()/readString()和writeStrongBinder()/readStrongBinder()。

2)Binder驅動(路由器)

Binder驅動是一個標準的Linux驅動,並運行於核心態。它會將自己註冊成一個misc device,並向上層提供一個/dev/binder節點。其不對應於真實的裝置,但是可以提供諸如,open(), ioctl(), mmap()等常用的檔案操作。全部的可用操作如下:

static const struct file_operations binder_fops={
    .owner = THIS_MODULE,
    .poll = binder_poll,
    .unlocked_ioctl = binder_ioctl,
    .map = binder_mmap,
    .open = binder_open,
    .flush = binder_flush,
    .release = binder_flush,
};

開啟(binder_open)。在核心態中完成開啟,也就是分配的是核心空間的地址,方便記憶體共享。每個程序對應於一個binder管理實體(結構體為binder_proc,如下所示)。我們可以adb連線手機裝置後在/sys/kernel/debug/binder(貌似4.0以後的系統就是這個路徑了,以前在/proc目錄下)目錄下看到其管理檔案。這裡面的檔案值得好好分析,包括了各個程序於binder互動的狀態(./proc目錄下)。還有全域性的狀態,還有binder操作日誌,包括全部的和失敗的。不失為debug利器。

struct binder_proc {
    struct hlist_node proc_node;
    struct rb_root threads;
    struct rb_root nodes;
    struct rb_root refs_by_desc;
    struct rb_root refs_by_node;
    int pid; 
    struct vm_area_struct *vma;
    struct mm_struct *vma_vm_mm;
    struct task_struct *tsk;
    struct files_struct *files;
    struct hlist_node deferred_work_node;
    int deferred_work;
    void *buffer;
    ptrdiff_t user_buffer_offset;

    struct list_head buffers;
    struct rb_root free_buffers;
    struct rb_root allocated_buffers;
    size_t free_async_space;

    struct page **pages;
    size_t buffer_size;
    uint32_t buffer_free;
    struct list_head todo;
    wait_queue_head_t wait;
    struct binder_stats stats;
    struct list_head delivered_death;
    int max_threads;
    int requested_threads;
    int requested_threads_started;
    int ready_threads;
    long default_priority;
    struct dentry *debugfs_entry;
};


對映(binder_mmap)。binder_mmap(struct file * filp, struct vm_area_struct *vma),其中vma是應用程式中的一段虛擬地址。該函式將會在核心中給這段虛擬地址分配核心空間的實體地址。同樣地,對於其他程序,也會發生同樣的事情。如此就完成了程序間的記憶體共享。

  • binder_mmap()函式首次只會分配一頁的核心空間實體地址,也算是一種緩慢分配的方法,對嵌入式裝置還是非常有必要的。
  • list_head proc->buffers管理對映的所有記憶體塊,rb_root proc->free_buffers管理所有可用的空閒記憶體,rb_root proc->allocated_buffers管理所有已經分配了的記憶體。

I/O操作(binder_ioctl)。binder_ioctl()可以滿足應用程序於binder之間的所有I/O操作,包括讀,寫等等。

3)Service Manager(DNS)

Service Manager本身就是一個Binder Server,所以其負責為Binder Client提供服務,且Binder驅動中有專門為SM服務的一些命令。所以,Service Manager在Binder被使用前就應該ready。SM的啟動是在init程序解析init.rc的過程中。

4)Binder Client(客戶端)

5)Binder Server(伺服器)

三. 對Binder的分析

相比與其他程序間通訊方式,如訊號量,管道,Socket等方式的優缺點。