Based on the analysis of Android 6.0 source code, analyze the Binder thread pool and the start process of the binder thread.
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
I. Overview
After the Android system is started, all major services such as ActivityManager and PackageManager are running in the system_server process. App applications need to use system services to complete the communication between processes through the binder. How is the binder thread managed? Created? In fact, whether it is the system_server process or the app process, after the process fork is completed, it will start the binder thread pool in the process of executing onZygoteInit() in the new process. Next, take this as a starting point to take a look at the world of binder from the perspective of threads.
2. Binder thread creation
The creation of the Binder thread and the creation of the process in which it is located. The creation of the Java layer process is through the Process.start() method, which sends a socket message to the Zygote process to create the process. After receiving the message, Zygote will call Zygote.forkAndSpecialize() to fork When a new process is created, the RuntimeInit.nativeZygoteInit method will be called in the new process. This method is mapped by jni and finally called onZygoteInit in app_main.cpp, so let's start with this method.
2.1 onZygoteInit
[-> app_main.cpp]
virtual void onZygoteInit() { //Get ProcessState object sp<ProcessState> proc = ProcessState::self(); //Start a new binder thread [see section 2.2] proc->startThreadPool(); }
ProcessState::self() is a singleton mode. The main work is to call open() to open the/dev/binder drive device, and then use mmap() to map the kernel's address space, and assign the Binder-driven fd to the variable mDriverFD in the ProcessState object. Used for interactive operation. startThreadPool() is to create a new binder thread and continue to talkWithDriver().
2.2 PS.startThreadPool
[-> ProcessState.cpp]
void ProcessState::startThreadPool() { AutoMutex _l(mLock);//Multi-thread synchronization if (!mThreadPoolStarted) { mThreadPoolStarted = true; spawnPooledThread(true); [see section 2.3] } }
2.3 PS.spawnPooledThread
[-> ProcessState.cpp]
void ProcessState::spawnPooledThread(bool isMain) { if (mThreadPoolStarted) { //Get the Binder thread name [see section 2.3.1] String8 name = makeBinderThreadName(); //Here isMain=true [see section 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; }
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);//[see section 2.4] return false; } const bool mIsMain; };
From the function name, it seems to create a thread pool, but in fact it just creates a thread. The PoolThread inherits the Thread class. The t->run() method finally calls the threadLoop() method of PoolThread.
2.4 IPC.joinThreadPool
[-> IPCThreadState.cpp]
void IPCThreadState::joinThreadPool(bool isMain) { //Create Binder thread mOut.writeInt32(isMain? BC_ENTER_LOOPER: BC_REGISTER_LOOPER); set_sched_policy(mMyThreadId, SP_FOREGROUND);//Set the foreground scheduling policy status_t result; do { processPendingDerefs();//Clear the reference of the queue [see section 2.5] result = getAndExecuteCommand();//Process the next command [see section 2.6] if (result <NO_ERROR && result != TIMED_OUT && result != -ECONNREFUSED && result != -EBADF) { abort(); } if(result == TIMED_OUT && !isMain) { break; the thread exits when timeout appears in the non-main thread } } while (result != -ECONNREFUSED && result != -EBADF); mOut.writeInt32(BC_EXIT_LOOPER);//thread exits the loop talkWithDriver(false);//false means that the read_buffer of bwr data is empty }
In the case of isMain=true, the command is BC_ENTER_LOOPER, which represents the Binder main thread, which will not exit;
In the case of isMain=false, the command is BC_REGISTER_LOOPER, which means that the thread is created by the binder driver.
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());//weak reference minus one } mPendingWeakDerefs.clear(); } numPending = mPendingStrongDerefs.size(); if (numPending> 0) { for (size_t i = 0; i <numPending; i++) { BBinder* obj = mPendingStrongDerefs[i]; obj->decStrong(mProcess.get());//Strong reference minus one } mPendingStrongDerefs.clear(); } } }
2.6 getAndExecuteCommand
[-> IPCThreadState.cpp]
status_t IPCThreadState::getAndExecuteCommand() { status_t result; int32_t cmd; result = talkWithDriver();//Interact with binder [see section 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);//Execute Binder response code [see section 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 has data, mIn has no data yet. The default value of doReceive is true status_t IPCThreadState::talkWithDriver(bool doReceive) { binder_write_read bwr; ... //When there is no input and output data at the same time, return directly if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR; ... do { //ioctl executes binder read and write operations, after syscall, enters the Binder driver. Call Binder_ioctl if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0) err = NO_ERROR; ... } while (err == -EINTR); ... return err; }
The isMain=true called here, that is, for example, BC_ENTER_LOOPER is written to mOut. After talkWithDriver(), where does the program proceed? From binder_thread_write() down to the processing of 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 command to copy user space, at this time 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) { //The reason for the error: the thread cannot execute the branch after calling BC_ENTER_LOOPER thread->looper |= BINDER_LOOPER_STATE_INVALID; } else if (proc->requested_threads == 0) { //The reason for the error: create a thread without a request 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) { //The reason for the error: the thread cannot execute the branch immediately after calling BC_REGISTER_LOOPER thread->looper |= BINDER_LOOPER_STATE_INVALID; } //Create Binder main thread thread->looper |= BINDER_LOOPER_STATE_ENTERED; break; case BC_EXIT_LOOPER: thread->looper |= BINDER_LOOPER_STATE_EXITED; break; } ... } *consumed = ptr-buffer; } return 0; }
2.7.2 binder_thread_read
binder_thread_read() { ... retry: //The current thread todo queue is empty and the transaction stack is empty, it means that the thread is idle 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;//If an error occurs, go directly to done } thread->looper |= BINDER_LOOPER_STATE_WAITING; if (wait_for_proc_work) proc->ready_threads++;//Number of available threads+1 binder_unlock(__func__); if (wait_for_proc_work) { if (non_block) { ... } else //When the process todo queue has no data, it enters the sleep waiting state ret = wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread)); } else { if (non_block) { ... } else //When the thread todo queue has no data, it enters the sleep waiting state ret = wait_event_freezable(thread->wait, binder_has_thread_work(thread)); } binder_lock(__func__); if (wait_for_proc_work) proc->ready_threads--;//Number of available threads -1 thread->looper &= ~BINDER_LOOPER_STATE_WAITING; if (ret) return ret;//For non-blocking calls, return directly while (1) { uint32_t cmd; struct binder_transaction_data tr; struct binder_work *w; struct binder_transaction *t = NULL; //First consider getting transaction data from the thread todo queue if (!list_empty(&thread->todo)) { w = list_first_entry(&thread->todo, struct binder_work, entry); //The thread todo queue has no data, then the transaction data is obtained from the process todo pair } else if (!list_empty(&proc->todo) && wait_for_proc_work) { w = list_first_entry(&proc->todo, struct binder_work, entry); } else { ...//If there is no data, return 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 driver sends a death notification to the client, then enter done break; } if (!t) continue;//Only the BINDER_WORK_TRANSACTION command can continue to execute ... break; } done: *consumed = ptr-buffer; //The conditions for creating a thread 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++; //Generate BR_SPAWN_LOOPER command to create a new thread put_user(BR_SPAWN_LOOPER, (uint32_t __user *)buffer); } return 0; }
When one of the following three situations occurs, it will enter done:
The return_error of the current thread has an error;
when the Binder driver sends a death notification to the client;
when the type is BINDER_WORK_TRANSACTION (that is, the received command is BC_TRANSACTION or BC_REPLY); when
any Binder thread meets the following conditions at the same time, it will Generate the BR_SPAWN_LOOPER command to create a new thread:
There is no request to create a binder thread in the current process, that is, requested_threads = 0; the
current process has no free binder threads available, that is, ready_threads = 0; (the number of threads entering the sleep state is the number of idle threads) The number of threads that the
current process has started is less than the maximum Upper limit (default 15); the
current thread has received the BC_ENTER_LOOPER or BC_REGISTER_LOOPER command, that is, it is currently in the BINDER_LOOPER_STATE_REGISTERED or BINDER_LOOPER_STATE_ENTERED state. [Section 2.6] The state has been set to BINDER_LOOPER_STATE_ENTERED, obviously this condition is met.
The execution flow from the binder thread of the system_server has been: IPC.joinThreadPool --> IPC.getAndExecuteCommand() -> IPC.talkWithDriver(), but after talkWithDriver receives the transaction, it enters IPC.executeCommand(), and then, let's start with 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: //Create a new binder thread [see section 2.3] mProcess->spawnPooledThread(false); break; ... } return result; }
The creation of the Binder main thread is created in the process of its process creation, and the ordinary binder thread created later is created by the spawnPooledThread(false) method.
2.9 Thinking
By default, the upper limit of the number of threads in the binder thread pool of each process is 15. The upper limit does not count the binder main threads created by the BC_ENTER_LOOPER command, but only counts the threads created by the BC_REGISTER_LOOPER command. In this regard, or many people do not understand, for example: the main thread of a process executes the following methods, then what is the upper limit of the number of binder threads that can be created by the process?
ProcessState::self()->setThreadPoolMaxThreadCount(6);//6 threads ProcessState::self()->startThreadPool();//1 thread IPCThread::self()->joinThreadPool();//1 thread
First of all, the upper limit of the number of binder threads in the thread pool is 6. The main thread created by startThreadPool() is not counted as the upper limit of the maximum thread. The last sentence is to turn the current thread into a binder thread, so the upper limit of the number of binder threads that can be created is 8. , If you still don’t understand, I suggest you look at the source code of these programs and think about the entire binder architecture.
3. Summary
In the Binder design architecture, only the first Binder main thread (that is, the Binder_1 thread) is actively created by the application. The ordinary threads of the Binder thread pool are created by the Binder driver according to the IPC communication requirements. The creation flow chart of the Binder thread:
Each time a new process is forked by Zygote, along with the creation of the binder thread pool, spawnPooledThread is called to create the binder main thread. When the thread executes the binder_thread_read process, it is found that there is no idle thread, no thread creation is requested, and the upper limit is not reached, a new binder thread is created.
There are 3 types of Binder transactions:
call: The thread that initiated the process is not necessarily the Binder thread. In most cases, the receiver only points to the process, and it is not sure which thread will be processed, so the thread is not specified;
reply: The initiator must be the binder thread and receive The thread is the initiating thread of the last call (this thread is not necessarily a binder thread, it can be any thread).
async: Similar to the call type, the only difference is that async is a oneway method and does not require a reply. The thread that initiates the process is not necessarily the Binder thread. The receiver only points to the process. It is not sure which thread will be processed, so the thread is not specified. .
Binder system can be divided into three types of binder threads:
Binder main thread: The process creation process will call startThreadPool() and then enter spawnPooledThread(true) to create the Binder main thread. The number starts from 1, which means that the main binder thread is named binder_1, and the main thread will not exit.
Binder ordinary thread: The Binder Driver decides whether to create a binder thread according to whether there is an idle binder thread, the callback spawnPooledThread(false), isMain=false, and the thread name format is binder_x.
Binder other threads: other threads do not call the spawnPooledThread method, but directly call IPC.joinThreadPool() to directly add the current thread to the binder thread queue. For example: The main threads of mediaserver and servicemanager are binder threads, but the main thread of system_server is not binder threads.
Author: MIUI Senior Engineer - Yuan Huihui personal blog: gityuan.com
For more information, please follow the subscription number of Xiaomi Open Platform:
Official QQ Exchange Group: 398616987
Want to know more?
Follow us then!
View Image