copy from : http://gityuan.com/2016/10/29/binder-thread-pool/
基於Android 6.0源碼剖析,分析Binder線程池以及binder線程啟動過程。
frameworks/base/cmds/app_process/app_main.cpp
frameworks/native/libs/binder/ProcessState.cpp framework/native/libs/binder/IPCThreadState.cpp kernel/drivers/staging/android/binder.c
一. 概述
Android系統啟動完成后,ActivityManager, PackageManager等各大服務都運行在system_server進程,app應用需要使用系統服務都是通過binder來完成進程之間的通信,上篇文章徹底理解Android Binder通信架構,從整體架構以及通信協議的角度來闡述了Binder架構。那對於binder線程是如何管理的呢,又是如何創建的呢?其實無論是system_server進程,還是app進程,都是在進程fork完成后,便會在新進程中執行onZygoteInit()的過程中,啟動binder線程池。接下來,就以此為起點展開從線程的視角來看看binder的世界。
二. Binder線程創建
Binder線程創建與其所在進程的創建中產生,Java層進程的創建都是通過Process.start()方法,向Zygote進程發出創建進程的socket消息,Zygote收到消息后會調用Zygote.forkAndSpecialize()來fork出新進程,在新進程中會調用到RuntimeInit.nativeZygoteInit
方法,該方法經過jni映射,最終會調用到app_main.cpp中的onZygoteInit,那么接下來從這個方法說起。
2.1 onZygoteInit
[-> app_main.cpp]
virtual void onZygoteInit() { //獲取ProcessState對象 sp<ProcessState> proc = ProcessState::self(); //啟動新binder線程 【見小節2.2】 proc->startThreadPool(); }
ProcessState::self()是單例模式,主要工作是調用open()打開/dev/binder驅動設備,再利用mmap()映射內核的地址空間,將Binder驅動的fd賦值ProcessState對象中的變量mDriverFD,用於交互操作。startThreadPool()是創建一個新的binder線程,不斷進行talkWithDriver()。 詳細過程,見注冊服務的[小節二].
2.2 PS.startThreadPool
[-> ProcessState.cpp]
void ProcessState::startThreadPool() { AutoMutex _l(mLock); //多線程同步 if (!mThreadPoolStarted) { mThreadPoolStarted = true; spawnPooledThread(true); 【見小節2.3】 } }
啟動Binder線程池后, 則設置mThreadPoolStarted=true. 通過變量mThreadPoolStarted來保證每個應用進程只允許啟動一個binder線程池, 且本次創建的是binder主線程(isMain=true). 其余binder線程池中的線程都是由Binder驅動來控制創建的。
2.3 PS.spawnPooledThread
[-> ProcessState.cpp]
void ProcessState::spawnPooledThread(bool isMain) { if (mThreadPoolStarted) { //獲取Binder線程名【見小節2.3.1】 String8 name = makeBinderThreadName(); //此處isMain=true【見小節2.3.2】 sp<Thread> t = new PoolThread(isMain); t->run(name.string()); } }
2.3.1 makeBinderThreadName
[-> ProcessState.cpp]
String8 ProcessState::makeBinderThreadName() {
int32_t s = android_atomic_add(1, &mThreadPoolSeq); String8 name; name.appendFormat("Binder_%X", s); return name; }
獲取Binder線程名,格式為Binder_x
, 其中x為整數。每個進程中的binder編碼是從1開始,依次遞增; 只有通過spawnPooledThread方法來創建的線程才符合這個格式,對於直接將當前線程通過joinThreadPool加入線程池的線程名則不符合這個命名規則。 另外,目前Android N中Binder命令已改為Binder:<pid>_x
格式, 則對於分析問題很有幫忙,通過binder名稱的pid字段可以快速定位該binder線程所屬的進程p.
2.3.2 PoolThread.run
[-> ProcessState.cpp]
class PoolThread : public Thread { public: PoolThread(bool isMain) : mIsMain(isMain) { } protected: virtual bool threadLoop() { IPCThreadState::self()->joinThreadPool(mIsMain); //【見小節2.4】 return false; } const bool mIsMain; };
從函數名看起來是創建線程池,其實就只是創建一個線程,該PoolThread繼承Thread類。t->run()方法最終調用 PoolThread的threadLoop()方法。
2.4 IPC.joinThreadPool
[-> IPCThreadState.cpp]
void IPCThreadState::joinThreadPool(bool isMain) { //創建Binder線程 mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER); set_sched_policy(mMyThreadId, SP_FOREGROUND); //設置前台調度策略 status_t result; do { processPendingDerefs(); //清除隊列的引用[見小節2.5] result = getAndExecuteCommand(); //處理下一條指令[見小節2.6] if (result < NO_ERROR && result != TIMED_OUT && result != -ECONNREFUSED && result != -EBADF) { abort(); } if(result == TIMED_OUT && !isMain) { break; ////非主線程出現timeout則線程退出 } } while (result != -ECONNREFUSED && result != -EBADF); mOut.writeInt32(BC_EXIT_LOOPER); // 線程退出循環 talkWithDriver(false); //false代表bwr數據的read_buffer為空 }
- 對於
isMain
=true的情況下, command為BC_ENTER_LOOPER,代表的是Binder主線程,不會退出的線程; - 對於
isMain
=false的情況下,command為BC_REGISTER_LOOPER,表示是由binder驅動創建的線程。
2.5 processPendingDerefs
[-> IPCThreadState.cpp]
void IPCThreadState::processPendingDerefs() { if (mIn.dataPosition() >= mIn.dataSize()) { size_t numPending = mPendingWeakDerefs.size(); if (numPending > 0) { for (size_t i = 0; i < numPending; i++) { RefBase::weakref_type* refs = mPendingWeakDerefs[i]; refs->decWeak(mProcess.get()); //弱引用減一 } mPendingWeakDerefs.clear(); } numPending = mPendingStrongDerefs.size(); if (numPending > 0) { for (size_t i = 0; i < numPending; i++) { BBinder* obj = mPendingStrongDerefs[i]; obj->decStrong(mProcess.get()); //強引用減一 } mPendingStrongDerefs.clear(); } } }
2.6 getAndExecuteCommand
[-> IPCThreadState.cpp]
status_t IPCThreadState::getAndExecuteCommand()
{
status_t result;
int32_t cmd;
result = talkWithDriver(); //與binder進行交互[見小節2.7] if (result >= NO_ERROR) { size_t IN = mIn.dataAvail(); if (IN < sizeof(int32_t)) return result; cmd = mIn.readInt32(); pthread_mutex_lock(&mProcess->mThreadCountLock); mProcess->mExecutingThreadsCount++; pthread_mutex_unlock(&mProcess->mThreadCountLock); result = executeCommand(cmd); //執行Binder響應碼 [見小節2.8] pthread_mutex_lock(&mProcess->mThreadCountLock); mProcess->mExecutingThreadsCount--; pthread_cond_broadcast(&mProcess->mThreadCountDecrement); pthread_mutex_unlock(&mProcess->mThreadCountLock); set_sched_policy(mMyThreadId, SP_FOREGROUND); } return result; }
2.7 talkWithDriver
//mOut有數據,mIn還沒有數據。doReceive默認值為true status_t IPCThreadState::talkWithDriver(bool doReceive) { binder_write_read bwr; ... // 當同時沒有輸入和輸出數據則直接返回 if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR; ... do { //ioctl執行binder讀寫操作,經過syscall,進入Binder驅動。調用Binder_ioctl if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0) err = NO_ERROR; ... } while (err == -EINTR); ... return err; }
在這里調用的isMain=true,也就是向mOut例如寫入的便是BC_ENTER_LOOPER
. 經過talkWithDriver(), 接下來程序往哪進行呢?在文章徹底理解Android Binder通信架構詳細講解了Binder通信過程,那么從binder_thread_write()
往下說BC_ENTER_LOOPER
的處理過程。
2.7.1 binder_thread_write
[-> binder.c]
static int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread, binder_uintptr_t binder_buffer, size_t size, binder_size_t *consumed) { uint32_t cmd; void __user *buffer = (void __user *)(uintptr_t)binder_buffer; void __user *ptr = buffer + *consumed; void __user *end = buffer + size; while (ptr < end && thread->return_error == BR_OK) { //拷貝用戶空間的cmd命令,此時為BC_ENTER_LOOPER if (get_user(cmd, (uint32_t __user *)ptr)) -EFAULT; ptr += sizeof(uint32_t); switch (cmd) { case BC_REGISTER_LOOPER: if (thread->looper & BINDER_LOOPER_STATE_ENTERED) { //出錯原因:線程調用完BC_ENTER_LOOPER,不能執行該分支 thread->looper |= BINDER_LOOPER_STATE_INVALID; } else if (proc->requested_threads == 0) { //出錯原因:沒有請求就創建線程 thread->looper |= BINDER_LOOPER_STATE_INVALID; } else { proc->requested_threads--; proc->requested_threads_started++; } thread->looper |= BINDER_LOOPER_STATE_REGISTERED; break; case BC_ENTER_LOOPER: if (thread->looper & BINDER_LOOPER_STATE_REGISTERED) { //出錯原因:線程調用完BC_REGISTER_LOOPER,不能立刻執行該分支 thread->looper |= BINDER_LOOPER_STATE_INVALID; } //創建Binder主線程 thread->looper |= BINDER_LOOPER_STATE_ENTERED; break; case BC_EXIT_LOOPER: thread->looper |= BINDER_LOOPER_STATE_EXITED; break; } ... } *consumed = ptr - buffer; } return 0; }
處理完BC_ENTER_LOOPER命令后,一般情況下成功設置thread->looper |= BINDER_LOOPER_STATE_ENTERED
。那么binder線程的創建是在什么時候呢? 那就當該線程有事務需要處理的時候,進入binder_thread_read()過程。
2.7.2 binder_thread_read
binder_thread_read(){
...
retry:
//當前線程todo隊列為空且transaction棧為空,則代表該線程是空閑的 wait_for_proc_work = thread->transaction_stack == NULL && list_empty(&thread->todo); if (thread->return_error != BR_OK && ptr < end) { ... put_user(thread->return_error, (uint32_t __user *)ptr); ptr += sizeof(uint32_t); goto done; //發生error,則直接進入done } thread->looper |= BINDER_LOOPER_STATE_WAITING; if (wait_for_proc_work) proc->ready_threads++; //可用線程個數+1 binder_unlock(__func__); if (wait_for_proc_work) { if (non_block) { ... } else //當進程todo隊列沒有數據,則進入休眠等待狀態 ret = wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread)); } else { if (non_block) { ... } else //當線程todo隊列沒有數據,則進入休眠等待狀態 ret = wait_event_freezable(thread->wait, binder_has_thread_work(thread)); } binder_lock(__func__); if (wait_for_proc_work) proc->ready_threads--; //可用線程個數-1 thread->looper &= ~BINDER_LOOPER_STATE_WAITING; if (ret) return ret; //對於非阻塞的調用,直接返回 while (1) { uint32_t cmd; struct binder_transaction_data tr; struct binder_work *w; struct binder_transaction *t = NULL; //先考慮從線程todo隊列獲取事務數據 if (!list_empty(&thread->todo)) { w = list_first_entry(&thread->todo, struct binder_work, entry); //線程todo隊列沒有數據, 則從進程todo對獲取事務數據 } else if (!list_empty(&proc->todo) && wait_for_proc_work) { w = list_first_entry(&proc->todo, struct binder_work, entry); } else { ... //沒有數據,則返回retry } switch (w->type) { case BINDER_WORK_TRANSACTION: ... break; case BINDER_WORK_TRANSACTION_COMPLETE:... break; case BINDER_WORK_NODE: ... break; case BINDER_WORK_DEAD_BINDER: case BINDER_WORK_DEAD_BINDER_AND_CLEAR: case BINDER_WORK_CLEAR_DEATH_NOTIFICATION: struct binder_ref_death *death; uint32_t cmd; death = container_of(w, struct binder_ref_death, work); if (w->type == BINDER_WORK_CLEAR_DEATH_NOTIFICATION) cmd = BR_CLEAR_DEATH_NOTIFICATION_DONE; else cmd = BR_DEAD_BINDER; put_user(cmd, (uint32_t __user *)ptr; ptr += sizeof(uint32_t); put_user(death->cookie, (void * __user *)ptr); ptr += sizeof(void *); ... if (cmd == BR_DEAD_BINDER) goto done; //Binder驅動向client端發送死亡通知,則進入done break; } if (!t) continue; //只有BINDER_WORK_TRANSACTION命令才能繼續往下執行 ... break; } done: *consumed = ptr - buffer; //創建線程的條件 if (proc->requested_threads + proc->ready_threads == 0 && proc->requested_threads_started < proc->max_threads && (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED))) { proc->requested_threads++; // 生成BR_SPAWN_LOOPER命令,用於創建新的線程 put_user(BR_SPAWN_LOOPER, (uint32_t __user *)buffer); } return 0; }
當發生以下3種情況之一,便會進入done
:
- 當前線程的return_error發生error的情況;
- 當Binder驅動向client端發送死亡通知的情況;
- 當類型為BINDER_WORK_TRANSACTION(即收到命令是BC_TRANSACTION或BC_REPLY)的情況;
任何一個Binder線程當同時滿足以下條件,則會生成用於創建新線程的BR_SPAWN_LOOPER命令:
- 當前進程中沒有請求創建binder線程,即requested_threads = 0;
- 當前進程沒有空閑可用的binder線程,即ready_threads = 0;(線程進入休眠狀態的個數就是空閑線程數)
- 當前進程已啟動線程個數小於最大上限(默認15);
- 當前線程已接收到BC_ENTER_LOOPER或者BC_REGISTER_LOOPER命令,即當前處於BINDER_LOOPER_STATE_REGISTERED或者BINDER_LOOPER_STATE_ENTERED狀態。【小節2.6】已設置狀態為BINDER_LOOPER_STATE_ENTERED,顯然這條件是滿足的。
從system_server的binder線程一直的執行流: IPC.joinThreadPool –> IPC.getAndExecuteCommand() -> IPC.talkWithDriver() ,但talkWithDriver收到事務之后, 便進入IPC.executeCommand(), 接下來,從executeCommand說起.
2.8 IPC.executeCommand
status_t IPCThreadState::executeCommand(int32_t cmd) { status_t result = NO_ERROR; switch ((uint32_t)cmd) { ... case BR_SPAWN_LOOPER: //創建新的binder線程 【見小節2.3】 mProcess->spawnPooledThread(false); break; ... } return result; }
Binder主線程的創建是在其所在進程創建的過程一起創建的,后面再創建的普通binder線程是由spawnPooledThread(false)方法所創建的。
2.9 思考
默認地,每個進程的binder線程池的線程個數上限為15,該上限不統計通過BC_ENTER_LOOPER命令創建的binder主線程, 只計算BC_REGISTER_LOOPER命令創建的線程。 對此,或者很多人不理解,例個栗子:某個進程的主線程執行如下方法,那么該進程可創建的binder線程個數上限是多少呢?
ProcessState::self()->setThreadPoolMaxThreadCount(6); // 6個線程 ProcessState::self()->startThreadPool(); // 1個線程 IPCThread::self()->joinThreadPool(); // 1個線程
首先線程池的binder線程個數上限為6個,通過startThreadPool()創建的主線程不算在最大線程上限,最后一句是將當前線程成為binder線程,所以說可創建的binder線程個數上限為8,如果還不理解,建議再多看看這幾個方案的源碼,多思考整個binder架構。
三. 總結
Binder設計架構中,只有第一個Binder主線程(也就是Binder_1線程)是由應用程序主動創建,Binder線程池的普通線程都是由Binder驅動根據IPC通信需求創建,Binder線程的創建流程圖:
每次由Zygote fork出新進程的過程中,伴隨着創建binder線程池,調用spawnPooledThread來創建binder主線程。當線程執行binder_thread_read的過程中,發現當前沒有空閑線程,沒有請求創建線程,且沒有達到上限,則創建新的binder線程。
Binder的transaction有3種類型:
- call: 發起進程的線程不一定是在Binder線程, 大多數情況下,接收者只指向進程,並不確定會有哪個線程來處理,所以不指定線程;
- reply: 發起者一定是binder線程,並且接收者線程便是上次call時的發起線程(該線程不一定是binder線程,可以是任意線程)。
- async: 與call類型差不多,唯一不同的是async是oneway方式不需要回復,發起進程的線程不一定是在Binder線程, 接收者只指向進程,並不確定會有哪個線程來處理,所以不指定線程。
Binder系統中可分為3類binder線程:
- Binder主線程:進程創建過程會調用startThreadPool()過程中再進入spawnPooledThread(true),來創建Binder主線程。編號從1開始,也就是意味着binder主線程名為
binder_1
,並且主線程是不會退出的。 - Binder普通線程:是由Binder Driver來根據是否有空閑的binder線程來決定是否創建binder線程,回調spawnPooledThread(false) ,isMain=false,該線程名格式為
binder_x
。 - Binder其他線程:其他線程是指並沒有調用spawnPooledThread方法,而是直接調用IPC.joinThreadPool(),將當前線程直接加入binder線程隊列。例如: mediaserver和servicemanager的主線程都是binder線程,但system_server的主線程並非binder線程。