架構(gòu)設(shè)計(jì)分析(三)Android 9.0 Binder機(jī)制

來點(diǎn)前奏說明

當(dāng)你打開這個(gè)文檔的時(shí)候侣监,你已經(jīng)做好準(zhǔn)備了鸭轮,話不多說開搞。
本文以Android 9.0 版本進(jìn)行分析橄霉,當(dāng)然你也可以在線看源碼
在線源碼查看
kernel_3.18 在線源碼
Android源碼下載編譯
9.0源碼下載鏈接 提取碼:d0ks
在此特別說明窃爷,這篇文檔參考了很多文章和視頻,主要自己記錄一下,有些我也不明白按厘。

產(chǎn)生Binder原因:
  • 為了多進(jìn)程間的通信
進(jìn)程間通訊為什么選擇使用Binder:
  • 性能上:Binder相對(duì)出傳統(tǒng)的Socket方式医吊,更加高效。Binder數(shù)據(jù)拷貝只需要一次逮京,而管道卿堂、消息隊(duì)列、Socket都需要2次
  • 安全方面:Binder機(jī)制從協(xié)議本身就支持對(duì)通信雙方做身份校檢懒棉,因而大大提升了安全性草描。
Binder是什么:
  • 從IPC角度說:它是Android中一種跨進(jìn)程通信方式,是Android獨(dú)有的
  • App層:它是客戶端和服務(wù)端進(jìn)行通信的媒介策严,當(dāng)bindService時(shí)候穗慕,服務(wù)端返回一個(gè)包含了服務(wù)器端業(yè)務(wù)調(diào)用的Binder對(duì)象,通過這個(gè)對(duì)象妻导,客戶端可以獲取服務(wù)端提供的服務(wù)或者數(shù)據(jù)揍诽,這里的服務(wù)包括普通服務(wù)和基于AIDL的服務(wù)。
  • Framework層:它是各種Manager(ActivityManager/WindowManager等)和XxxManagerService(ActivityManagerService/WindowManagerService等)的橋梁栗竖。
  • Native層:它是創(chuàng)建Service Manager以及BpBinder/BBinder模型,搭建和Kernel層的橋梁渠啤。
  • Kernel層:它提供了最底層的數(shù)據(jù)傳遞狐肢、對(duì)象標(biāo)識(shí)、線程管理沥曹、調(diào)用過程控制等功能份名。驅(qū)動(dòng)層是整個(gè)Binder機(jī)制的核心。它還可以理解為一種虛擬的數(shù)據(jù)設(shè)備妓美,它的設(shè)備驅(qū)動(dòng)是別dev/binder
核心類圖
核心類圖
Binder架構(gòu)
Binder架構(gòu)圖
源碼分析

Framework層

 frameworks/base/core/jni/AndroidRuntime.cpp的start方法開始
