Binder梳理

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
整體架構(gòu)
三、應(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)系圖,搞定!


進(jìn)程線程關(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_write_read數(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。剛好寫完一半,接下來就不打算寫了,照貓畫虎,原理是相似的。
接下來附上幾張圖:


數(shù)據(jù)轉(zhuǎn)換圖
內(nèi)存轉(zhuǎn)移關(guān)系圖
整體調(diào)用流程圖

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者。

友情鏈接更多精彩內(nèi)容