博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
进程的Binder线程池工作过程
阅读量:5745 次
发布时间:2019-06-18

本文共 12754 字,大约阅读时间需要 42 分钟。

基于Android 6.0源码剖析,分析Binder线程池以及binder线程启动过程。

frameworks/base/cmds/app_process/app_main.cppframeworks/native/libs/binder/ProcessState.cppframework/native/libs/binder/IPCThreadState.cppkernel/drivers/staging/android/binder.c

一. 概述

Android系统启动完成后,ActivityManager, PackageManager等各大服务都运行在system_server进程,app应用需要使用系统服务都是通过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
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】    }}

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
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); //【见小节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默认值为truestatus_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(), 接下来程序往哪进行呢?从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;}

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线程。

作者:MIUI资深工程师-袁辉辉个人博客:gityuan.com

更多内容可以关注小米开放平台订阅号:

官方QQ交流群:398616987
想要了解更多?
那就关注我们吧!
图片描述

转载地址:http://uxazx.baihongyu.com/

你可能感兴趣的文章
ISO8583接口的详细资料
查看>>
tmux不自动加载配置文件.tmux.conf
查看>>
[MOSEK] Stupid things when using mosek
查看>>
程序实例---栈的顺序实现和链式实现
查看>>
服务的使用
查看>>
Oracle 用户与模式
查看>>
MairDB 初始数据库与表 (二)
查看>>
拥在怀里
查看>>
chm文件打开,有目录无内容
查看>>
whereis、find、which、locate的区别
查看>>
一点不懂到小白的linux系统运维经历分享
查看>>
桌面支持--打不开网页上的pdf附件解决办法(ie-tools-compatibility)
查看>>
nagios监控windows 改了NSclient++默认端口 注意事项
查看>>
干货 | JAVA代码引起的NATIVE野指针问题(上)
查看>>
POI getDataFormat() 格式对照
查看>>
Python 中的进程、线程、协程、同步、异步、回调
查看>>
好的产品原型具有哪些特点?
查看>>
实现java导出文件弹出下载框让用户选择路径
查看>>
刨根问底--技术--jsoup登陆网站
查看>>
OSChina 五一劳动节乱弹 ——女孩子晚上不要出门,发生了这样的事情
查看>>