天天看點

Binder 跨程序通信原理

在 Linux下程序間通信的原理 裡面,我們正式了解一下Binder的IPC原理。

一、動态核心可加載子產品 && 記憶體映射

跨程序通信是需要核心空間做支援的。傳統的 IPC 機制如管道、Socket 都是核心的一部分,是以通過核心支援來實作程序間通信自然是沒問題的。但是 Binder 并不是 Linux 系統核心的一部分,那怎麼辦呢?這就得益于 Linux 的動态核心可加載子產品的機制;子產品是具有獨立功能的程式,它可以被單獨編譯,但是不能獨立運作。它在運作時被連結到核心作為核心的一部分運作。這樣,Android 系統就可以通過動态添加一個核心子產品運作在核心空間,使用者程序之間通過這個核心子產品作為橋梁來實作通信。

在 Android 系統中,這個運作在核心空間,負責各個使用者程序通過 Binder 實作通信的核心子產品就叫 Binder 驅動(Binder Dirver)。

那麼在 Android 系統中使用者程序之間是如何通過這個核心子產品(Binder 驅動)來實作通信的呢?難道是和前面說的傳統 IPC 機制一樣,先将資料從發送方程序拷貝到核心緩存區,然後再将資料從核心緩存區拷貝到接收方程序,通過兩次拷貝來實作嗎?顯然不是,否則也不會有開篇所說的 Binder 在性能方面的優勢了。

這就不得不通道 Linux 下的另一個概念:記憶體映射。

Binder IPC 機制中涉及到的記憶體映射通過 mmap() 來實作,mmap() 是作業系統中一種記憶體映射的方法。記憶體映射簡單的講就是将使用者空間的一塊記憶體區域映射到核心空間。映射關系建立後,使用者對這塊記憶體區域的修改可以直接反應到核心空間;反之核心空間對這段區域的修改也能直接反應到使用者空間。

記憶體映射能減少資料拷貝次數,實作使用者空間和核心空間的高效互動。兩個空間各自的修改能直接反映在映射的記憶體區域,進而被對方空間及時感覺。也正因為如此,記憶體映射能夠提供對程序間通信的支援。

二、Binder IPC 實作原理

Binder IPC 正是基于記憶體映射(mmap)來實作的,但是 mmap() 通常是用在有實體媒體的檔案系統上的。

比如程序中的使用者區域是不能直接和實體裝置打交道的,如果想要把磁盤上的資料讀取到程序的使用者區域,需要兩次拷貝(磁盤-->核心空間-->使用者空間);通常在這種場景下 mmap() 就能發揮作用,通過在實體媒體和使用者空間之間建立映射,減少資料的拷貝次數,用記憶體讀寫取代I/O讀寫,提高檔案讀取效率。

而 Binder 并不存在實體媒體,是以 Binder 驅動使用 mmap() 并不是為了在實體媒體和使用者空間之間建立映射,而是用來在核心空間建立資料接收的緩存空間。

一次完整的 Binder IPC 通信過程通常是這樣:

1. 首先 Binder 驅動在核心空間建立一個資料接收緩存區;

2. 接着在核心空間開辟一塊核心緩存區,建立核心緩存區和核心中資料接收緩存區之間的映射關系,以及核心中資料接收緩存區和接收程序使用者空間位址的映射關系;

3. 發送方程序通過系統調用 copyfromuser() 将資料 copy 到核心中的核心緩存區,由于核心緩存區和接收程序的使用者空間存在記憶體映射,是以也就相當于把資料發送到了接收程序的使用者空間,這樣便完成了一次程序間的通信。

其原理如下圖:

Binder 跨程式通信原理