Ahab's Studio.

细读《深入理解 Android 内核设计思想》(四)Binder 机制 [中]

字数统计: 3.8k阅读时长: 15 min
2020/04/23 Share

对冗余挑拣重点,对重点深入补充,输出结构清晰的精简版

  1. 智能指针
  2. binder 驱动中的结构体
  3. ProcessState
  4. IPCThreadState
  5. BpBinder
  6. BinderProxy
  7. ServiceManager getService 流程

智能指针

在 binder 相关源码可以看到 sp、wp 类型的引用:

1
2
3
sp<IBinder> result = new BpBinder(handle);

wp<IBinder> result = new BpBinder(handle);

sp 即 strong pointer 强指针引用;wp 是 weak pointer 弱指针引用。在 Java 中我们不用关心对象的销毁及内存释放,GC 机制会自动辨别回收无用对象,而智能指针就是 native 层一个小型的 GC 实现。

智能指针以引用计数的方式来标识无用对象,使用智能指针的对象需继承自 RefBase,RefBase 中维护了此对象被强引用数量和弱引用数量。

强引用 sp 重载了 “=” 运算符,在引用其他对象时将强引用计数 +1,在 sp 析构函数中将强引用计数 -1,当强引用计数减至 0 时销毁引用的对象。这样就实现了对象的自动释放,但若只靠强引用计数方式,会存在循环引用的问题,导致对象永远无法被释放,弱引用就是专门用来解决循环引用问题的。

若在 A 中强引用了 B,那 B 需要引用 A 时就使用弱引用,当判断对象是否无用时仅考虑强引用计数是否为 0,不关心弱引用计数的数量。当 B 要访问 A 时,若发现 A 已经被销毁,那就表示 A 已经不存在了,需要进行重新创建等其他操作。

RefBase 提供了 extendObjectLifetime() 方法,可以用来设置引用计数器的规则,不同规则对删除目标对象的时机判断也是不一样的,包括以下三种规则:

OBJECT_LIFETIME_STRONG:只有在这个对象内存空间中的强计数器值为 0 的时候才会销毁对象)

OBJECT_LIFETIME_WEAK:只有在这个对象内存空间中的强计数器和弱计数器的值都为 0 的时候才会销毁对象

OBJECT_LIFETIME_MASK:不管这两个计数器是不是都为 0,都不销毁对象,即与一般指针无异,还是要自己手动去释放对象

binder 驱动相关的结构体

结构体 说明
binder_proc 描述使用 binder 的进程,当调用 binder_open 函数时会创建
binder_thread 描述使用 binder 的线程,当调用 binder_ioctl 函数时会创建
binder_node 描述 binder 实体节点,对应于一个 serve,即用户态的 BpBinder 对象
binder_ref 描述对 binder 实体节点的引用,关联到一个 binder_node
binder_buffer 描述 binder 通信过程中存储数据的Buffer
binder_work 描述一个 binder 任务
binder_transaction 描述一次 binder 任务相关的数据信息
binder_ref_death 描述 binder_node 即 binder server 的死亡信息

其中主要结构体引用关系如下:

以上结构体仅存在于 binder 驱动内部,还有一类结构体是与用户态共用的:

结构体 说明
flat_binder_object 描述在 binder IPC 过程传递的对象
binder_write_read 描述存储读写操作的数据
binder_version 描述 binder 的版本号
transaction_flags 描述事务的 flag,例如是否是异步请求,是否支持 fd
binder_transaction_data 描述一次事务的相关数据

参考: http://www.codeceo.com/article/android-binder-part-one.html

ProcessState

ProcessState 专门管理每个应用进程的 Binder 操作,同一个进程中只有一个 ProcessState 实例存在,且只在 ProcessState 对象创建时才打开 Binder 设备以及内存映射。相关代码如下:

1
2
3
4
5
6
7
8
9
///frameworks/native/libs/binder/ProcessState.cpp
sp<ProcessState> ProcessState::self(){
Mutex::Autolock _l(gProcessMutex);
if (gProcess != NULL) { //如果创建过 ProcessState 就直接返回
return gProcess;
}
gProcess = new ProcessState;
return gProcess;
}

