Android 進(jìn)程中binder主線程啟動(dòng)流程

無論是在Activity啟動(dòng)流程 上篇(Android 10)還是在SystemServer啟動(dòng)流程中都有提到秀睛,在fork出進(jìn)程之后都會(huì)調(diào)用到ZygoteInit.nativeZygoteInit()也就是啟動(dòng)binder相關(guān)的內(nèi)容。本篇將從這個(gè)函數(shù)開始宙橱,詳細(xì)講講這個(gè)函數(shù)到底是如何開啟binder的。

該函數(shù)在\frameworks\base\core\java\com\android\internal\os\ZygoteInit.java的聲明如下:

private static final native void nativeZygoteInit();

是一個(gè)本地方法呕臂,在\frameworks\base\cmds\app_process\app_main.cpp中:

    virtual void onZygoteInit()
    {
        sp<ProcessState> proc = ProcessState::self();
        ALOGV("App process: starting thread pool.\n");
        proc->startThreadPool();
    }

首先來看ProcessState::self()

sp<ProcessState> ProcessState::self()
{
    Mutex::Autolock _l(gProcessMutex);
    if (gProcess != nullptr) {
        return gProcess;
    }
    gProcess = new ProcessState(DEFAULT_BINDER_VM_SIZE);
    return gProcess;
}

這里單例創(chuàng)建了ProcessState朗儒,傳入了一個(gè)參數(shù)DEFAULT_BINDER_VM_SIZE拾稳,其定義為:

#define DEFAULT_BINDER_VM_SIZE ((1 * 1024 * 1024) - sysconf(_SC_PAGE_SIZE) * 2)

也就是我們常說的1M-8K吮炕,ProcessState的構(gòu)造函數(shù)如下:

ProcessState::ProcessState(size_t mmap_size)
    : mDriverFD(open_driver())
    , mVMStart(MAP_FAILED)
    , mThreadCountLock(PTHREAD_MUTEX_INITIALIZER)
    , mThreadCountDecrement(PTHREAD_COND_INITIALIZER)
    , mExecutingThreadsCount(0)
    , mMaxThreads(DEFAULT_MAX_BINDER_THREADS)
    , mStarvationStartTimeMs(0)
    , mManagesContexts(false)
    , mBinderContextCheckFunc(nullptr)
    , mBinderContextUserData(nullptr)
    , mThreadPoolStarted(false)
    , mSpawnThreadOnStart(true)
    , mThreadPoolSeq(1)
    , mMmapSize(mmap_size)
    , mCallRestriction(CallRestriction::NONE)
{
    if (mDriverFD >= 0) {
        // mmap the binder, providing a chunk of virtual address space to receive transactions.
        mVMStart = mmap(nullptr, mMmapSize, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
        if (mVMStart == MAP_FAILED) {
            // *sigh*
            ALOGE("Mmapping /dev/hwbinder failed: %s\n", strerror(errno));
            close(mDriverFD);
            mDriverFD = -1;
        }
    }
    else {
        ALOGE("Binder driver could not be opened.  Terminating.");
    }
}

這里初始化了一系列參數(shù),最重要的就是第二個(gè)參數(shù)mDriverFD(open_driver())访得,其打開了Binder驅(qū)動(dòng)并設(shè)置了一些值:

static int open_driver()
{
    //打開binder驅(qū)動(dòng)
    int fd = open("/dev/hwbinder", O_RDWR | O_CLOEXEC);
    if (fd >= 0) {
        int vers = 0;
        //發(fā)送binder版本
        status_t result = ioctl(fd, BINDER_VERSION, &vers);
        if (result == -1) {
            ALOGE("Binder ioctl to obtain version failed: %s", strerror(errno));
            close(fd);
            fd = -1;
        }
        if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) {
          ALOGE("Binder driver protocol(%d) does not match user space protocol(%d)!", vers, BINDER_CURRENT_PROTOCOL_VERSION);
            close(fd);
            fd = -1;
        }
        size_t maxThreads = DEFAULT_MAX_BINDER_THREADS;
        //發(fā)送binder最大線程
        result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);
        if (result == -1) {
            ALOGE("Binder ioctl to set max threads failed: %s", strerror(errno));
        }
    } else {
        ALOGW("Opening '/dev/hwbinder' failed: %s\n", strerror(errno));
    }
    return fd;
}

