Binder原理是很清楚,但是調(diào)用細(xì)節(jié)每次看了又忘,好吧,干脆再寫篇文章梳理一次,也方便之后查閱。
一、定義
Binder: Android平臺上的一種跨進(jìn)程通信的方式。
二、為什么選Binder:
- 內(nèi)存拷貝1次:比socket 2次好,比共享內(nèi)存0次差,總體算是不錯的;
- 基于C/S架構(gòu),結(jié)構(gòu)清晰,兩端相對獨立,適合Android架構(gòu);
- 安全性:通信雙方身份驗證,可獲取UID/PID;
綜合考慮選擇Binder

三、應(yīng)用層到Binder驅(qū)動調(diào)用流程
以getMemoryInfo為例梳理Binder調(diào)用流程:代碼 Android N(為什么用N的代碼,因為O之后AMS的binder調(diào)用改成了aidl方式了,具體Stub文件只能在編譯的時候才會生成,太麻煩還不如看N來得直接,反正原理都一樣。)
#ActivityManager
public void getMemoryInfo(MemoryInfo outInfo) {
2367 try {
2368 ActivityManagerNative.getDefault().getMemoryInfo(outInfo);
2369 } catch (RemoteException e) {
2370 throw e.rethrowFromSystemServer();
2371 }
2372 }
#ActivityManagerNative
88 static public IActivityManager getDefault() {
89 return gDefault.get();
90 }
private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
3095 protected IActivityManager create() {
3096 IBinder b = ServiceManager.getService("activity");
3097 if (false) {
3098 Log.v("ActivityManager", "default service binder = " + b);
3099 }
3100 IActivityManager am = asInterface(b);
3101 if (false) {
3102 Log.v("ActivityManager", "default service = " + am);
3103 }
3104 return am;
3105 }
3106 };
先獲取AMS向ServiceManager注冊的BinderProxy, 系統(tǒng)服務(wù)需要先在ServiceManager注冊然后才能被獲取到使用,而APP的組件service只需要向AMS注冊就好了。
static public IActivityManager asInterface(IBinder obj) {
73 if (obj == null) {
74 return null;
75 }
76 IActivityManager in =
77 (IActivityManager)obj.queryLocalInterface(descriptor);
78 if (in != null) {
79 return in;
80 }
81
82 return new ActivityManagerProxy(obj);
83
創(chuàng)建ActivityManagerProxy,并傳入BinderProxy, asInterface相當(dāng)于獲取本地服務(wù) or 遠(yuǎn)程服務(wù)代理,(queryLocalInterfacec出來就是本地服務(wù))。
ActivityManagerProxy中有對應(yīng)的方法,mRemote就是proxy初始化時傳入的BinderProxy。
#ActivityManagerProxy
public void getMemoryInfo(ActivityManager.MemoryInfo outInfo) throws RemoteException {
5006 Parcel data = Parcel.obtain();
5007 Parcel reply = Parcel.obtain();
5008 data.writeInterfaceToken(IActivityManager.descriptor);
5009 mRemote.transact(GET_MEMORY_INFO_TRANSACTION, data, reply, 0);
5010 reply.readException();
5011 outInfo.readFromParcel(reply);
5012 data.recycle();
5013 reply.recycle();
5014 }
打包數(shù)據(jù)為Parcel,并通過binder調(diào)用transact傳遞數(shù)據(jù),這里選擇Parcel目的主要有兩點:
- 跨進(jìn)程服務(wù)包含不同接口,每個接口的參數(shù)數(shù)量和類型都不一樣,這里Parcel提供了所有基本數(shù)據(jù)類型的讀寫接口,對于非基本數(shù)據(jù)類型需要開發(fā)者拆分為然后寫入到Parcel中(讀也一樣)。
- Parcel可以打包為一個整體在進(jìn)程間通信。
frameworks/base/core/java/android/os/Binder.java
final class BinderProxy implements IBinder {
...
public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
if (Binder.isTracingEnabled()) { Binder.getTransactionTracker().addTrace(); }
return transactNative(code, data, reply, flags);
}
...
}
transactNative是native方法,那就走jni.
frameworks/base/core/jni/android_util_Binder.cpp
static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
jint code, jobject dataObj, jobject replyObj, jint flags) // throws RemoteException
{
Parcel* data = parcelForJavaObject(env, dataObj);
if (data == NULL) {
return JNI_FALSE;
}
Parcel* reply = parcelForJavaObject(env, replyObj);
if (reply == NULL && replyObj != NULL) {
return JNI_FALSE;
}
...
IBinder* target = (IBinder*)
env->GetLongField(obj, gBinderProxyOffsets.mObject);
...
status_t err = target->transact(code, *data, reply, flags);
...
return JNI_FALSE;
}
這里就是把java數(shù)據(jù)轉(zhuǎn)為C++數(shù)據(jù),然后交給BpBinder。通過JNI之后,正式進(jìn)入Native層。
frameworks/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;
return status;
}
return DEAD_OBJECT;
}
這里直接調(diào)用IPCThreadState對應(yīng)的方法來發(fā)送請求到binder驅(qū)動。IPCThreadState是一個線程的實例。
這里簡單捋一下進(jìn)程與線程的關(guān)系:
ProcessState表示當(dāng)前binder請求對應(yīng)的進(jìn)程
frameworks/native/libs/binder/ProcessState.cpp
#define DEFAULT_MAX_BINDER_THREADS 15
ProcessState::ProcessState(const char *driver)
: mDriverName(String8(driver))
, mDriverFD(open_driver(driver)) //open dev/binder驅(qū)動
, mVMStart(MAP_FAILED)
, mThreadCountLock(PTHREAD_MUTEX_INITIALIZER)
, mThreadCountDecrement(PTHREAD_COND_INITIALIZER)
, mExecutingThreadsCount(0)
, mMaxThreads(DEFAULT_MAX_BINDER_THREADS)//設(shè)置支持的最大線程數(shù)15
, mStarvationStartTimeMs(0)
, mManagesContexts(false)
, mBinderContextCheckFunc(NULL)
, mBinderContextUserData(NULL)
, mThreadPoolStarted(false)
, mThreadPoolSeq(1)
{
if (mDriverFD >= 0) {
mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
if (mVMStart == MAP_FAILED) {
// *sigh*
ALOGE("Using /dev/binder failed: unable to mmap transaction memory.\n");
close(mDriverFD);
mDriverFD = -1;
mDriverName.clear();
}
}
LOG_ALWAYS_FATAL_IF(mDriverFD < 0, "Binder driver could not be opened. Terminating.");
}
這里最重要的原則就是:任何使用Binder機制的進(jìn)程都必須要對/dev/binder設(shè)備進(jìn)行open和mmap才能使用!跟驅(qū)動通信你總得把驅(qū)動打開把,當(dāng)然open驅(qū)動的過程會有一些初始化工作,比如創(chuàng)建binder_proc進(jìn)程對象等,這里不鋪開了,另外你還需要一塊buffer放數(shù)據(jù)吧,mmap申請一塊物理內(nèi)存,用戶空間與內(nèi)核空間同時映射到這塊內(nèi)存,這也就是為什么內(nèi)存只拷貝1次的原因。
看了進(jìn)程再回到線程:IPCThreadState,具體通信細(xì)節(jié)交給線程來處理。
好再看一張關(guān)系圖,搞定!