外部统一通过 ProcessState::self() 方法获取 ProcessState,以此保证 ProcessState 的进程单例,ProcessState 的构造函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#define BINDER_VM_SIZE ((1*1024*1024) - (4096 *2))
#define DEFAULT_MAX_BINDER_THREADS 15

ProcessState::ProcessState()
: mDriverFD(open_driver()) //打开 binder 设备
, mVMStart(MAP_FAILED) //初始化为 MAP_FAILED,映射成功后会变更
, mThreadCountLock(PTHREAD_MUTEX_INITIALIZER)
, mThreadCountDecrement(PTHREAD_COND_INITIALIZER)
, mExecutingThreadsCount(0)
, mMaxThreads(DEFAULT_MAX_BINDER_THREADS) //binder 线程最大数量
, mStarvationStartTimeMs(0)
, mManagesContexts(false)
, mBinderContextCheckFunc(NULL)
, mBinderContextUserData(NULL)
, mThreadPoolStarted(false)
, mThreadPoolSeq(1){
if (mDriverFD >= 0) { //已经成功打开 binder 驱动设备
// 将应用进程一块虚拟内存空间与 binder 驱动映射,在此内存块上进行数据通信
mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
if (mVMStart == MAP_FAILED) { //映射失败处理
ALOGE("Using /dev/binder failed: unable to mmap transaction memory.\n");
close(mDriverFD);
mDriverFD = -1;
}
}
}

ProcessState 的构造函数初始化了一些重要的变量,包括调用 open_driver() 打开 binder 设备,初始化 binder 线程最大数量,将 BINDER_VM_SIZE (接近 1M ) 的内存与 binder 驱动 mmap.

除了 ProcessState 的初始化,ProcessState 中还有一些比较重要的方法,比如 getStrongProxyForHandle()、getWeakProxyForHandle() 等,可以通过 handle 值获取对应 IBinder 对象,getWeakProxyForHandle() 方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
wp<IBinder> ProcessState::getWeakProxyForHandle(int32_t handle){
wp<IBinder> result;
AutoMutex _l(mLock);
//查找 IBinder 是否已经创建过
handle_entry* e = lookupHandleLocked(handle);
if (e != NULL) {
IBinder* b = e->binder;
if (b == NULL || !e->refs->attemptIncWeak(this)) {
b = new BpBinder(handle); //没创建过就新建 BpBinder
result = b;
e->binder = b;
if (b) e->refs = b->getWeakRefs();
} else {
result = b;
e->refs->decWeak(this);
}
}
return result;
}

lookupHandleLocked() 方法用于查找本进程中是否已经创建过要获取的 IBinder,如果没有获取到,就创建一个,lookupHandleLocked() 内部通过一个 Vector 来存放创建过的 IBinder:

1
2
3
4
5
6
Vector<handle_entry> mHandleToObject;

struct handle_entry{
IBinder* binder;
RefBase::weakref_type* refs;
}

如上代码所示,每个 IBinder 对象通过一个 handle_entry 结构体存放,也就是说,ProcessState 中有一个全局列表来记录所有的 IBinder 对象。

IPCThreadState

ProcessState 对应于一个进程,是进程内单例,而 IPCThreadState 对应于一个线程,是线程单例(Thread Local)。

ProcessState 中打开了 binder 驱动、进行 mmap 映射,虽然调用了 ioctl() 函数,但主要是一些初始化配置。而具体的 BR_TRANSACTION 等命令都是由 IPCThreadState 负责执行的,当上层传来一个命令,会调用它的 transact 函数,该函数精简后如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
status_t IPCThreadState::transact(int32_t handle,
uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags){
//检查数据是否有效
status_t err = data.errorCheck();
if (err == NO_ERROR) {
//将数据打包塞到 mOut 里
err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
}
if ((flags & TF_ONE_WAY) == 0) { //不是 one way 调用,需要等待回复
if (reply) {
err = waitForResponse(reply);
} else {
Parcel fakeReply;
err = waitForResponse(&fakeReply);
}
} else { //one way 调用,不用等待回复
err = waitForResponse(NULL, NULL);
}
return err;
}