回到上面的onZygoteInit()函數(shù)龙亲,該函數(shù)調(diào)用完ProcessState::self()之后調(diào)用了proc->startThreadPool()陕凹,也就是ProcessStatestartThreadPool函數(shù):

void ProcessState::startThreadPool()
{
    AutoMutex _l(mLock);
    if (!mThreadPoolStarted) {
        mThreadPoolStarted = true;
        if (mSpawnThreadOnStart) {
            //spawn:產(chǎn)卵,產(chǎn)生Binder線程池線程鳄炉,和zygote意思差不多杜耙,還是比較形象的。
            spawnPooledThread(true);
        }
    }
}
void ProcessState::spawnPooledThread(bool isMain)
{
    if (mThreadPoolStarted) {
        String8 name = makeBinderThreadName();
        ALOGV("Spawning new pooled thread, name=%s\n", name.string());
        sp<Thread> t = new PoolThread(isMain);
        t->run(name.string());
    }
}

這里首先創(chuàng)建了PoolThread拂盯,之后調(diào)用了其run方法佑女,這個(gè)線程了定義如下,繼承了Thread類谈竿,這個(gè)Thread類是android自己定義的珊豹,路徑為\system\libhwbinder\ProcessState.cpp,這里可以簡(jiǎn)單的認(rèn)為榕订,其會(huì)運(yùn)行threadLoop中的內(nèi)容即可,如果想了解更多信息的話蜕便,可以參考Android Framework中的線程Thread及它的threadLoop方法

class PoolThread : public Thread
{
public:
    explicit PoolThread(bool isMain)
        : mIsMain(isMain)
    {
    }

protected:
    virtual bool threadLoop()
    {
        IPCThreadState::self()->joinThreadPool(mIsMain);
        return false;
    }

    const bool mIsMain;
};

IPCThreadState::self()內(nèi)容如下

static pthread_mutex_t gTLSMutex = PTHREAD_MUTEX_INITIALIZER;
static bool gHaveTLS = false;
static pthread_key_t gTLS = 0;
static bool gShutdown = false;

IPCThreadState* IPCThreadState::self()
{
    if (gHaveTLS) {
restart:
        const pthread_key_t k = gTLS;
        IPCThreadState* st = (IPCThreadState*)pthread_getspecific(k);
        if (st) return st;
        return new IPCThreadState;
    }

    if (gShutdown) {
        ALOGW("Calling IPCThreadState::self() during shutdown is dangerous, expect a crash.\n");
        return nullptr;
    }

    pthread_mutex_lock(&gTLSMutex);
    if (!gHaveTLS) {
        int key_create_value = pthread_key_create(&gTLS, threadDestructor);
        if (key_create_value != 0) {
            pthread_mutex_unlock(&gTLSMutex);
            ALOGW("IPCThreadState::self() unable to create TLS key, expect a crash: %s\n",
                    strerror(key_create_value));
            return nullptr;
        }
        gHaveTLS = true;
    }
    pthread_mutex_unlock(&gTLSMutex);
    goto restart;
}

