细读《深入理解 Android 内核设计思想》Binder 机制 [中]
Posted 王英豪
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了细读《深入理解 Android 内核设计思想》Binder 机制 [中]相关的知识,希望对你有一定的参考价值。
对冗余挑拣重点,对重点深入补充,输出结构清晰的精简版
- 智能指针
- binder 驱动中的结构体
- ProcessState
- IPCThreadState
- BpBinder
- BinderProxy
- ServiceManager getService 流程
智能指针
在 binder 相关源码可以看到 sp、wp 类型的引用:
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,即用户态的 BBinder 对象 |
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 设备以及内存映射。相关代码如下:
///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 的构造函数如下:
#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() 方法如下:
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:
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 函数,该函数精简后如下:
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() 方法如下:
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 :
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 数据:
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 驱动的,其实很简单,真正执行写入的操作就一行代码:
ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr)
对应的会调用到 binder 驱动的 binder_ioctl() 函数,这里不延伸此函数,接着看 talkWithDriver() 方法的第三个工作,处理驱动的回复:
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 对象返回:
new BpBinder(handle)
IPCThreadState 作为主要与 binder 驱动交互的对象,它的 transact 方法第一个参数就是 handle 值:
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 源码来验证是否属实,首先是构造函数:
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:
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 的代码如下:
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 对象:
/**
* 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 中:
static jobject android_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz)
sp<IBinder> b = ProcessState::self()->getContextObject(NULL);
return javaObjectForIBinder(env, b);
ProcessState 的 getContextObject() 方法如下:
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 驱动进行具体的命令通信。
以上是关于细读《深入理解 Android 内核设计思想》Binder 机制 [中]的主要内容,如果未能解决你的问题,请参考以下文章
细读《深入理解 Android 内核设计思想》Binder 机制 [下]
细读《深入理解 Android 内核设计思想》Binder 机制 [下]
细读《深入理解 Android 内核设计思想》Binder 机制 [中]
细读《深入理解 Android 内核设计思想》Binder 机制 [中]