IPCThreadState 中有 mIn、mOut 两个 Parcel 数据,mIn 用来存放从别处读取而来的数据,mOut 存放要写入到别处的数据,在 writeTransactionData() 方法中将数据存放到 mOut,准备写入到 binder 驱动。

waitForResponse() 方法去实际执行写入到 binder 驱动,简化后的 waitForResponse() 方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult){
uint32_t cmd;
int32_t err;
while (1) {
//进一步调用 talkWithDriver 去执行写入数据到 binder 驱动
if ((err=talkWithDriver()) < NO_ERROR) break;
err = mIn.errorCheck(); //检查数据有效性
if (err < NO_ERROR) break;
if (mIn.dataAvail() == 0) continue; //检查数据有效性
cmd = (uint32_t)mIn.readInt32(); //拿到 binder 驱动发过来的命令
switch (cmd) {
//处理命令
case BR_TRANSACTION_COMPLETE:{...}
case BR_DEAD_REPLY:{...}
case BR_FAILED_REPLY:{...}
case BR_ACQUIRE_RESULT:{...}
case BR_REPLY:{...}
default:
//其他命令在 executeCommand 方法中处理
err = executeCommand(cmd);
if (err != NO_ERROR) goto finish;
break;
}
}
return err;
}

可以看到 waitForResponse() 中并没有直接执行写入数据到 binder,而是进一步调用 talkWithDriver 去处理,随后 waitForResponse() 方法处理了由 binder 驱动发送过来的命令,比如 BR_TRANSACTION_COMPLETE :

1
2
3
case BR_TRANSACTION_COMPLETE:
if (!reply && !acquireResult) goto finish;
break;

在 transact() 方法中判断如果是 one way 调用,reply 及 acquireResult 都传入 NULL,所以上面条件成立,直接退出循环,不用再等待 binder 驱动的回复。在上一篇文章细读《深入理解 Android 内核设计思想》(三)Binder 机制 [上] 中 IPC 调用中提到过 BR_TRANSACTION_COMPLETE 命令,再来回顾下:

到目前为止,由 transact() 到 waitForResponse(),已经将要发送的数据准备好,并对后续 binder 驱动的回复也做了处理,但还没看到真正写入数据给 binder 驱动的代码,但已经知道就在 talkWithDriver() 方法中,此方法中主要做了三个工作:1.准备 binder_write_read 数据,2.写入 binder 驱动 3.处理驱动回复。以此将 talkWithDriver() 代码简化分为对应的三部分来看,首先是准备 binder_write_read 数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
status_t IPCThreadState::talkWithDriver(bool doReceive){
binder_write_read bwr; //binder 驱动接受的数据格式
const bool needRead = mIn.dataPosition() >= mIn.dataSize();
const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;
bwr.write_size = outAvail; //要写入的数据量
bwr.write_buffer = (uintptr_t)mOut.data(); //要写入的数据
// This is what we'll read.
if (doReceive && needRead) {
bwr.read_size = mIn.dataCapacity(); //要读取的数据量
bwr.read_buffer = (uintptr_t)mIn.data(); //存放读取数据的内存空间
} else {
bwr.read_size = 0;
bwr.read_buffer = 0;
}
// 如果不需要读也不需要写,那就直接返回
if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;

在 IPCThreadState.h 中声明了 talkWithDriver() 方法的参数 doReceive 默认为 true,waitForResponse() 中没有传入参数,所以这里的 doReceive 为 true。

在上文 binder 驱动相关的结构体 中提到过 binder_write_read,它是 binder 驱动与用户态共用的、存储读写操作的数据,在 binder 驱动内部依赖 binder_write_read 决定是要读取还是写入数据:其内部变量 read_size>0 则代表要读取数据,write_size>0 代表要写入数据,若都大于 0 则先写入,后读取。

准备好 binder_write_read 后,再来看是怎么写入 binder 驱动的,其实很简单,真正执行写入的操作就一行代码:

1
ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr)