/*
 * Start the Android runtime.  This involves starting the virtual machine
 * and calling the "static void main(String[] args)" method in the class
 * named by "className".
 *
 * Passes the main function two arguments, the class name and the specified
 * options string.
 */
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
    ALOGD(">>>>>> START %s uid %d <<<<<<\n",
            className != NULL ? className : "(unknown)", getuid());

    static const String8 startSystemServer("start-system-server");

    /*
     * 'startSystemServer == true' means runtime is obsolete and not run from
     * init.rc anymore, so we print out the boot start event here.
     */
    for (size_t i = 0; i < options.size(); ++i) {
        if (options[i] == startSystemServer) {
           /* track our progress through the boot sequence */
           const int LOG_BOOT_PROGRESS_START = 3000;
           LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START,  ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
        }
    }

    const char* rootDir = getenv("ANDROID_ROOT");
    if (rootDir == NULL) {
        rootDir = "/system";
        if (!hasDir("/system")) {
            LOG_FATAL("No root directory specified, and /android does not exist.");
            return;
        }
        setenv("ANDROID_ROOT", rootDir, 1);
    }

    //const char* kernelHack = getenv("LD_ASSUME_KERNEL");
    //ALOGD("Found LD_ASSUME_KERNEL='%s'\n", kernelHack);

    /* start the virtual machine */
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);
    JNIEnv* env;
    if (startVm(&mJavaVM, &env, zygote) != 0) {
        return;
    }
    onVmCreated(env);

    /*
     * Register android functions.
     */
    if (startReg(env) < 0) {
        ALOGE("Unable to register all android natives\n");
        return;
    }

    /*
     * We want to call main() with a String array with arguments in it.
     * At present we have two arguments, the class name and an option string.
     * Create an array to hold them.
     */
    jclass stringClass;
    jobjectArray strArray;
    jstring classNameStr;

    stringClass = env->FindClass("java/lang/String");
    assert(stringClass != NULL);
    strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);
    assert(strArray != NULL);
    classNameStr = env->NewStringUTF(className);
    assert(classNameStr != NULL);
    env->SetObjectArrayElement(strArray, 0, classNameStr);

    for (size_t i = 0; i < options.size(); ++i) {
        jstring optionsStr = env->NewStringUTF(options.itemAt(i).string());
        assert(optionsStr != NULL);
        env->SetObjectArrayElement(strArray, i + 1, optionsStr);
    }

    /*
     * Start VM.  This thread becomes the main thread of the VM, and will
     * not return until the VM exits.
     */
    char* slashClassName = toSlashClassName(className != NULL ? className : "");
    jclass startClass = env->FindClass(slashClassName);
    if (startClass == NULL) {
        ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
        /* keep going */
    } else {
        jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
            "([Ljava/lang/String;)V");
        if (startMeth == NULL) {
            ALOGE("JavaVM unable to find main() in '%s'\n", className);
            /* keep going */
        } else {
            env->CallStaticVoidMethod(startClass, startMeth, strArray);

#if 0
            if (env->ExceptionCheck())
                threadExitUncaughtException(env);
#endif
        }
    }
    free(slashClassName);

    ALOGD("Shutting down VM\n");
    if (mJavaVM->DetachCurrentThread() != JNI_OK)
        ALOGW("Warning: unable to detach main thread\n");
    if (mJavaVM->DestroyJavaVM() != 0)
        ALOGW("Warning: VM did not shut down cleanly\n");
}

startReg 在Android系統(tǒng)開機(jī)過程中僵腺,Zygote啟動(dòng)會(huì)有一個(gè)虛擬機(jī)注冊(cè)過程,該過程調(diào)用了該方法完成了jni的注冊(cè)

/*
 * Register android native functions with the VM.
 */
/*static*/ int AndroidRuntime::startReg(JNIEnv* env)
{
    ATRACE_NAME("RegisterAndroidNatives");
    /*
     * This hook causes all future threads created in this process to be
     * attached to the JavaVM.  (This needs to go away in favor of JNI
     * Attach calls.)
     */
    androidSetCreateThreadFunc((android_create_thread_fn) javaCreateThreadEtc);

    ALOGV("--- registering native functions ---\n");

    /*
     * Every "register" function calls one or more things that return
     * a local reference (e.g. FindClass).  Because we haven't really
     * started the VM yet, they're all getting stored in the base frame
     * and never released.  Use Push/Pop to manage the storage.
     */
    env->PushLocalFrame(200);

    if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {
        env->PopLocalFrame(NULL);
        return -1;
    }
    env->PopLocalFrame(NULL);

    //createJavaThread("fubar", quickTest, (void*) "hello");

    return 0;
}

Native層(System Manager)

1 壶栋、啟動(dòng)Service Manager時(shí)序圖

啟動(dòng)Service Manager時(shí)序圖
  • 啟動(dòng)大致從 frameworks/native/cmds/service_manager.c這個(gè)文件開始
  • main方法開始
  • binder_open 打開binder驅(qū)動(dòng)
  • binder_become_context_manager 注冊(cè)成為binder服務(wù)
  • binder_loop 進(jìn)入無限循環(huán)辰如,處理客戶client端發(fā)來的請(qǐng)求
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";
    }

    bs = binder_open(driver, 128*1024);
    if (!bs) {
#ifdef VENDORSERVICEMANAGER
        ALOGW("failed to open binder driver %s\n", driver);
        while (true) {
            sleep(UINT_MAX);
        }
#else
        ALOGE("failed to open binder driver %s\n", driver);
#endif
        return -1;
    }

    if (binder_become_context_manager(bs)) {
        ALOGE("cannot become context manager (%s)\n", strerror(errno));
        return -1;
    }

    cb.func_audit = audit_callback;
    selinux_set_callback(SELINUX_CB_AUDIT, cb);
    cb.func_log = selinux_log_callback;
    selinux_set_callback(SELINUX_CB_LOG, cb);

