咱們知道,同一個程序中的兩個函數之間能直接調用的根本緣由是處於相同的內存空間中(虛擬地址的映射規則徹底一致);反之,兩個不一樣的進程,好比微信App和淘寶App所在的進程,它們是沒用辦法直接經過內存地址來訪問到對方內部的函數或者變量的。 既然沒法直接訪問到對方進程的內存空間,那有沒有間接的方法呢?天然是有的,好比接下來要講的Binder。java
Binder 是 Android 中使用最普遍的 IPC 機制。node
整個Binder機制涉及的東西既多又雜,劃分下來能夠包括linux
Binder 的本質目標用一句話來描述,就是進程1(客戶端)但願與進程2(服務器)進行互訪。但由於它們之間是跨進程(跨網絡)的,因此必須藉助於 Binder 驅動(路由器)來把請求正確投遞到對方所在進程(網絡)中。而參與通訊的進程們則需持有 Binder 頒發的惟一標誌(IP地址)。android
既然 Binder 是用來處理進程間通訊的,所謂通訊本質是數據的交互。那進程間是如何傳遞數據的呢?聰明的你確定猜到了,沒錯就是 Parcel。咱們先用一個例子簡單理解一下 Parcel 的概念:c++
你去逛商場,有件衣服 (商品A) 你特別喜歡,可是因爲種種緣由沒有購買;但是你實在是想要啊,因而你打開了某寶,下單付款一鼓作氣。幾天後,你收到了包裹,終於獲得了求之不得的衣服,開心的像個孩子。git
整個過程當中,你拿到了你想要的衣服,可是那個衣服是你當初在商場看到的同一件麼? 不見得吧,可是能夠確定的是,你拿到的和商場看到的是如出一轍的。整個過程當中,你只是手指輕輕點了下下單操做,隱藏在背後的工做人員幫你實現了分檢、打包、物流、運輸,最後將商品完完整整送到了你的手中。若是把你在商店看到的那件衣服視作 IPC 中的原始數據的話,下單的過程則是你將原始數據的意圖發送了出去,接着幕後工做人員們就承當了 Parcel 的角色,根據你的意圖,對你發送的數據進行必要的包裝,這時候打包進去後的則可能商品 A 的兄弟姐妹們,假設爲商品 B,而後通過一層層的運輸交到了你的手上。因爲商品 A 和商品 B 看上去和用起來都是如出一轍的,因此對你來講 商品 A 和 B 沒有什麼區別的。github
回到正題,若是進程間傳遞的數據只是 int 等基礎類型,只需不斷複製直到目標進程便可。但是,若是是個對象呢?同一個進程內的對象傳遞基本都是經過引用來作的,本質上是傳遞了一個內存地址,因爲採用了虛擬的內存機制,每一個進程都有本身獨立的內存地址空間,跨進程傳遞的地址值是無效的,那麼這種簡單的經過複製實現跨進程是行不通的。數組
既然沒法直接複製,那可否經過某種規則,把對象在進程 A 中佔據的內存相關數據打包起來,而後寄送到進程 B 中,由 B 在本身的進程空間中復現這個對象,是否可行? Parcel 就具有這種打包和重組的能力
。既然涉及到了打包和重組,那它確定會依據協議來保證重組後的數據能完整的還原打包前的數據。這有點相似加密與解密的過程,打包與重組使用的數據必須是配套的。緩存
Parcel 操做對象與內容包括bash
原始數據的讀寫操做
原始數據類型數組的讀寫操做
Parcelables 遵循 Parcelable 協議的對象能夠經過 Parcel 來存儲 。與這類對象相關的操做包括
method | 描述 |
---|---|
writeParcelable(Parcelable,int) | 將 Parcelable 類的名字和內容寫入 Parcel 中 |
readParcelable(ClassLoader) | 讀取而且返回一個新的 Parcelable 對象 |
writeParcelableArray(T[],int) | 寫入 Parcelable 對象數組 |
readParcelableArray(ClassLoader) | 讀取並返回一個 Parcelable 對象數組 |
Bundle Bundle 繼承自 Parcelable,是一種特殊的 type-safe 的容器
Active Object 一般咱們存入 Parcel 的是對象的內容,而 Active Object 可就厲害了,寫入的是它們的特殊標誌引用,因此從 Parcel 中讀取這些對象時,你們看到的並非從新建立的對象,而是原來那個被寫入的實例。 Android中採用這種方式傳遞的對象主要有2種:
Untyped Containers
用於讀寫標準的任意類型的 java 容器
咱們知道,Android 系統是基於 linux 內核的,於是它所依賴的 Binder 驅動也必須是一個標準的 Linux 驅動。具體而言,Binder Driver 會將本身註冊成一個虛擬的 misc device,並向上層提供一個/dev/binder 節點。 Binder Driver 位於內核態,能夠提供 open(), ioctl() , mmap() 等經常使用的文件操做。
用於打開 binder 驅動(打開 dev/binder 節點),爲用戶建立一個它本身的 binder_proc 實體,完成對這個新生成的 proc 對象各類初始化並將它加入到 Binder 的全局管理中
/* 在9.0源碼中,binder 驅動已經合入 linux 主分支https://github.com/torvalds/linux/blob/master/drivers/android/binder.c */
static int binder_open(struct inode *nodp, struct file *filp)
{
struct binder_proc *proc;
struct binder_device *binder_dev;
...
proc = kzalloc(sizeof(*proc), GFP_KERNEL); /*分配空間*/
if (proc == NULL)
return -ENOMEM;
spin_lock_init(&proc->inner_lock); /* 宏 spin_lock_init()初始化自旋鎖lock */
spin_lock_init(&proc->outer_lock);
get_task_struct(current->group_leader);
proc->tsk = current->group_leader;
INIT_LIST_HEAD(&proc->todo); /* todo 鏈表*/
proc->default_priority = task_nice(current);
/* binderfs stashes devices in i_private */
if (is_binderfs_device(nodp))
binder_dev = nodp->i_private;
else
binder_dev = container_of(filp->private_data,
struct binder_device, miscdev);
proc->context = &binder_dev->context;
binder_alloc_init(&proc->alloc);
binder_stats_created(BINDER_STAT_PROC);
proc->pid = current->group_leader->pid;
INIT_LIST_HEAD(&proc->delivered_death);
INIT_LIST_HEAD(&proc->waiting_threads); /* wait 鏈表*/
filp->private_data = proc; /* proc 與 flip 關聯 */
mutex_lock(&binder_procs_lock); /* mutex 互斥鎖 */
hlist_add_head(&proc->proc_node, &binder_procs); /* 加入到 Binder 的全局管理 */
mutex_unlock(&binder_procs_lock);
...
return 0;
}
複製代碼
mmap() 是操做系統中一種內存映射的方法。內存映射簡單的講就是將用戶空間的一塊內存區域映射到內核空間。映射關係創建後,用戶對這塊內存區域的修改能夠直接反應到內核空間;反以內核空間對這段區域的修改也能直接反應到用戶空間。
mmap() 一般是用在有物理介質的文件系統上的。但 Binder 自己並非一個硬件設備,而只是基於內存的「僞硬件」, 所以 Binder 驅動使用 mmap() 並非爲了在物理介質和用戶空間之間創建映射,而是用來在內核空間建立數據接收的緩存空間。
一次完整的 Binder IPC 通訊過程一般是這樣:
假若有兩個進程 A 和 B,其中 B 經過 open()和 mmap()後與 Binder 驅動創建了聯繫
這時 Binder 和應用程序就至關於擁有了若干物理內存塊。它們對各自內存地址的操做,實際上就是在同一塊內存中執行的。當進程 A 但願與進程 B 通訊時,進程 A 先經過 Binder 驅動調用 copy_from_user() 將用戶空間數據複製到 binder_proc->buffer 所指向的內存空間,由於 binder_proc->buffer 在物理內存中的位置進程 B 是共享的,由於進程 B 能夠直接訪問這段數據。
Binder 驅動經過 copy_from_user(), 把進程 A 中的某段數據複製到 binder_proc->buffer 所指向的內存空間中。由於 binder_proc->buffer 在物理內存中的位置和進程 B 是共享的,於是進程 B 可以直接訪問到這段數據。因而,Binder 驅動只用了一次複製,就實現了進程 A 和進程 B 間的數據共享。
在實際操做以前,Binder 驅動會斷定應用程序申請的內存大小是否合理--它最多隻支持 4M 的空間的 mmap 操做。當應用程序申請內存大小超過 4M 時,並無直接退出,而是隻知足用戶恰好 4M 的請求:
static int binder_mmap(struct file *filp, struct vm_area_struct *vma) { int ret; struct vm_struct *area; struct binder_proc *proc = filp->private_data; const char *failure_string; struct binder_buffer *buffer; //限制不能超過4M if ((vma->vm_end - vma->vm_start) > SZ_4M) vma->vm_end = vma->vm_start + SZ_4M; ... }
那理論上經過 Intent 傳遞數據大小的限制是否也是 4M 呢?內核上 binder 限定確實是 4M ,可是普通的由 Zygote 孵化而來的用戶進程所映射的 Binder 內存大小已經被限定在了1M左右,這個限制定義 processState 這個類中
/* frameworks/native/libs/binder/ProcessState.cpp */
define BINDER_VM_SIZE ((110241024) - (4096 *2))
故而,對於超過 BINDER_VM_SIZE 的內存申請請求,還沒到 binder drive ,系統就已經拋出異常了。
ServiceManager (後面統一稱 SM )的啓動是在 init 程序解析init.rc
時候啓動的。init 進程是 Android 中第一個被啓動的進程,init 的 PID 是0。注意,這裏的 SM 是指的 native 層的 ServiceManager,源碼路徑在 /frameworks/native/cmds/servicemanager 。
/* /frameworks/native/cmds/servicemanager/servive_manager.c */
int main(int argc, char** argv)
{
struct binder_state *bs;
union selinux_callback cb;
char *driver;
if (argc > 1) {
driver = argv[1];
} else {
driver = "/dev/binder";
}
// 打開 Binder 設備,內部執行 open 、mmap
bs = binder_open(driver, 128*1024);
...
// 將 servicesmanager 註冊成 Binder 機制大管家
if (binder_become_context_manager(bs)) {
ALOGE("cannot become context manager (%s)\n", strerror(errno));
return -1;
}
...
// 進入循環,等待客戶請求
binder_loop(bs, svcmgr_handler);
return 0;
}
複製代碼
/* framework/native/cmds/servicemanager/binder.c */
struct binder_state *binder_open(const char* driver, size_t mapsize)
{
struct binder_state *bs; // binder_state 結構體記錄了 SM 中有關 Binder 的全部信息
struct binder_version vers;
bs = malloc(sizeof(*bs));
if (!bs) {
errno = ENOMEM;
return NULL;
}
bs->fd = open(driver, O_RDWR | O_CLOEXEC); //調用驅動 open 函數,打開 Binder 驅動節點
if (bs->fd < 0) {
fprintf(stderr,"binder: cannot open %s (%s)\n",
driver, strerror(errno));
goto fail_open;
}
if ((ioctl(bs->fd, BINDER_VERSION, &vers) == -1) ||
(vers.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION)) {
fprintf(stderr,
"binder: kernel driver version (%d) differs from user space version (%d)\n",
vers.protocol_version, BINDER_CURRENT_PROTOCOL_VERSION);
goto fail_open;
}
bs->mapsize = mapsize; // 映射大小 是由 SM 設置的 ,大小爲 128k
bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
if (bs->mapped == MAP_FAILED) {
fprintf(stderr,"binder: cannot map device (%s)\n",
strerror(errno));
goto fail_map;
}
return bs;
fail_map:
close(bs->fd);
fail_open:
free(bs);
return NULL;
}
複製代碼
binder_become_context_manager 會向 Binder 驅動發送 BINDER_SET_CONTEXT_MGR 命令
/* /frameworks/native/cmds/servicemanager/binder.c */
int binder_become_context_manager(struct binder_state *bs)
{
return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}
複製代碼
binder_loop 不停循環,從 Binder 驅動獲取數據,正常狀況下就像是一個永不退出的消息隊列
void binder_loop(struct binder_state *bs, binder_handler func)
{
int res;
struct binder_write_read bwr; // 執行 BINDER_WRITE_READ 命令所需的數據結構
uint32_t readbuf[32];
bwr.write_size = 0;
bwr.write_consumed = 0;
bwr.write_buffer = 0;
readbuf[0] = BC_ENTER_LOOPER; // BC_ENTER_LOOPER 命令
binder_write(bs, readbuf, sizeof(uint32_t));
for (;;) {
bwr.read_size = sizeof(readbuf);
bwr.read_consumed = 0;
bwr.read_buffer = (uintptr_t) readbuf;
/* BINDER_WRITE_READ 既可讀也可寫,根據 bwr.write_size 和 bwr.read_size 來肯定具體的執行邏輯 */
res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr); // 讀取消息
...
res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func); //解析消息
...
}
}
複製代碼
binder_parse
int binder_parse(struct binder_state *bs, struct binder_io *bio,
uintptr_t ptr, size_t size, binder_handler func)
{
...
while (ptr < end) {
uint32_t cmd = *(uint32_t *) ptr;
ptr += sizeof(uint32_t);
#if TRACE
fprintf(stderr,"%s:\n", cmd_name(cmd));
#endif
switch(cmd) { // 這裏 cmd 對應 svcmgr_handler
...
// BR_TRANSACTION 這個命令在 整個 Binder 中會頻繁用到
case BR_TRANSACTION: {
struct binder_transaction_data *txn = (struct binder_transaction_data *) ptr;
if ((end - ptr) < sizeof(*txn)) {
ALOGE("parse: txn too small!\n");
return -1;
}
binder_dump_txn(txn);
if (func) {
unsigned rdata[256/4];
struct binder_io msg;
struct binder_io reply;
int res;
bio_init(&reply, rdata, sizeof(rdata), 4);
bio_init_from_txn(&msg, txn);
res = func(bs, txn, &msg, &reply); //主要幹活的函數 具體處理消息
if (txn->flags & TF_ONE_WAY) { /* TF_ONE_WAY表明是單向通訊,不須要回復 */
binder_free_buffer(bs, txn->data.p tr.buffer);
} else {
binder_send_reply(bs, &reply, txn->data.ptr.buffer, res); /* 迴應處理結果*/
}
}
ptr += sizeof(*txn);
break;
}
...
}
return r;
}
複製代碼
BR_TRANSACTION 命令主要由 func 來完成,而後將結果返回給 Binder 驅動。而 ServiceManager 是爲了完成 *「域名」與」IP地址「*間的對應關係的,因此咱們能夠推測它提供的服務至少包括註冊 、查詢等功能。
對於註冊、查詢等具體實現這裏就不分析了。
這裏涉及到 binder_io 類型的 msg 和 reply 對象,想象一下 Parcel 中的 mIn、 mOut, 你能聯想到什麼?
若是要獲取 SM 服務,流程應該是怎麼樣的?
核心工做就只有這些。不過有些具體細節須要再商榷
問題轉換爲 如何設計出一個符合上述要求的 Binder Client?
ProcessState 顧名思義,確定與進程狀態相關。實際上也是如此,它是用來管理每一個應用進程Binder操做的
ProcessState 用於管理進程 Binder ,進程中還可能會有不少線程,進程中的每個線程都應該有與 Binder 驅動溝通的權利,那麼僅僅只有 ProcessState 就有點力不從心了。
與 Binder 驅動進行實際命令通訊的就是 IPCThreadStat
Proxy
有了上面兩個類,應用程序就能夠與 Binder 驅動通訊類,應用程序能夠發送 BINDER_WRITE_READ 等 Binder 支持的指令來與其交互。那爲啥還要代理呢?
代理簡單來講是爲了讓調用方使用起來方便而進行的一層層封裝,在 binder 中真正幹活的是在驅動層,而使用方則多是 app 層、java framework層、c++ framework層、native 層、HAL 層中的任意一個程序;爲了讓客戶端使用起來儘量簡潔,binder 經歷了層層代理;以致於最終咱們經過 startActivity()、bindService()等方式調用到 binder 服務時候根本無感知了。
整個代理過程涉及到類較多,也有不少拗口的新名詞出現,可是無論怎麼繞,它們最終的目的只有一個,那就是將要請求傳遞到 IPCThreadState 讓其與 Binder 驅動交換而且拿到請求結果。
首先,咱們須要有 native 層的 ServiceManager 的代理,經過這個代理咱們能夠輕鬆獲取 SM 服務
ServiceManagerProxy sm=new ServiceManagerProxy(new BpBinder(HANDLE));
IBinder wms_binder= sm.getService("window");
這樣 應用程序僅需兩步就能獲取到 ServiceManager 提供的服務。固然,上面並非源碼真正實現方式,可是並不影響咱們理解它;在 android 中對上面的代碼作了進一步的封裝。這裏咱們先從設計思路來一步步拆解binder設計思想,以避免一會兒陌生名稱太多致使大腦「類爆炸」,cpu運轉不過來
ServiceManagerProxy 能提供的服務和服務端的 SM 必須是要一致的,咱們能夠把這些方法抽成接口的形式;
/* framework/base/core/java/android/os/ServiceManagerNative */
class ServiceManagerProxy implements IServiceManager {
複製代碼
這個接口就是 IServiceManager ,IServiceManager 則繼承自 IInterface 接口
IInterface 這個接口只定義了一個函數聲明 asBinder(),它會返回一個 IBinder 對象
public interface IInterface
{
/**
* Retrieve the Binder object associated with this interface.
* You must use this instead of a plain cast, so that proxy objects
* can return the correct result.
*/
public IBinder asBinder();
}
複製代碼
既然 SM 是 Binder 的大管家,那它天然會提升獲取和添加 binder 服務的功能,做爲 SM 的經理人,ServiceManagerProxy 必然也須要提供這些功能。前面也說過,這些功能是以 IServiceManager 接口的形式定義的。咱們來看看具體實現
/* framework/base/core/java/android/os/ServiceManagerNative */
public final class ServiceManagerNative {
private ServiceManagerNative() {}
/**
* Cast a Binder object into a service manager interface, generating
* a proxy if needed.
*/
@UnsupportedAppUsage
static public IServiceManager asInterface(IBinder obj)
{
if (obj == null) {
return null;
}
IServiceManager in =
(IServiceManager) obj.queryLocalInterface(IServiceManager.descriptor);
if (in != null) {
return in;
}
return new ServiceManagerProxy(obj);
}
}
class ServiceManagerProxy implements IServiceManager {
public ServiceManagerProxy(IBinder remote) {
mRemote = remote;
}
public IBinder asBinder() {
return mRemote;
}
@UnsupportedAppUsage
public IBinder getService(String name) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IServiceManager.descriptor);
data.writeString(name);
mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0);
IBinder binder = reply.readStrongBinder();
reply.recycle();
data.recycle();
return binder;
}
...
@UnsupportedAppUsage
private IBinder mRemote;
}
複製代碼
getService 要取得 ServiceMananger 這個服務,想必得與 Binder 創建關係, 經過mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0); 利用 IBinder 的 transact 將 Parcel 包裝後數據發送出去,而不用去理會 Binder 驅動的 open 、 mmap 、ioctl 等一大堆具體的協議命令,就能夠得到想要的結果。因此咱們能夠大膽猜想,這個 IBinder 必定會在內部調用 ProcessState 和 IPCThreadState 來與 Binder 驅動通訊。
transact 是耗時操做,可是上面再執行完 transact 後就立馬返回了目標 IBinder 對象,因此 Binder 驅動必定要先將調用者線程掛起,直到有告終果再將它喚醒。
GET_SERVICE_TRANSACTION 這個業務碼在 IBinder 中定義的值是 1
int GET_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION;
int FIRST_CALL_TRANSACTION = 0x00000001;
複製代碼
它與 SM 中關聯的業務是 SVC_MGR_GET_SERVICE
/* framework/native/cmds/servicemanager/binder.h */
enum {
/* Must match definitions in IBinder.h and IServiceManager.h */
PING_TRANSACTION = B_PACK_CHARS('_','P','N','G'),
SVC_MGR_GET_SERVICE = 1,
SVC_MGR_CHECK_SERVICE,
SVC_MGR_ADD_SERVICE,
SVC_MGR_LIST_SERVICES,
};
複製代碼
mRemote.transact 的流程稍後分析,這裏又出現了 IBinder 這個新對象,有必要先對它說明一下
爲了讓應用程序簡單便利的使用 SM 服務,咱們見識到了 ServiceManagerProxy ,ServiceManagerProxy 又是經過 ServiceManagerNative.asInterface(IBinder obj) 方法來建立的,建立的時候保證了 ServiceManagerProxy 的惟一性。
ServiceManagerNative.asInterface 則是被 ServiceManager的 getIServiceManager() 調用喚起的。而ServiceManager.java 這個類提供的全是 static 的方法,因此 ServiceManager.java 這個類則是對ServiceManagerProxy 使用進一步封裝。這樣應用程序想要使用 SM 提供的服務,只須要經過調用 ServiceManager.getService(HANDLE) 便可。在這樣層層封裝之下,ServiceManagerNative、ServiceManagerProxy、 ProcessState 、IPCThreadState 、IServiceManager 、BpBinder 、BBinder 等許多讓人頭大的對象都對應用程序隱藏了。
/* framework/base/core/java/android/os/ServiceManager.java */
/** @hide */
public final class ServiceManager {
/**
* Place a new @a service called @a name into the service
* manager.
*
* @param name the name of the new service
* @param service the service object
* @param allowIsolated set to true to allow isolated sandboxed processes
* @param dumpPriority supported dump priority levels as a bitmask
* to access this service
*/
@UnsupportedAppUsage
public static void addService(String name, IBinder service, boolean allowIsolated,
int dumpPriority) {
try {
getIServiceManager().addService(name, service, allowIsolated, dumpPriority);
} catch (RemoteException e) {
Log.e(TAG, "error in addService", e);
}
}
public static IBinder getService(String name) {
try {
IBinder service = sCache.get(name);
if (service != null) {
return service;
} else {
return Binder.allowBlocking(rawGetService(name));
}
} catch (RemoteException e) {
Log.e(TAG, "error in getService", e);
}
return null;
}
@UnsupportedAppUsage
private static IServiceManager getIServiceManager() {
if (sServiceManager != null) {
return sServiceManager;
}
// Find the service manager
sServiceManager = ServiceManagerNative
.asInterface(Binder.allowBlocking(BinderInternal.getContextObject()));
return sServiceManager;
}
}
複製代碼
經過 getService() 會獲得 IBinder 對象。Binder 提供的功能能夠統一在 IBinder 接口中表示。此外,經過getIServiceManager() 獲取 ServiceManagerProxy 時候提供了一個 BinderInternal.getContextObject()。
/**
* Return the global "context object" of the system. This is usually
* an implementation of IServiceManager, which you can use to find
* other services.
*/
@UnsupportedAppUsage
public static final native IBinder getContextObject();
複製代碼
這是個 native 函數,返回的也是一個 IBinder 對象,它的最終實現是經過 jni 調用本地代碼實現的
/* framework/base/core/jni/android_util_Binder.cpp */
static jobject android_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz)
{
sp<IBinder> b = ProcessState::self()->getContextObject(NULL);
return javaObjectForIBinder(env, b);
}
複製代碼
前面引出的 ProcessState 在這裏終於出現了。這裏也有個 IBinder,對應native 層的 Binder 接口。它確定與java 層的 IBinder 功能是對應的。native 層 IBinder 對應的實現是 BpBinder,java 層 IBinder 對應的實現是 BinderProxy。其中 BinderProxy 是由 ProcessState 建立的,BpBinder 是由 javaObjectForIBinder() 函數建立的。
那麼 mRemote.transact 的調用流程 就清晰了
mRemote.transact -> IBinder.transact -> BinderProxy.transact ->BpBinder.transact
/* framework/native/libs/binder/Bpbinder.cpp */
status_t BpBinder::transact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
// Once a binder has died, it will never come back to life.
if (mAlive) {
status_t status = IPCThreadState::self()->transact(
mHandle, code, data, reply, flags);
if (status == DEAD_OBJECT) mAlive = 0;
if (reply != nullptr) {
reply->setTransactingBinder(this);
}
return status;
}
return DEAD_OBJECT;
}
複製代碼
可見 確實是經過 ProcessState 和 IPCThreadState 來真正和 Binder 深刻交流的
ProcessState 要實現的功能包括
保證同一個進程只有一個 ProcessState 實例
只在 ProcessState 對象建立的時候纔打開 Binder 設備以及內存映射
提供 IPC 服務給上層
/* framework/native/libs/binder/ProcessState.cpp */
sp<ProcessState> ProcessState::self()
{
Mutex::Autolock _l(gProcessMutex);
if (gProcess != nullptr) {
return gProcess;
}
gProcess = new ProcessState(kDefaultDriver);
return gProcess;
}
複製代碼
ProcessState::self() 保證了 ProcessState 實例的惟一性,而且僅僅在 ProcessState 實例生成時纔會打開 Binder 設備以及內存映射,與 binder 交互的處理是在 ProcessState 構造函數中
/* framework/native/libs/binder/ProcessState.cpp */
ProcessState::ProcessState(const char *driver)
: mDriverName(String8(driver))
, mDriverFD(open_driver(driver))
, mVMStart(MAP_FAILED)
, mThreadCountLock(PTHREAD_MUTEX_INITIALIZER)
, mThreadCountDecrement(PTHREAD_COND_INITIALIZER)
, mExecutingThreadsCount(0)
, mMaxThreads(DEFAULT_MAX_BINDER_THREADS)
, mStarvationStartTimeMs(0)
, mBinderContextCheckFunc(nullptr)
, mBinderContextUserData(nullptr)
, mThreadPoolStarted(false)
, mThreadPoolSeq(1)
, mCallRestriction(CallRestriction::NONE)
{
if (mDriverFD >= 0) {
// mmap the binder, providing a chunk of virtual address space to receive transactions.
mVMStart = mmap(nullptr, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
...
}
}
複製代碼
mDriverFD(open_driver(driver)) 會打開 binder 節點,拿到 binder 驅動對應的文件描述符 ,以後開始執行內存映射,映射的內存塊大小爲 BINDER_VM_SIZE ,即以前在 binder_mmap 中提到的約等於1M的空間 。
ProcessState 有了,接着便會調用它的 getContextObject() 方法返回一個 BpBinder 對象
/* framework/native/libs/binder/ProcessState.cpp */
sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& /*caller*/)
{
return getStrongProxyForHandle(0);
}
sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
sp<IBinder> result;
...
handle_entry* e = lookupHandleLocked(handle);
if (e != nullptr) {
IBinder* b = e->binder;
if (b == nullptr || !e->refs->attemptIncWeak(this)) {
if (handle == 0) {
Parcel data;
status_t status = IPCThreadState::self()->transact(
0, IBinder::PING_TRANSACTION, data, nullptr, 0);
if (status == DEAD_OBJECT)
return nullptr;
}
b = BpBinder::create(handle);
e->binder = b;
if (b) e->refs = b->getWeakRefs();
result = b;
}
...
}
return result;
}
複製代碼
ProcessState 中有一個 handle_entry 類型的全局列表來記錄全部與 Binder 對象相關的信息, 這裏的 e->binder 指向的則是 BpBinder。與此同時,IPCThreadState 終於出如今了咱們視野中,並且它仍是與transact 這個咱們重點關注的方法有關。
總結來講就是 ProcessState 只是負責打開了 Binder 節點並作 mmap,IPCThreadState 負責與 Binder 驅動進行具體的交互命令
/* framework/native/libs/binder/IPCThreadState.cpp */
status_t IPCThreadState::transact(int32_t handle,
uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags)
{
status_t err;
flags |= TF_ACCEPT_FDS;
...
err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, nullptr);
if (err != NO_ERROR) {
if (reply) reply->setError(err);
return (mLastError = err);
}
if ((flags & TF_ONE_WAY) == 0) {
...
#endif
if (reply) {
err = waitForResponse(reply);
} else {
Parcel fakeReply;
err = waitForResponse(&fakeReply);
}
} else {
err = waitForResponse(nullptr, nullptr);
}
return err;
}
複製代碼
writeTransactionData() 會將數據整理,打包成 Binder 驅動協議規定的格式(具體來說對應於 binder_transaction_data 這個結構體),而且將結果存入 mOut 中,再由 waitForResponse() 將數據發送出去並處理回饋信息。
/* framework/native/libs/binder/IPCThreadState.cpp */
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
uint32_t cmd;
int32_t err;
while (1) {
/* 經過talkWithDriver處理與 Binder之間的交互命令 */
if ((err=talkWithDriver()) < NO_ERROR) break;
err = mIn.errorCheck();
if (err < NO_ERROR) break;
if (mIn.dataAvail() == 0) continue;
...
}
...
return err;
}
複製代碼
talkWithDriver() 這個函數負責真正與 Binder 驅動打交道,它將數據進行必要的包裝後,發送給 Binder 驅動(以 ioctl 的形式執行),當程序執行到 mIn.errorCheck() 時,說明已經收到了 Binder 驅動到回覆,接下來則是對回覆的數據進行處理操做。在目標進程未給予回覆時,在 Binder 內核調度上會先將發起請求的進程掛起,直到目標進程返回結果後,Binder 驅動再喚醒等待的線程。這一塊的邏輯都是在 binder 驅動中定義的,因爲高版本 binder 內核代碼已經合併至 linux 內核,感興趣的能夠自行去閱讀,這裏就再也不細聊了。
最後咱們再簡單回顧下 java 層的 ServiceManager 執行 getService() 對大概流程,簡單的梳理下整個 binder 機制中從 java 到驅動的調用關係吧
ServiceManager(java層) 執行 getService() 首先要拿到 native 層 ServiceManager 的代理 BpBinder,BpBinder 是一個 IBinder 類型對象。爲了拿到這個 IBinder ,Android 進行了一系列的封裝,首先它會經過 ServiceManagerNative 的 asInterface 方法拿到一個 IServiceManager 對象,這個 IServiceManager 就是 ServiceManagerProxy, 爲了拿到這個 ServiceManagerProxy,又須要 BinderInternal 的 getContextObject 取得目標 Service 客戶端代理 BpBinder。因爲當前進程和目標 Service 極可能不是在同一個進程,因此要經過 ServiceManager 來統一調度找到目標 Service,拿到 BpBinder 後經過 transact 函數最終調用調用IPCThreadState 的 transact 函數進行數據傳輸。
文章簡單描述了 Binder 客戶端獲取服務的簡單流程,主要圍繞 ServiceManager 來展開的,涉及 java 層和 native 層的調用邏輯, 對於大部分細節都沒有深刻去探討。
對於 Binder 服務端如何提供服務未涉及、對於 binder 線程池管理或者其餘內部一些數據結構沒有細究、對於 binder 驅動內存管理和數據與驅動交互邏輯未深刻探討。
參考文獻