对应的会调用到 binder 驱动的 binder_ioctl() 函数,这里不延伸此函数,接着看 talkWithDriver() 方法的第三个工作,处理驱动的回复:

1
2
3
4
5
6
7
8
9
10
11
12
     if (bwr.write_consumed > 0) { //成功写入了数据
if (bwr.write_consumed < mOut.dataSize())
mOut.remove(0, bwr.write_consumed);
else
mOut.setDataSize(0);
}
if (bwr.read_consumed > 0) { //成功读取到了数据
mIn.setDataSize(bwr.read_consumed);
mIn.setDataPosition(0);
}
return NO_ERROR;
}

bwr.write_consumed > 0 代表 binder 驱动消耗了 mOut 中的数据,所以要把这部分已经处理过的数据移除调;bwr.read_consumed > 0 代表 binder 驱动成功的返回了数据给我们,并写入了上面通过 bwr.read_buffer 指定的内存地址,即 mIn 中,所以要对 mIn 对相关的修正。

到这里 talkWithDriver 执行完毕,读取到的数据放到了 mIn 中,也正好对应于上面 waitForResponse() 方法中从 mIn 中取数据的逻辑。

BpBinder

上文介绍 ProcessState 中的 getWeakProxyForHandle() 方法时,构造了一个 BpBinder 对象返回:

1
new BpBinder(handle)

IPCThreadState 作为主要与 binder 驱动交互的对象,它的 transact 方法第一个参数就是 handle 值:

1
2
3
status_t IPCThreadState::transact(int32_t handle,
uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags)

注意这两个线索:一是将 handle 交给 BpBinder 持有,二是在调用 IPCThreadState transact 方法时需要传入 handle,这意味着什么呢?一个 BpBinder 对象就是关联了一个远程 handle 的操作封装,其内部是通过 IPCThreadState 来实现的 。但这个仅是猜想,下面通过 BpBinder 源码来验证是否属实,首先是构造函数:

1
2
3
4
5
6
7
8
9
10
11
BpBinder::BpBinder(int32_t handle)
: mHandle(handle)
, mAlive(1)
, mObitsSent(0)
, mObituaries(NULL)

ALOGV("Creating BpBinder %p handle %d\n", this, mHandle);

extendObjectLifetime(OBJECT_LIFETIME_WEAK);
IPCThreadState::self()->incWeakHandle(handle);
}

在上文智能指针介绍过 OBJECT_LIFETIME_WEAK,其代表 BpBinder 对象的强计数器和弱计数器的值都为 0 时才会被销毁。另外可以看到通过内部变量 mHandle 持有 handle 值,在 BpBinder 的 transact 方法中使用了 mHandle:

1
2
3
4
5
6
7
8
9
10
status_t BpBinder::transact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags){
if (mAlive) {
status_t status = IPCThreadState::self()->transact(
mHandle, code, data, reply, flags);
if (status == DEAD_OBJECT) mAlive = 0;
return status;
}
return DEAD_OBJECT;
}

其内部确实是调用了 IPCThreadState 的 transact 方法,这便验证了 “一个 BpBinder 对象就是关联了一个远程 handle 的操作封装,其内部是通过 IPCThreadState 来实现的” 的描述是正确的。

BinderProxy

先给出结论:BinderProxy 就是 BpBinder,”BpBinder” 中的 “p” 即 Proxy,只不过 BpBinder 是 Native 层的,BinderProxy 是 Java 层的。BinderProxy 和 BpBinder 分别继承自 Java 和 Native 层的 IBinder 接口,即 IBinder.h 和 IBinder.java,它们可以看作同一个接口,都定义了 transact 等方法。

