进程的Binde线程池工作过程

开发 开发工具
本文基于Android 6.0源码剖析,分析Binder线程池以及binder线程启动过程。

进程的Binde线程池工作过程

一. 概述

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,那么接下来从这个方法说起。

1. onZygoteInit

 

  1. [-> app_main.cpp] 

onZygoteInit

ProcessState::self()是单例模式,主要工作是调用open()打开/dev/binder驱动设备,再利用mmap()映射内核的地址空间,将Binder驱动的fd赋值ProcessState对象中的变量mDriverFD,用于交互操作。startThreadPool()是创建一个新的binder线程,不断进行talkWithDriver()。

2. PS.startThreadPool

  1. [-> ProcessState.cpp] 

PS.startThreadPool

启动Binder线程池后, 则设置mThreadPoolStarted=true. 通过变量mThreadPoolStarted来保证每个应用进程只允许启动一个binder线程池, 且本次创建的是binder主线程(isMain=true). 其余binder线程池中的线程都是由Binder驱动来控制创建的。

3. PS.spawnPooledThread

  1. [-> ProcessState.cpp] 

(1) makeBinderThreadName

  1. [-> ProcessState.cpp] 

makeBinderThreadName

获取Binder线程名,格式为Binder_x, 其中x为整数。每个进程中的binder编码是从1开始,依次递增; 只有通过spawnPooledThread方法来创建的线程才符合这个格式,对于直接将当前线程通过joinThreadPool加入线程池的线程名则不符合这个命名规则。 另外,目前Android N中Binder命令已改为Binder:_x格式, 则对于分析问题很有帮忙.

(2) PoolThread.run

  1. [-> ProcessState.cpp] 

PoolThread.run

从函数名看起来是创建线程池,其实就只是创建一个线程,该PoolThread继承Thread类。t->run()方法最终调用 PoolThread的threadLoop()方法。

4. IPC.joinThreadPool

  1. [-> IPCThreadState.cpp] 

IPC.joinThreadPool

  • 对于isMain=true的情况下, command为BC_ENTER_LOOPER,代表的是Binder主线程,不会退出的线程;
  • 对于isMain=false的情况下,command为BC_REGISTER_LOOPER,表示是由binder驱动创建的线程。

5. processPendingDerefs

  1. [-> IPCThreadState.cpp] 

processPendingDerefs

6. getAndExecuteCommand

  1. [-> IPCThreadState.cpp] 

2.6 getAndExecuteCommand

7. talkWithDriver

7. talkWithDriver

在这里调用的isMain=true,也就是向mOut例如写入的便是BC_ENTER_LOOPER. 经过talkWithDriver(), 接下来程序往哪进行呢?从binder_thread_write()往下说BC_ENTER_LOOPER的处理过程。

(1) binder_thread_write

  1. [-> binder.c] 

binder_thread_write

binder_thread_write

binder_thread_write

binder_thread_write

当发生以下3种情况之一,便会进入done:

  • 当前线程的return_error发生错误的情况;
  • 当Binder驱动向客户端发送死亡通知的情况;
  • 当类型为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说起。

8. IPC.executeCommand

IPC.executeCommand

Binder主线程的创建是在其所在的进程创建的过程一起创建的,后面再创建的普通binder线程是由spawnPooledThread(false)方法所创建的。

9. 思考

默认地,每个进程的binder线程池的线程个数上限为15,该上限不统计通过BC_ENTER_LOOPER命令创建的binder主线程,只计算BC_REGISTER_LOOPER命令创建的线程对此,或者很多人不理解,例栗子:某个进程的主线程执行如下方法,那么该进程可创建的binder线程个数上限是多少呢?

进程可创建的binder线程个数上限是多少

首先线程池的binder线程个数上限为6个,通过startThreadPool()创建的主线程不在***线程上限,***一句是将当前线程成为binder线程,所以说可创建的binder线程个数上限为8 ,如果还不理解,建议再多看看这几个方案的源码,多思考整个binder架构。

三、总结

Binder设计架构中,只有***个Binder主线程(也就是Binder_1线程)是由应用程序主动创建,Binder线程池的普通线程都是由Binder驱动根据IPC通信需求创建,Binder线程的创建流程图:

Binder线程的创建流程图

每次由Zygote fork出新进程的过程中,伴随着创建binder线程池,调用spawnPooledThread来创建binder主线程。当线程执行binder_thread_read的过程中,发现当前没有空闲线程,没有请求创建线程,且没有达到上限,则创建新的binder线程。

1. Binder的交易有3种类型:

  • 调用:发起进程的线程不一定是在Binder线程,大多数情况下,接收者只指向进程,并不确定会有哪个线程来处理,所以不指定线程;
  • 答复:发起者一定是binder线程,并且接收者线程便是上次call时的发起线程(该线程不一定是binder线程,可以是任意线程)。
  • async:与调用类型差不多,唯一不同的是async是oneway方式不需要回复,发起进程的线程不一定是在Binder线程,接收者只指向进程,并且不确定会有哪个线程来处理,所以不指定线程。

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

【本文是51CTO专栏“小米开放平台”原创文章,“小米开放平台”微信公众号xiaomideveloper】

戳这里,看该作者更多好文

责任编辑:赵宁宁 来源: 51CTO.com
相关推荐

2010-09-01 15:17:04

DHCP工作过程

2009-02-24 09:48:00

IP电话语音数据

2010-09-01 15:37:04

DHCP工作过程

2014-06-03 17:29:48

PPP路由器

2010-08-30 11:28:53

DHCP工作过程

2010-03-19 13:50:48

三层交换机

2010-01-06 11:30:22

.NET Framew

2017-01-10 13:39:57

Python线程池进程池

2013-03-04 09:56:20

网络管理员路由器路由工作原理

2021-07-16 11:35:20

Java线程池代码

2011-02-24 14:23:18

2011-12-26 14:07:07

游戏

2024-07-15 08:20:24

2024-03-11 18:18:58

项目Spring线程池

2024-11-27 08:15:50

2020-10-22 09:35:11

线程池核心线程阻塞队列

2018-07-05 14:18:12

数据分析神经网络模型

2021-03-08 08:55:22

开发

2010-06-22 15:51:38

Autoconf rp

2009-08-25 15:48:03

C#数组操作
点赞
收藏

51CTO技术栈公众号