#ifdef VENDORSERVICEMANAGER
    sehandle = selinux_android_vendor_service_context_handle();
#else
    sehandle = selinux_android_service_context_handle();
#endif
    selinux_status_open(true);

    if (sehandle == NULL) {
        ALOGE("SELinux: Failed to acquire sehandle. Aborting.\n");
        abort();
    }

    if (getcon(&service_manager_context) != 0) {
        ALOGE("SELinux: Failed to acquire service_manager context. Aborting.\n");
        abort();
    }


    binder_loop(bs, svcmgr_handler);

    return 0;
}

2、獲取Service Manager時(shí)序圖


獲取Service Manager時(shí)序圖
  • 獲取Service Manager大致從 frameworks/native/libs/binder/IServiceManager.cpp 這個(gè)文件開始
sp<IServiceManager> defaultServiceManager()
{
    if (gDefaultServiceManager != NULL) return gDefaultServiceManager;

    {
        AutoMutex _l(gDefaultServiceManagerLock);
        while (gDefaultServiceManager == NULL) {
            gDefaultServiceManager = interface_cast<IServiceManager>(
                ProcessState::self()->getContextObject(NULL));
            if (gDefaultServiceManager == NULL)
                sleep(1);
        }
    }
    return gDefaultServiceManager;
}
  • frameworks/native/libs/binder/ProcessState.cpp
    ProcessState::self()獲取ProcessState單例對(duì)象贵试,每個(gè)進(jìn)程有且只有一個(gè)ProcessState對(duì)象琉兜,存在則返回,否則就創(chuàng)建毙玻。
sp<ProcessState> ProcessState::self()
{
    Mutex::Autolock _l(gProcessMutex);
    if (gProcess != NULL) {
        return gProcess;
    }
    gProcess = new ProcessState("/dev/binder");
    return gProcess;
}

getContextObject() 用于獲取BpBinder對(duì)象豌蟋,對(duì)于handle=0的BpBinder對(duì)象,存在就返回否則創(chuàng)建

sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& /*caller*/)
{
    return getStrongProxyForHandle(0);
}
sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
    sp<IBinder> result;

    AutoMutex _l(mLock);

    handle_entry* e = lookupHandleLocked(handle);

    if (e != NULL) {
        // We need to create a new BpBinder if there isn't currently one, OR we
        // are unable to acquire a weak reference on this current one.  See comment
        // in getWeakProxyForHandle() for more info about this.
        IBinder* b = e->binder;
        if (b == NULL || !e->refs->attemptIncWeak(this)) {
            if (handle == 0) {
                // Special case for context manager...
                // The context manager is the only object for which we create
                // a BpBinder proxy without already holding a reference.
                // Perform a dummy transaction to ensure the context manager
                // is registered before we create the first local reference
                // to it (which will occur when creating the BpBinder).
                // If a local reference is created for the BpBinder when the
                // context manager is not present, the driver will fail to
                // provide a reference to the context manager, but the
                // driver API does not return status.
                //
                // Note that this is not race-free if the context manager
                // dies while this code runs.
                //
                // TODO: add a driver API to wait for context manager, or
                // stop special casing handle 0 for context manager and add
                // a driver API to get a handle to the context manager with
                // proper reference counting.

                Parcel data;
                status_t status = IPCThreadState::self()->transact(
                        0, IBinder::PING_TRANSACTION, data, NULL, 0);
                if (status == DEAD_OBJECT)
                   return NULL;
            }

            b = BpBinder::create(handle);
            e->binder = b;
            if (b) e->refs = b->getWeakRefs();
            result = b;
        } else {
            // This little bit of nastyness is to allow us to add a primary
            // reference to the remote proxy when this team doesn't have one
            // but another team is sending the handle to us.
            result.force_set(b);
            e->refs->decWeak(this);
        }
    }

    return result;
}

