深入掌握Binder原理(下)

本文深入探讨了Android Binder的实现原理,包括Client端如何打开Binder、陷入循环以及发送消息,以及Server端如何响应和服务注册。通过一个典型的应用启动Activity的场景,详细解释了Binder在Client和Server之间的交互过程,涉及IPCThreadState、ServiceManager和ActivityManagerService的角色和作用。通过理解这些细节,读者能够全面掌握Binder通信机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

在前一篇文章《深入掌握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第一步,第二步和第四步的事情。这两行代码主要做的事情如下

  1. ProcessState::self()打开binder驱动,并进行内存的映射
  2. 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的构造函数中主要做了这两件事情

  1. 调用open_driver("/dev/binder")函数,打开位于内核空间binder驱动程序
  2. 调用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函数主要做了这三件事情

  1. 调用I/O函数open打开驱动,open函数会经过系统调用,最终执行binder驱动程序中的open_binder函数
  2. 调用I/O函数ioctl获取BINDER_VERSION,ioctl函数经过系统调用最终会执行binder驱动程序中的binder_ioctl函数
  3. 调用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构造函数初始化了mInmOut两个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,这一行代码由三部分组成。

  1. 获取ServiceManager的Binder地址,并根据获取到的Binder地址创建ServiceManager在应用进程的Proxy
  2. 获取ActivityManagerService的Binder地址,更根据AMS的Binder地址创建AMS在应用进程的Proxy
  3. 通知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()函数里面做了这两件事情

  1. 调用ServiceManager.getService(Context.ACTIVITY_SERVICE)获取AMS的Binder地址
  2. 调用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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值