前言
在前一篇文章《深入掌握Binder原理(上)》中,我们已经了解了Binder的架构以及Binder的组成部分,并且深入的了解了Binder驱动程序以及ServiceManager的原理,在这篇文章中,会对Binder剩下两部分Client和Server进行讲解,这里建议没有阅读上一篇文章的先读完上一篇文章,然后再来看这一篇,只有这两篇文章连起来,才能对Binder的体系有一个完整和深入的了解。
Binder实现原理
为了能更具体的理解Binder中Client和Server这两个组成部分,这里我以我们最熟悉的一个场景:在应用进程中startActivity,这个场景来讲解Clinet端和Server端是如何使用Binder的。在这个场景中,应用进程是Client端,位于system_server进程的ActivityManagerService是Server端。通过理解应用进程是如何调用Binder,通知AMS启动一个Activity的,我们便能理解Android系统中所有Clinet和Server进行Binder通信的原理,因为其他的场景的Binder通信原理都是一样的。
Client
当我们在桌面点击应用icon启动应用或者手动调用startActivity函数启动一个Activity,实际都是通过Binder调用ActivityManangerService去启动指定的Activity,在AMS启动Activity的流程中,会检查该Activity所属的进程是否存在,如果不存在,就会通知Zygote去fork该Activity的进程,关于Activity更详细的启动流程可以看我的这篇文章《Activity启动详解》,在这里我们只需要了解所有通过Zygote fork成功的进程,都会执行onZygoteInit回调,所以我们的应用进程,在第一次被创建时,也会执行onZygoteInit回调,而onZygoteInit中,默认会打开Binder。
/frameworks/base/cmds/app_process/app_main.cpp
virtual void onZygoteInit() {
sp<ProcessState> proc = ProcessState::self();
proc->startThreadPool();
}
在前面讲ServiceManager使用Binder主要有这几步流程,第一步调用open打开binder,第二步调用mmap映射内存,第三步是在Binder驱动中将ServiceManagerServer注册成BinderManager,第四步让ServiceManager进程陷入循环,并不断的调用ioctl监听缓存区是否有数据。
onZygoteInit函数中虽然只有两行代码,但是其实做了ServiceManager第一步,第二步和第四步的事情。这两行代码主要做的事情如下
- ProcessState::self()打开binder驱动,并进行内存的映射
- proc->startThreadPool()让binder线程无线的循环,并不断的通过ioctl往binder驱动读或者写数据。
打开Binder
ProcessState
我们先来看看ProcessState::self()函数是如何打开Binder驱动程序的,ProcessState对象相当于是一个进程操作Binder的工具类,并且它是一个全局的单例对象,通过self获取单例的实例。
/frameworks/native/libs/binder/ProcessState.cpp
sp<ProcessState> ProcessState::self()
{
Mutex::Autolock _l(gProcessMutex);
if (gProcess != NULL) {
return gProcess;
}
gProcess = new ProcessState("/dev/binder");
return gProcess;
}
ProcessState的构造函数实现如下
/frameworks/native/libs/binder/ProcessState.cpp
#define BINDER_VM_SIZE ((1 * 1024 * 1024) - sysconf(_SC_PAGE_SIZE) * 2)
ProcessState::ProcessState(const char *driver)
: mDriverName(String8(driver))
, mDriverFD(open_driver(driver)) //打开binder
, mVMStart(MAP_FAILED)
, mThreadCountLock(PTHREAD_MUTEX_INITIALIZER)
, mThreadCountDecrement(PTHREAD_COND_INITIALIZER)
, mExecutingThreadsCount(0)
, mMaxThreads(DEFAULT_MAX_BINDER_THREADS)
, mStarvationStartTimeMs(0)
, mManagesContexts(false)
, mBinderContextCheckFunc(NULL)
, mBinderContextUserData(NULL)
, mThreadPoolStarted(false)
, mThreadPoolSeq(1)
{
if (mDriverFD >= 0) {
mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);//进行内核空间和用户空间的内存映射
if (mVMStart == MAP_FAILED) {
close(mDriverFD);
mDriverFD = -1;
mDriverName.clear();
}
}
}
ProcessState的构造函数中主要做了这两件事情
- 调用open_driver("/dev/binder")函数,打开位于内核空间binder驱动程序
- 调用mmap函数,将Binder驱动的内核空间内存映射到当前进程的用户空间来
open_driver
我们先看open_driver("/dev/binder")这个函数的实现
/frameworks/native/libs/binder/ProcessState.cpp
static int open_driver(const char *driver)
{
//打开binder驱动程序
int fd = open(driver, O_RDWR | O_CLOEXEC);
if (fd >= 0) {
int vers = 0;
//获取当前binder版本
status_t result = ioctl(fd, BINDER_VERSION, &vers);
if (result == -1) {
close(fd);
fd = -1;
}
if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) {
close(fd);
fd = -1;
}
size_t maxThreads = DEFAULT_MAX_BINDER_THREADS;
//设置binder最大线程数
result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);
if (result == -1) {
ALOGE("Binder ioctl to set max threads failed: %s", strerror(errno));
}
} else {
ALOGW("Opening '%s' failed: %s\n", driver, strerror(errno));
}
return fd;
}
open_driver函数主要做了这三件事情
- 调用I/O函数open打开驱动,open函数会经过系统调用,最终执行binder驱动程序中的open_binder函数
- 调用I/O函数ioctl获取BINDER_VERSION,ioctl函数经过系统调用最终会执行binder驱动程序中的binder_ioctl函数
- 调用I/O函数ioctl设置当前进程最大的Binder线程数量,这里设置的线程数是15个
前一篇文章我们已经知道了系统调用的流程,以及binder驱动中open_binder函数实现,这里就不再讲了,并且还知道了binder_ioctl响应BINDER_SET_CONTEXT_MGR和BINDER_WRITE_READ命令的流程,在这里,我主要补充binder_ioctl函数是如何响应剩下的两个命令BINDER_VERSION和BINDER_SET_MAX_THREADS的。
响应BINDER_VERSION指令
/drivers/staging/android/binder.c
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret;
struct binder_proc *proc = filp->private_data;
struct binder_thread *thread;
unsigned int size = _IOC_SIZE(cmd);
void __user *ubuf = (void __user *)arg;
……
switch (cmd) {
case BINDER_WRITE_READ:
……
break;
case BINDER_SET_MAX_THREADS:
//将最大线程数从用户空间拷贝到内核空间,并赋值给binder_proc结构体中的max_threads变量
if (copy_from_user(&proc->max_threads, ubuf, sizeof(proc->max_threads))) {
ret = -EINVAL;
goto err;
}
break;
case BINDER_SET_CONTEXT_MGR:
……
break;
case BINDER_THREAD_EXIT:
……
break;
case BINDER_VERSION: {
struct binder_version __user *ver = ubuf;
if (size != sizeof(struct binder_version)) {
ret = -EINVAL;
goto err;
}
//将Binder驱动程序版本信息会写到用户空间
if (put_user(BINDER_CURRENT_PROTOCOL_VERSION,
&ver->protocol_version)) {
ret = -EINVAL;
goto err;
}
break;
}
default:
ret = -EINVAL;
goto err;
}
ret = 0;
err:
……
return ret;
}
在binder驱动程序的BINDER_VERSION响应逻辑中,会直接调用put_user函数将当前版本信息写入到用户空间地址**&vers**中,put_user函数是Linux内核函数,功能和copy_to_user类似,都是将数据从内核空间拷贝到用户空间,但是put_user函数只能拷贝一些简单的变量类似,复杂的数据结构和数据的拷贝,还是要copy_to_user来进行。
响应BINDER_SET_MAX_THREADS指令
在binder驱动程序的BINDER_SET_MAX_THREADS响应逻辑中,会调用copy_from_user函数,将线程数量拷贝到内核空间并赋值给proc->max_threads。
mmap
ProcessState构造函数执行open_driver函数打开binder驱动并且设置了binder线程数,便会执行mmap进行内存的映射,mmap的原理以及Binder驱动程序中binder_mmap的实现,在前一篇文章都详细讲了,这里也不重复讲了,需要注意的是,这儿映射的内存大小为BINDER_VM_SIZE,也就是1M-8kb的大小,而前面讲到的ServiceManager映射的内存是128kb。
陷入循环
ProcessState的构造函数中调用open_driver打开了binder驱动,以及调用了mmap进行内存映射后,就会执行proc->startThreadPool()让binder线程陷入无限循环,看一下它是怎么实现的。
/frameworks/native/libs/binder/ProcessState.cpp
void ProcessState::startThreadPool()
{
AutoMutex _l(mLock);
if (!mThreadPoolStarted) {
mThreadPoolStarted = true;
spawnPooledThread(true);
}
}
void ProcessState::spawnPooledThread(bool isMain)
{
if (mThreadPoolStarted) {
String8 name = makeBinderThreadName();
ALOGV("Spawning new pooled thread, name=%s\n", name.string());
sp<Thread> t = new PoolThread(isMain);
t->run(name.string());
}
}
startThreadPool函数实际是启动了一个PoolThread线程,该线程是ProcessState的内部类,然后执行该线程的run方法。
/frameworks/native/libs/binder/ProcessState.cpp
class PoolThread : public Thread
{
public:
explicit PoolThread(bool isMain)
: mIsMain(isMain)
{
}
protected:
virtual bool threadLoop()
{
IPCThreadState::self()->joinThreadPool(mIsMain);
return false;
}
const bool mIsMain;
};
IPCThreadState
PoolThread线程启动时会执行threadLoop函数中的IPCThreadState::self()->joinThreadPool(mIsMain)函数。Binder通信是基于多线程模型的,一个Binder线程就对应了一个IPCThreadState,它是进程中真正调用Binder驱动读写数据的对象,并且IPCThreadState是基于线程的单例模式,通过ThreadLocal,也就是本地线程存储机制,确保每个线程都只有唯一的一个IPCThreadState,我们看一下IPCThreadState::self()里的逻辑。
/frameworks/native/libs/binder/IPCThreadState.cpp
IPCThreadState* IPCThreadState::self()
{
//首次进来gHaveTLS的值为false
if (gHaveTLS) {
restart:
const pthread_key_t k = gTLS;
IPCThreadState* st = (IPCThreadState*)pthread_getspecific(k);
if (st) return st;
return new IPCThreadState;
}
if (!gHaveTLS) {
//创建线程私有数据
int key_create_value = pthread_key_create(&gTLS, threadDestructor);
gHaveTLS = true;
}
goto restart;
}
接着看看IPCThreadState的构造函数
/frameworks/native/libs/binder/IPCThreadState.cpp
IPCThreadState::IPCThreadState()
: mProcess(ProcessState::self()),
mStrictModePolicy(0),
mLastTransactionBinderFlags(0)
{
pthread_setspecific(gTLS, this);
clearCaller();
mIn.setDataCapacity(256);
mOut.setDataCapacity(256);
}
IPCThreadState构造函数初始化了mIn和mOut两个buffer,mIn用来存放其他进程写过来的数据,mOut用来存放需要传递给其他进程的数据。
joinThreadPool
了解了IPCThreadState,我们再看看它的joinThreadPool函数实现。
/frameworks/native/libs/binder/IPCThreadState.cpp
void IPCThreadState::joinThreadPool(bool isMain)
{
mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);
status_t result;
do {
// now get the next command to be processed, waiting if necessary
result = getAndExecuteCommand();
if (result < NO_ERROR && result != TIMED_OUT && result != -ECONNREFUSED && result != -EBADF) {
abort();
}
if(result == TIMED_OUT && !isMain) {
break;
}
} while (result != -ECONNREFUSED && result != -EBADF);
mOut.writeInt32(BC_EXIT_LOOPER);
talkWithDriver(false);
}
joinThreadPool中通过do while不断的循环,并不断调用getAndExecuteCommand函数来进行数据的读写。
/frameworks/native/libs/binder/IPCThreadState.cpp
status_t IPCThreadState::getAndExecuteCommand()
{
status_t result;
int32_t cmd;
//往Binder驱动读写数据
result = talkWithDriver();
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++;
if (mProcess->mExecutingThreadsCount >= mProcess->mMaxThreads &&
mProcess->mStarvationStartTimeMs == 0) {
mProcess->mStarvationStartTimeMs = uptimeMillis();
}
pthread_mutex_unlock(&mProcess->mThreadCountLock);
//处理其他进程写入的数据
result = executeCommand(cmd);
pthread_mutex_lock(&mProcess->mThreadCountLock);
mProcess->mExecutingThreadsCount--;
if (mProcess->mExecutingThreadsCount < mProcess->mMaxThreads &&
mProcess->mStarvationStartTimeMs != 0) {
int64_t starvationTimeMs = uptimeMillis() - mProcess->mStarvationStartTimeMs;
if (starvationTimeMs > 100) {
ALOGE("binder thread pool (%zu threads) starved for %" PRId64 " ms",
mProcess->mMaxThreads, starvationTimeMs);
}
mProcess->mStarvationStartTimeMs = 0;
}
pthread_cond_broadcast(&mProcess->mThreadCountDecrement);
pthread_mutex_unlock(&mProcess->mThreadCountLock);
}
return result;
}
getAndExecuteCommand函数中的关键流程是talkWithDriver函数,这个函数封装了ioctl函数直接会直接操作binder驱动程序,经过talkWithDriver调用后,会检查mIn缓冲区中是否有数据,如果有数据,就表示有其他进程写入了数据进来,则调用executeCommand处理数据。
我们先看看talkWithDriver函数。
talkWithDriver
/frameworks/native/libs/binder/IPCThreadState.cpp
status_t IPCThreadState::talkWithDriver(bool doReceive)
{
if (mProcess->mDriverFD <= 0) {
return -EBADF;
}
binder_write_read bwr;
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();
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;
bwr.write_consumed = 0;
bwr.read_consumed = 0;
status_t err;
do {
//往Binder驱动程序收发送数据
if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
err = NO_ERROR;
else
err = -errno;
if (mProcess->mDriverFD <= 0) {
err = -EBADF;
}
} while (err == -EINTR);
if (err >= NO_ERROR) {
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;
}
return err;
}
talkWithDriver函数中会调用ioctl函数,将binder_write_read结构体传给binder驱动,binder_write_read包含了一个write_buffer和一个read_buffer,write_buffer存放了要写入到其他进程的数据,read_buffer用来接收其他进程返回来的数据。binder驱动程序中会在binder_ioctl函数中响应BINDER_WRITE_READ命令。在上一篇文章中也讲到了ServerMananger陷入无限循环后,也是通过调用ioctl函数来不断的往binder驱动读写数据,并且也讲了Binder驱动是如何响应ServerMananger的ioctl函数中的BINDER_WRITE_READ命令的,这里就不再讲了,和ServerMananger是一样的,唯一的区别是这里是通过IPCThreadState对象封装调用的。
消息发送
经历了前面的步骤后,就能使用Binder进行消息的发送了,接下来开始讲解用户进程和AMS所在system_server进程进行Binder通信的全流程。这里先简单了解一下当我们调用startActivity启动一个activity时所经历的流程。
通知AMS启动Activity启动
startActivity函数最终都会调用startActivityForResult函数,看一下他的实现。
/frameworks/base/core/java/android/app/Activity.java
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
@Nullable Bundle options) {
if (mParent == null) {
options = transferSpringboardActivityOptions(options);
//启动activity
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,
intent, requestCode, options);
if (ar != null) {
mMainThread.sendActivityResult(
mToken, mEmbeddedID, requestCode, ar.getResultCode(),
ar.getResultData());
}
if (requestCode >= 0) {
mStartedActivity = true;
}
cancelInputsAndStartExitTransition(options);
} else {
……
}
}
startActivityForResult函数中实际是调用了Instrumentation对象的execStartActivity方法。
/frameworks/base/core/java/android/app/Instrumentation.java
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
IApplicationThread whoThread = (IApplicationThread) contextThread;
Uri referrer = target != null ? target.onProvideReferrer() : null;
if (referrer != null) {
intent.putExtra(Intent.EXTRA_REFERRER, referrer);
}
……
try {
intent.migrateExtraStreamToClipData();
intent.prepareToLeaveProcess(who);
int result = ActivityManager.getService()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
return null;
}
execStartActivity函数关键的代码就是ActivityManager.getService() .startActivity,通过这一行代码,就能调用AMS启动activity,这一行代码由三部分组成。
- 获取ServiceManager的Binder地址,并根据获取到的Binder地址创建ServiceManager在应用进程的Proxy
- 获取ActivityManagerService的Binder地址,更根据AMS的Binder地址创建AMS在应用进程的Proxy
- 通知ActivityManagerService启动activity。
Proxy是Server在Clinet端的代理,里面实际是持有了Server端的Binder地址,Clinet可以直接通过调用Proxy对象中某个业务函数的方式,如startActivity函数来直接Server通信,而不用自己去进行Binder通信要求的数据格式创建,协议封装,数据的处理等繁琐操作。如果我们不使用Android提供的Proxy,就要自己约定Server端和Clinet端的数据协议,格式,命令协议等等,这样就让Binder的使用变得繁琐和复杂。Proxy文件一般也是编译时自动生成的,当我们在AIDL文件约定好Server和Clinet的业务调用函数时,编译时会根据AIDL文件自动生成供Client端使用的Proxy。
下面来详细看一下这三步的具体实现。
获取ServcieManager
先看看ActivityManager.getService()的实现
/frameworks/base/core/java/android/app/ActivityManager.java
public static final String ACTIVITY_SERVICE = "activity";
public static IActivityManager getService() {
return IActivityManagerSingleton.get();
}
private static final Singleton<IActivityManager> IActivityManagerSingleton =
new Singleton<IActivityManager>() {
@Override
protected IActivityManager create() {
//获取ActivityManagerService的binder地址
final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
//转成AMS的Proxy
final IActivityManager am = IActivityManager.Stub.asInterface(b);
return am;
}
};
可以看到,ActivityManager.getService()函数里面做了这两件事情
- 调用ServiceManager.getService(Context.ACTIVITY_SERVICE)获取AMS的Binder地址
- 调用IActivityManager.Stub.asInterface(b)根据AMS的binder地址,创建AMS在用户进程的Proxy。
接着来看**ServiceManager.getService(Context.ACTIVITY_SERVICE)**的流程。
/frameworks/base/core/java/android/os/ServiceManager.java
public static IBinder getService(String name) {
try {
//检查缓存是否有AMS的Binder地址
IBinder service = sCache