3桑滩、注冊(cè)服務(wù)(對(duì)照架構(gòu)圖1.注冊(cè)服務(wù))

注冊(cè)服務(wù).jpg
  • 注冊(cè)服務(wù)大致從 frameworks/native/libs/binder/IServiceManager.cpp 這個(gè)文件開始分析
    virtual status_t addService(const String16& name, const sp<IBinder>& service,
                                bool allowIsolated, int dumpsysPriority) {
        Parcel data, reply;
        data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
        data.writeString16(name);
        data.writeStrongBinder(service);
        data.writeInt32(allowIsolated ? 1 : 0);
        data.writeInt32(dumpsysPriority);
        status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
        return err == NO_ERROR ? reply.readExceptionCode() : err;
    }

Kernel層:

  • 大致從 kernel3.18/driver/staging/android/binder.c 這個(gè)文件分析
  • binder_init 驅(qū)動(dòng)設(shè)備初始化方法
  • binder_open 打開binder驅(qū)動(dòng)設(shè)備
  • binder_mmap 實(shí)現(xiàn)用戶空間Buffer和內(nèi)存空間的Buffer 同步操作梧疲。在內(nèi)存虛擬地址空間,申請(qǐng)一塊與用戶虛擬內(nèi)存相同大小的內(nèi)存;再申請(qǐng)一個(gè)page大小的物理內(nèi)存幌氮,將同一塊物理內(nèi)存分別映射到內(nèi)存虛擬地址空間和用戶虛擬空間去實(shí)現(xiàn)的缭受。
  • binder_ioctl 數(shù)據(jù)操作
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市浩销,隨后出現(xiàn)的幾起案子贯涎,更是在濱河造成了極大的恐慌,老刑警劉巖慢洋,帶你破解...
    沈念sama閱讀 219,110評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件塘雳,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡普筹,警方通過查閱死者的電腦和手機(jī)败明,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來太防,“玉大人妻顶,你說我怎么就攤上這事⊙殉担” “怎么了讳嘱?”我有些...
    開封第一講書人閱讀 165,474評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)酿愧。 經(jīng)常有香客問我沥潭,道長(zhǎng),這世上最難降的妖魔是什么嬉挡? 我笑而不...
    開封第一講書人閱讀 58,881評(píng)論 1 295
  • 正文 為了忘掉前任钝鸽,我火速辦了婚禮,結(jié)果婚禮上庞钢,老公的妹妹穿的比我還像新娘拔恰。我一直安慰自己,他們只是感情好基括,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,902評(píng)論 6 392
  • 文/花漫 我一把揭開白布颜懊。 她就那樣靜靜地躺著,像睡著了一般风皿。 火紅的嫁衣襯著肌膚如雪饭冬。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,698評(píng)論 1 305
  • 那天揪阶,我揣著相機(jī)與錄音昌抠,去河邊找鬼。 笑死鲁僚,一個(gè)胖子當(dāng)著我的面吹牛炊苫,可吹牛的內(nèi)容都是我干的裁厅。 我是一名探鬼主播,決...
    沈念sama閱讀 40,418評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼侨艾,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼执虹!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起唠梨,我...
    開封第一講書人閱讀 39,332評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤袋励,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后当叭,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體茬故,經(jīng)...
    沈念sama閱讀 45,796評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,968評(píng)論 3 337
  • 正文 我和宋清朗相戀三年蚁鳖,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了磺芭。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,110評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡醉箕,死狀恐怖钾腺,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情讥裤,我是刑警寧澤放棒,帶...
    沈念sama閱讀 35,792評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站己英,受9級(jí)特大地震影響间螟,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜剧辐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,455評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望邮府。 院中可真熱鬧荧关,春花似錦、人聲如沸褂傀。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽仙辟。三九已至同波,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間叠国,已是汗流浹背未檩。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留粟焊,地道東北人冤狡。 一個(gè)月前我還...
    沈念sama閱讀 48,348評(píng)論 3 373
  • 正文 我出身青樓孙蒙,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親悲雳。 傳聞我的和親對(duì)象是個(gè)殘疾皇子挎峦,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,047評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容