下面根据源码来验证这个结论,ServiceManager.java 中获取 Service Manager 的代码如下:

1
2
3
4
5
6
7
8
private static IServiceManager getIServiceManager() {
if (sServiceManager != null) {
return sServiceManager;
}
// Find the service manager
sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());
return sServiceManager;
}

其中 asInterface 方法接收的参数就是一个 IBinder 类型对象,可想而知,BinderInternal 的 getContextObject() 方法返回的是一个 BinderProxy 对象:

1
2
3
4
5
6
/**
* Return the global "context object" of the system. This is usually
* an implementation of IServiceManager, which you can use to find
* other services.
*/
public static final native IBinder getContextObject();

这里保留了注释,因为书中专门对为什么将 Service Manager 命名为 Context Object 做了解释:每个进程都需要 IPC 操作,IPC 是作为进程的基础配置存在的。而上面代码的注释更直接的描述了此方法,有助于我们理解。

接着看这个方法,对应的 native 实现在 android_util_Binder.cpp 中:

1
2
3
4
static jobject android_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz){
sp<IBinder> b = ProcessState::self()->getContextObject(NULL);
return javaObjectForIBinder(env, b);
}

ProcessState 的 getContextObject() 方法如下:

1
2
3
sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& /*caller*/){
return getStrongProxyForHandle(0);
}

上文介绍过 ProcessState 的 getWeakProxyForHandle() 方法,其内部构造了一个 BpBinder 对象返回,getStrongProxyForHandle() 方法跟 getWeakProxyForHandle() 一样,也是返回了一个 BpBinder 对象,只不过是强引用类型。

javaObjectForIBinder() 方法不再展开,它根据 BpBinder 对象构造了一个 BinderProxy 对象,并且记录了 BpBinder 的内存地址,以便后续从 Java->Native 时,可以根据 BinderProxy 获取到对应的 BpBinder 对象。与 javaObjectForIBinder() 对应, 由 BinderProxy -> BpBinder 调用的是 android_util_Binder.cpp 的 ibinderForJavaObject() 方法。

ServiceManager getService 流程

在对 binder 驱动、ProcessState、IPCThreadState、BpBinder、BinderProxy 一一单点突破之后,通过一个示例将它们串联起来。对于 ServiceManager getService 流程,首先需要获取 handle 值为 0 的 IBinder 对象:

然后调用 transact() 函数进行 IPC 通信:

最后

Service Manager 自身也是一个 Server,相当于 DNS 服务器本身也要提供 IP 地址才能被访问一样,只不过 Service Manager 的 IP 地址是预先就设定好的,句柄值固定为 0。所以任何 Binder Client 都可以直接通过 0 这个 binder 句柄值创建一个 BpBinder,再通过 Binder 驱动去使用 Service Manager 的服务。具体来说,就是调用 BinderInternal 的 getContextObject() 方法来获取 Service Manager 的 BpBinder。

Android 系统同时支持 Java 与 Native 层的 Binder 机制,所以很多对象都有 “双重身份”,比如 IBinder 在 Java 层用 BinderProxy 表示,在 Native 层用 BpBinder 表示。

大多数程序都有 IPC 的需要,而且进程间通信本身又是非常繁琐的,所以 Android 系统特别为 Binder 机制封装了两个类:ProcessState 和 IPCThreadState,分别对应于进程和线程。ProcessState 负责打开 Binder 驱动设备及 mmap;IPCThreadState 负责与 binder 驱动进行具体的命令通信。

原文作者:Ahab

原文链接:http://yhaowa.gitee.io/a8eae25c/

发表日期:April 23rd 2020, 11:24:46 pm

更新日期:May 23rd 2020, 10:57:30 am

版权声明:本文采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可

CATALOG
  1. 1. 智能指针
  2. 2. binder 驱动相关的结构体
  3. 3. ProcessState
  4. 4. IPCThreadState
  5. 5. BpBinder
  6. 6. BinderProxy
  7. 7. ServiceManager getService 流程
  8. 8. 最后