[Android Interview Questions] Android Framework Core Interview Questions - What is the working process of the Binder thread pool?

What is the working process of Binder thread pool? (Oriental Headlines)

What do you want to examine with this question?

This question wants to test students' understanding of Binder thread pool.

How should candidates answer

In general, there are the following situations: 1) The binder thread pool is not a thread pool structure in the traditional sense. It has only one PoolThread class inherited from Thread in the client process. The startup and management of threads are controlled by binderDriver. 2) Binder threads are divided into main threads and non-main threads. The main thread only appears when it is started. There is only one binder thread pool. In other cases, applications are made from non-main threads.
3) When the binder thread pool is started, it actually only starts the main binder thread in the client. 4) The binder thread (non-main thread) is started in two situations: the client process sends an IPC request to binderDriver, and the client process replies to binderDriver with the IPC request result. 5) The default size of the binder thread pool is 16, 1 main thread and 15 non-main threads. For more details, we need to start with the creation of the Binder thread. You can refer to the following content:

1.Binder thread creation

The creation of the Binder thread is generated by 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 the socket message of the creation process to the Zygote process. After Zygote receives the message, it 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 will eventually be called to onZygoteInit in app_main.cpp. Let’s start with this method.

2.onZygoteInit
// app_main.cpp
virtual void onZygoteInit() {
    
    
    //获取ProcessState对象
    sp<ProcessState> proc = ProcessState::self();
    //启动新binder线程
    proc->startThreadPool();
}

ProcessState::self() is a singleton mode. Its main job is to call open() to open the /dev/binder driver device, then use mmap() to map the kernel's address space, and assign the Binder driver's fd to the variable mDriverFD in the ProcessState object. for interactive operations. startThreadPool() creates a new binder thread and continuously performs talkWithDriver().

3.PS.startThreadPool
// ProcessState.cpp
void ProcessState::startThreadPool()
{
    
    
    AutoMutex _l(mLock);    //多线程同步
    if (!mThreadPoolStarted) {
    
    
        mThreadPoolStarted = true;
        spawnPooledThread(true);  
    }
}

After starting the Binder thread pool, set mThreadPoolStarted=true. The variable mThreadPoolStarted is used to ensure that each application process is only allowed to start one binder thread pool, and the binder main thread is created this time (isMain=true). The remaining binder thread pools Threads are created under the control of the Binder driver.

4.PS.spawnPooledThread
// ProcessState.cpp
void ProcessState::spawnPooledThread(bool isMain)
{
    
    
    if (mThreadPoolStarted) {
    
    
        //获取Binder线程名
        String8 name = makeBinderThreadName();
        //此处isMain=true
        sp<Thread> t = new PoolThread(isMain);
        t->run(name.string());
    }
}

4-1.makeBinderThreadName

// ProcessState.cpp
String8 ProcessState::makeBinderThreadName() {
    
    
    int32_t s = android_atomic_add(1, &mThreadPoolSeq);
    String8 name;
    name.appendFormat("Binder_%X", s);
    return name;
}

Get the Binder thread name in the format of Binder_x, where x is an integer. The binder encoding in each process starts from 1 and increases in sequence; only threads created through the spawnPooledThread method conform to this format. Thread names that directly add the current thread to the thread pool through joinThreadPool do not conform to this naming rule. In addition, the Binder command in Android N has been changed to the Binder:_x format, which is very helpful for analyzing problems. The pid field of the binder name can be used to quickly locate the process p to which the binder thread belongs.

4-2.PoolThread.run

// ProcessState.cpp
class PoolThread : public Thread
{
    
    
public:
    PoolThread(bool isMain)
        : mIsMain(isMain)
    {
    
    
    }

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

From the function name, it seems that it creates 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.

5. 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(); //清除队列的引用
        result = getAndExecuteCommand(); //处理下一条指令

        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为空
}
  • For isMain=true, the command is BC_ENTER_LOOPER, which represents the Binder main thread and will not exit;
  • For isMain=false, the command is BC_REGISTER_LOOPER, indicating that the thread is created by the binder driver.
6. 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();
        }
    }
}
7. getAndExecuteCommand
// IPCThreadState.cpp
status_t IPCThreadState::getAndExecuteCommand()
{
    
    
    status_t result;
    int32_t cmd;

    result = talkWithDriver(); //与binder进行交互
    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响应码

        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;
}
8. 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;
}

The isMain=true called here means that what is written to mOut is BC_ENTER_LOOPER. After talkWithDriver(), where does the program go next? In the article, a thorough understanding of the Android Binder communication architecture explains the Binder communication process in detail, then let’s start with binder_thread_write() and talk about the BC_ENTER_LOOPER processing process.

8-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;
}

After processing the BC_ENTER_LOOPER command, thread->looper |= BINDER_LOOPER_STATE_ENTERED is generally set successfully. So when was the binder thread created? Then when the thread has a transaction to process, enter the binder_thread_read() process.

8-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;
}

When one of the following three situations occurs, it will enter done:

  • The current thread's return_error error occurs;
  • 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 for creating a new thread:

  1. There is no request to create a binder thread in the current process, that is, requested_threads = 0;
  2. The current process has no idle binder threads available, that is, ready_threads = 0; (the number of threads entering sleep state is the number of idle threads)
  3. The number of started threads in the current process is less than the maximum limit (default 15);
  4. 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. The state has been set to BINDER_LOOPER_STATE_ENTERED before, obviously this condition is met.

The execution flow from the binder thread of system_server is: IPC.joinThreadPool –> IPC.getAndExecuteCommand() –> IPC.talkWithDriver(), but after talkWithDriver receives the transaction, it enters IPC.executeCommand(). Next, let’s start with executeCommand .

** 9.IPC.executeCommand**

status_t IPCThreadState::executeCommand(int32_t cmd)
{
    
    
    status_t result = NO_ERROR;
    switch ((uint32_t)cmd) {
    
    
      ...
      case BR_SPAWN_LOOPER:
          //创建新的binder线程
          mProcess->spawnPooledThread(false);
          break;
      ...
    }
    return result;
}

The main Binder thread is created during the creation of the process in which it is located. The ordinary binder thread created later is created by the spawnPooledThread(false) method.

9. Summary

The 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 binder main thread is named binder_1, and the main thread will not exit.
  • Binder ordinary thread: The Binder Driver determines whether to create a binder thread based on 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 both binder threads, but the main thread of system_server is not a binder thread.

There are three types of Binder transactions:

  1. call: The thread that initiates the process is not necessarily the Binder thread. In most cases, the receiver only points to the process and is not sure which thread will handle it, so the thread is not specified;
  2. reply: The initiator must be a binder thread, and the receiver thread is the initiating thread during the last call (the thread does not have to be a binder thread, it can be any thread).
  3. async: Similar to the call type. The only difference is that async is a oneway method that does not require a reply. The thread that initiates the process is not necessarily the Binder thread. The receiver only points to the process and is not sure which thread will handle it, so the thread is not specified. .

at last

I have compiled a collection of Android interview questions. In addition to the above interview questions, it also includes [ Java basics, collections, multi-threading, virtual machines, reflection, generics, concurrent programming, Android's four major components, asynchronous tasks and message mechanisms, UI drawing , performance tuning, SDN, third-party framework, design pattern, Kotlin, computer network, system startup process, Dart, Flutter, algorithm and data structure, NDK, H.264, H.265. Audio codec, FFmpeg, OpenMax, OpenCV, OpenGL ES ]
Insert image description here

Friends in need can scan the QR code below to receive all interview questions + answer analysis for free! ! !

Guess you like

Origin blog.csdn.net/datian1234/article/details/133324615