当前位置: 首页 > 科技观察

进程Binde线程池的工作流程

时间:2023-03-22 13:25:34 科技观察

一、概述Android系统启动后,ActivityManager、PackageManager等主要服务运行在system_server进程中,app应用需要使用系统服务来通过Binder完成进程间的通信。那么binder线程是如何管理的,又是如何创建的呢?其实无论是system_server进程还是app进程,进程fork完成后,都会在新进程执行onZygoteInit()的过程中执行。活页夹线程池。接下来以此为起点,从线程的角度来看Binder的世界。2.Binder线程的创建Binder线程的创建与所在进程的创建有关。Java层进程的创建是通过Process.start()方法,向Zygote进程发送socket消息创建进程。Zygote收到消息后会调用Zygote。forkAndSpecialize()fork一个新进程,在新进程中会调用RuntimeInit.nativeZygoteInit方法,这个方法被jni映射,最终会调用到app_main.cpp中的onZygoteInit,所以我们先从这个方法开始。1、onZygoteInit[->app_main.cpp]ProcessState::self()是单例模式,主要工作是调用open()打开/dev/binder驱动设备,然后使用mmap()映射地址内核的空间来驱动Binderfd被赋值给ProcessState对象中的变量mDriverFD进行交互操作。startThreadPool()是新建一个binder线程,继续talkWithDriver()。2.PS.startThreadPool[->ProcessState.cpp]启动Binder线程池后,设置mThreadPoolStarted=true。使用变量mThreadPoolStarted保证每个应用进程只允许启动一个binder线程池,此时创建binder主线程(isMain=true)。Binder线程池中的其他线程由Binder驱动程序创建。3、PS.spawnPooledThread[->ProcessState.cpp](1)makeBinderThreadName[->ProcessState.cpp]获取Binder线程名称,格式为Binder_x,其中x为整数。每个进程中的bindercode从1开始依次递增;只有spawnPooledThread方法创建的线程符合这种格式,通过joinThreadPool直接将当前线程加入线程池的线程名不符合这种命名规则。另外,AndroidN中的Binder命令改为Binder:_x格式,这对分析问题很有帮助。(2)PoolThread.run[->ProcessState.cpp]从函数名看是创建线程池,其实只是创建一个线程,PoolThread继承了Thread类。t->run()方法最终调用了PoolThread的threadLoop()方法。4、IPC.joinThreadPool[->IPCThreadState.cpp]在isMain=true的情况下,命令为BC_ENTER_LOOPER,代表Binder主线程,不会退出线程;在isMain=false的情况下,command是BC_REGISTER_LOOPER,表示是binderdriver创建的线程。5.processPendingDerefs[->IPCThreadState.cpp]6.getAndExecuteCommand[->IPCThreadState.cpp]7.这里talkWithDriver调用的isMain=true,即BC_ENTER_LOOPER写入mOut,例如。talkWithDriver()之后,next程序跑到哪里去了?从binder_thread_write()到BC_ENTER_LOOPER的处理。(1)binder_thread_write[->binder.c]当出现以下三种情况之一时,会进入done:当当前线程的return_error有错误时;当Binder驱动程序向客户端发送死亡通知时;当type为BINDER_WORK_TRANSACTION(即接收到的命令为BC_TRANSACTION或BC_REPLY)时;当任一Binder线程同时满足以下条件时,会产生创建新线程的BR_SPAWN_LOOPER命令:当前进程中没有创建Binder线程的请求,即requested_threads=0;current进程没有空闲可用的binder线程,即ready_threads=0;(进入休眠状态的线程数为空闲线程数)当前进程启动的线程数小于上限(默认15);当前线程已收到BC_ENTER_LOOPER或BC_REGISTER_LOOPER命令,当前处于BINDER_LOOPER_STATE_REGISTERED或BINDER_LOOPER_STATE_ENTERED状态。[Section2.6]状态已经设置为BINDER_LOOPER_STATE_ENTERED,显然这个条件是满足的。从system_server的binder线程执行流程:IPC.joinThreadPool->IPC.getAndExecuteCommand()->IPC.talkWithDriver(),但是talkWithDriver收到事务后,进入IPC.executeCommand(),然后从executeCommand开始。8.IPC.executeCommandBinder主线程的创建是在创建所在进程的过程中一起创建的,后面创建的普通binder线程是通过spawnPooledThread(false)方法创建的。9、考虑默认情况下每个进程的binder线程池的线程数上限为15个,这个上限不计算BC_ENTER_LOOPER命令创建的binder主线程,只计算binder主线程创建的线程BC_REGISTER_LOOPER命令。很多人可能不理解这一点。举个栗子:一个进程的主线程执行了如下方法,那么这个进程最多可以创建多少个binder线程呢?首先,线程池的binder线程数上限为6,startThreadPool()创建的主线程不是***线程上限,***语句是将当前线程变成一个binder线程,所以最大可以创建的binder线程数是8个。如果还是不明白,建议多看看这些解决方案的源码,多想想整个binder架构。3.总结在Binder设计架构中,只有第一个Binder主线程(即Binder_1线程)是应用程序主动创建的。Binder线程池中的普通线程是由Binder驱动根据IPC通信需求创建的。Binder线程的创建流程图:Zygote每次fork一个新进程,伴随着binder线程池的创建,都会调用spawnPooledThread创建binder主线程。当线程执行binder_thread_read时,发现没有空闲线程,没有创建线程的请求,也没有达到上限,则创建一个新的binder线程。1、Binder事务有3种类型:Call:发起进程的线程不一定在Binder线程中。大多数情况下,receiver只是指向进程,并不确定哪个线程会处理它,所以没有指定thread;回复:发起者必须是binder线程,接收者线程是上次调用的发起者线程(这个线程不一定是binder线程,可以是任意线程)。async:与调用类型类似,唯一不同的是async是一个不需要回复的oneway方法,发起进程的线程不一定在Binder线程中,receiver只指向进程,它是不确定哪个线程会处理它,所以没有指定线程。2、Binder系统分为三种Binder线程:Binder主线程:进程创建过程会调用startThreadPool()然后进入spawnPooledThread(true)创建Binder主线程。数字从1开始,表示binder主线程命名为binder_1,主线程不会退出。Binder普通线程:BinderDriver根据是否有空闲的Binder线程决定是否创建Binder线程,回调spawnPooledThread(false),isMain=false,线程名格式为binder_x。Binder其他线程其他线程不调用spawnPooledThread方法,而是直接调用IPC.joinThreadPool()直接将当前线程加入到binder线程队列中。例如:mediaserver和servicemanager的主线程是binder线程,而system_server的主线程不是binder线程。【本文为“小米开放平台”专栏原创,“小米开放平台”微信公众号xiaomideveloper】点此阅读更多本作者好文