那么接下來看IPCThreadState執(zhí)行transact做了什么
status_t IPCThreadState::transact(int32_t handle,
uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags)
{
status_t err = data.errorCheck();
...
if (err == NO_ERROR) {
err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
}
...
if ((flags & TF_ONE_WAY) == 0) {
...
if (reply) {
err = waitForResponse(reply);
} else {
Parcel fakeReply;
err = waitForResponse(&fakeReply);
}
...
return err;
}
這里主要就兩點:writeTransactionData,寫入數(shù)據(jù)打包成binder_transaction_data數(shù)據(jù)結(jié)構(gòu),然后通過waitForResponse等待返回結(jié)果,如果是one way的就不等了。
另外這里開始用到了Binder協(xié)議,因為馬上就要跟kernel binder通信了,進(jìn)入人家的地盤得按它的規(guī)矩來,Binder協(xié)議分為控制協(xié)議和驅(qū)動協(xié)議,控制協(xié)議就是通過ioctl(syscall)與Binder通信的協(xié)議。
具體協(xié)議參考之前文章:Android通信方式篇(五)-Binder機制(Kernel層)
繼續(xù)看:
status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags,
int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer)
{
binder_transaction_data tr;
tr.target.ptr = 0; /* Don't pass uninitialized stack data to a remote process */
tr.target.handle = handle;
tr.code = code;
tr.flags = binderFlags;
tr.cookie = 0;
tr.sender_pid = 0;
tr.sender_euid = 0;
const status_t err = data.errorCheck();
if (err == NO_ERROR) {
tr.data_size = data.ipcDataSize();
tr.data.ptr.buffer = data.ipcData();
tr.offsets_size = data.ipcObjectsCount()*sizeof(binder_size_t);
tr.data.ptr.offsets = data.ipcObjects();
} else if (statusBuffer) {
tr.flags |= TF_STATUS_CODE;
*statusBuffer = err;
tr.data_size = sizeof(status_t);
tr.data.ptr.buffer = reinterpret_cast<uintptr_t>(statusBuffer);
tr.offsets_size = 0;
tr.data.ptr.offsets = 0;
} else {
return (mLastError = err);
}
mOut.writeInt32(cmd);
mOut.write(&tr, sizeof(tr));
return NO_ERROR;
}
writeTransactionData打包好數(shù)據(jù):binder_transaction_data,并通過mOut.write,這就是向mmap申請的buffer寫數(shù)據(jù)了。
每個IPCThreadState中都有一對Parcel變量:mIn、mOut。相當(dāng)于兩根數(shù)據(jù)管道:
- mIn 用來接收來自Binder設(shè)備的數(shù)據(jù),默認(rèn)大小為256字節(jié);
- mOut用來存儲發(fā)往Binder設(shè)備的數(shù)據(jù),默認(rèn)大小為256字節(jié)。
最后由waitForResponse執(zhí)行talkWithDriver
status_t IPCThreadState::talkWithDriver(bool doReceive)
{
…
ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr)
...
}
這里mProcess->mDriverFD是對應(yīng)打開binder設(shè)備時的fd,BINDER_WRITE_READ對應(yīng)具體的控制協(xié)議(就是告訴binder driver你要干嘛),bwr存儲了類型為binder_write_read的數(shù)據(jù),而binder_write_read詳細(xì)數(shù)據(jù)結(jié)構(gòu)看下圖:

binder_ioctl函數(shù)對應(yīng)了ioctl系統(tǒng)調(diào)用處理。
那么接下來正式進(jìn)入kernel層。
kernel/msm-3.18/drivers/staging/android/binder.c
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
4635{
switch (cmd) {
case BINDER_WRITE_READ:
4661 ret = binder_ioctl_write_read(filp, cmd, arg, thread);
4662 if (ret)
4663 goto err;
4664 break;
...
}
}
對應(yīng)BINDER_WRITE_READ的操作在binder_ioctl_write_read
4497static int binder_ioctl_write_read(struct file *filp,
4498 unsigned int cmd, unsigned long arg,
4499 struct binder_thread *thread)
4500{
4501 int ret = 0;
4502 struct binder_proc *proc = filp->private_data;
4503 unsigned int size = _IOC_SIZE(cmd);
4504 void __user *ubuf = (void __user *)arg;
4505 struct binder_write_read bwr;
...
4511 if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
4512 ret = -EFAULT;
4513 goto out;
4514 }
...
4521 if (bwr.write_size > 0) {
4522 ret = binder_thread_write(proc, thread,
4523 bwr.write_buffer,
4524 bwr.write_size,
4525 &bwr.write_consumed);
...
4533 }
4534 if (bwr.read_size > 0) {
4535 ret = binder_thread_read(proc, thread, bwr.read_buffer,
4536 bwr.read_size,
4537 &bwr.read_consumed,
4538 filp->f_flags & O_NONBLOCK);
...
4549 }
...
4560 return ret;
4561}
如果bar.write.size>0,則調(diào)用binder_thread_write,如果bwr.read_size > 0,則調(diào)用binder_thread_read。
這里我們看binder_thread_write,我們之前封裝的驅(qū)動協(xié)議命令是:BC_TRANSACTION,那么定位到這來:
4static int binder_thread_write(struct binder_proc *proc,
3385 struct binder_thread *thread,
3386 binder_uintptr_t binder_buffer, size_t size,
3387 binder_size_t *consumed)
3388{
…
3611 case BC_TRANSACTION:
3612 case BC_REPLY: {
3613 struct binder_transaction_data tr;
3614
3615 if (copy_from_user(&tr, ptr, sizeof(tr)))
3616 return -EFAULT;
3617 ptr += sizeof(tr);
3618 binder_transaction(proc, thread, &tr,
3619 cmd == BC_REPLY, 0);
3620 break;
3621 }
...
}
執(zhí)行binder_transaction,這個方法是對一次binder事務(wù)的處理。
static void binder_transaction(struct binder_proc *proc,
struct binder_thread *thread,
struct binder_transaction_data *tr, int reply){
//根據(jù)各種判定,獲取以下信息:
struct binder_thread *target_thread; //目標(biāo)線程
struct binder_proc *target_proc; //目標(biāo)進(jìn)程
struct binder_node *target_node; //目標(biāo)binder節(jié)點
struct list_head *target_list; //目標(biāo)TODO隊列
wait_queue_head_t *target_wait; //目標(biāo)等待隊列
...
//分配兩個結(jié)構(gòu)體內(nèi)存
struct binder_transaction *t = kzalloc(sizeof(*t), GFP_KERNEL);
struct binder_work *tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL);
//從target_proc分配一塊buffer【見小節(jié)3.2】
t->buffer = binder_alloc_buf(target_proc, tr->data_size,
for (; offp < off_end; offp++) {
switch (fp->type) {
case BINDER_TYPE_BINDER: ...
case BINDER_TYPE_WEAK_BINDER: ...
case BINDER_TYPE_HANDLE: ...
case BINDER_TYPE_WEAK_HANDLE: ...
case BINDER_TYPE_FD: ...
}
}
//向目標(biāo)進(jìn)程的target_list添加BINDER_WORK_TRANSACTION事務(wù)
t->work.type = BINDER_WORK_TRANSACTION;
list_add_tail(&t->work.entry, target_list);
//向當(dāng)前線程的todo隊列添加BINDER_WORK_TRANSACTION_COMPLETE事務(wù)
tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
list_add_tail(&tcomplete->entry, &thread->todo);
if (target_wait)
wake_up_interruptible(target_wait);
return;
}
將發(fā)起端數(shù)據(jù)拷貝到接收端進(jìn)程的buffer結(jié)構(gòu)體,讓server端去read。剛好寫完一半,接下來就不打算寫了,照貓畫虎,原理是相似的。
接下來附上幾張圖:



更多細(xì)節(jié)參考gityuan.com的binder系列文章。好了天色不早了,狗命要緊!?。?/p>