該函數(shù)gHaveTLS初始值為false劫恒,所以先直接來到最后一個(gè)if判斷,首先創(chuàng)建一個(gè)ThreadLocal key轿腺,這里和java的ThreadLocal key一個(gè)道理两嘴,然后返回到restart,獲取這個(gè)key對(duì)應(yīng)的IPCThreadState如果為null族壳,則創(chuàng)建一個(gè)憔辫,所以可以明白,每個(gè)線程都有自己的一個(gè)IPCThreadState仿荆,如果沒有贰您,則創(chuàng)建,當(dāng)然這一點(diǎn)從名字也可以看出來拢操。
回到上面IPCThreadState::self()調(diào)用完成锦亦,需要調(diào)用IPCThreadStatejoinThreadPool函數(shù)了:

void IPCThreadState::joinThreadPool(bool isMain)
{
    LOG_THREADPOOL("**** THREAD %p (PID %d) IS JOINING THE THREAD POOL\n", (void*)pthread_self(), getpid());
    //告訴驅(qū)動(dòng)線程將進(jìn)入循環(huán)
    mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);

    status_t result;
    mIsLooper = true;
    do {
        processPendingDerefs();
        // now get the next command to be processed, waiting if necessary
        //獲取和執(zhí)行命令 
        result = getAndExecuteCommand();

        if (result < NO_ERROR && result != TIMED_OUT && result != -ECONNREFUSED && result != -EBADF) {
            ALOGE("getAndExecuteCommand(fd=%d) returned unexpected error %d, aborting",
                  mProcess->mDriverFD, result);
            abort();
        }

        // Let this thread exit the thread pool if it is no longer
        // needed and it is not the main process thread.
        if(result == TIMED_OUT && !isMain) {
            break;
        }
    } while (result != -ECONNREFUSED && result != -EBADF);

    LOG_THREADPOOL("**** THREAD %p (PID %d) IS LEAVING THE THREAD POOL err=%d\n",
        (void*)pthread_self(), getpid(), result);

    mOut.writeInt32(BC_EXIT_LOOPER);
    mIsLooper = false;
    talkWithDriver(false);
}

在這里等待和binder驅(qū)動(dòng)傳送命令過來,并執(zhí)行(詳見getAndExecuteCommand()令境,不再贅述)杠园,如果不是主線程則退出循環(huán),主線程則繼續(xù)循環(huán)舔庶。至此抛蚁,進(jìn)程中的binder線程就啟動(dòng)完成了。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末惕橙,一起剝皮案震驚了整個(gè)濱河市瞧甩,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌弥鹦,老刑警劉巖亲配,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡吼虎,警方通過查閱死者的電腦和手機(jī)犬钢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來思灰,“玉大人玷犹,你說我怎么就攤上這事∪骶危” “怎么了歹颓?”我有些...
    開封第一講書人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)油湖。 經(jīng)常有香客問我巍扛,道長(zhǎng),這世上最難降的妖魔是什么乏德? 我笑而不...
    開封第一講書人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任撤奸,我火速辦了婚禮,結(jié)果婚禮上喊括,老公的妹妹穿的比我還像新娘胧瓜。我一直安慰自己,他們只是感情好郑什,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開白布府喳。 她就那樣靜靜地躺著,像睡著了一般蘑拯。 火紅的嫁衣襯著肌膚如雪钝满。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,772評(píng)論 1 290
  • 那天申窘,我揣著相機(jī)與錄音舱沧,去河邊找鬼。 笑死偶洋,一個(gè)胖子當(dāng)著我的面吹牛熟吏,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播玄窝,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼牵寺,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了恩脂?” 一聲冷哼從身側(cè)響起帽氓,我...
    開封第一講書人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎俩块,沒想到半個(gè)月后黎休,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體浓领,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年势腮,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了联贩。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡捎拯,死狀恐怖泪幌,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情署照,我是刑警寧澤祸泪,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站建芙,受9級(jí)特大地震影響没隘,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜禁荸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一右蒲、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧屡限,春花似錦、人聲如沸炕倘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽罩旋。三九已至啊央,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間涨醋,已是汗流浹背瓜饥。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留浴骂,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓溯警,卻偏偏與公主長(zhǎng)得像趣苏,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子梯轻,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348

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