Binder 原理探索

191 阅读10分钟

先不分析 Native 、 Kerne 内核层 的 Binder源码。万物由简入繁,从最简单 Binder 在java 应用层使用说起 , 我们知道 Binder的使用可以借助 Messenger 和 AIDL 。 对于这两种的使用就不在多说了 , AIDL和Messenger 只不过是帮我们生成模板代码 ,简化我们的使用 , Binder在java层最终使用的都要通过如下写法。

Binder在java层的使用

server 端代码。


public class BinderService extends Service
{
    private static final String DESCRIPTOR = "BinderService";
    private static final String TAG = "BinderService";
    
    private MyBinder mBinder = new MyBinder();
  
    public IBinder onBind(Intent t)
    {
        return mBinder;
    }
        
    private class MyBinder extends Binder
    {
        @Override
        protected boolean onTransact(int code, Parcel data, Parcel reply,
                                     int flags) throws RemoteException
        {
            switch (code)
            {
                case 0x110:
                {
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();
                    int _arg1;
                    _arg1 = data.readInt();
                    int _result = _arg0 * _arg1;
                    reply.writeNoException();
                    reply.writeInt(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }
        
    };
    
}


BinderService 需要在AndroidMenifest中注册 。

 <service android:name=".BinderService" >
            <intent-filter>
                <action android:name="com.jansir.binder.demo" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </service>

client端代码。

private IBinder mProxyBinder;

private ServiceConnection mServiceConntion = new ServiceConnection()
	{
		@Override
		public void onServiceDisconnected(ComponentName name)
		{
		}
 
		@Override
		public void onServiceConnected(ComponentName name, IBinder service)
		{    //获取server的binder代理对象
			mProxyBinder = service;
		}
	};


public void bindService(View view){ 
    
    Intent intentPlus = new Intent();
    intentPlus.setAction("com.jansir.binder.demo");
    boolean res = bindService(intentPlus, mServiceConntion, Context.BIND_AUTO_CREATE);
}

public void callServer(View view){
    
    if (mProxyBinder != null){
    	android.os.Parcel _data = android.os.Parcel.obtain();
    	android.os.Parcel _reply = android.os.Parcel.obtain();
    	int _result;
    	try
    	{  
            //方法标注远程服务名称
    		_data.writeInterfaceToken("BinderService");
    		_data.writeInt(50);
    		_data.writeInt(12);
    		mProxyBinder.transact(0x110, _data, _reply, 0);
    		_reply.readException();
    		_result = _reply.readInt();
    		Toast.makeText(this, _result + "", Toast.LENGTH_SHORT).show();
    
    	} catch (RemoteException e){
    		e.printStackTrace();
    	} finally{
    		_reply.recycle();
    		_data.recycle();
    	}
    }
 
}

这是一个典型的C/S 通信模式。 什么是C/S通信模式?
C/S通信模式即客户(client)/ 服务器(server)模型。 以我们日常使用最多的Http 来说 , 一次完整的Http短连接通信主要分为以下几个步骤 : 1.建立连接; 2.发送请求信息; 3.获取响应信息; 4.关闭连接。

Binder在java使用层设计思想

Binder 的设计思想和上述大致相同 , client 通过bindService 和 server建立连接 ,然后client 通过 server 返回的 BinderProxy 调用server方法发送请求信息 , 最后获取server的响应信息 , 通过 unbindService 关闭连接。 说完Binder的设计思想 , 接下来说下Binder的设计原理 :

Binder的设计原理

想要理解Binder的设计原理, 就不得不思考一个问题,为什么要有Binder ?
image.png
Android系统基于Linux内核 ,现有的Linux的IPC机制种类 :1.共享内存 2.管道 3.消息队列 4.socket 等 ,关于Linux的IPC 更详细的介绍, 可以看这篇 linux基础——linux进程间通信(IPC)机制总结 我们需要的IPC 机制 , 是性能好、安全性高、开发者使用不繁琐,可以看到这些传统的Linux 通信机制根本不满足我们对于IPC的要求。

那Binder是怎么做到高性能的呢? 先来看下这张图
可以看到用户空间和内核空间只发生一次数据拷贝 , 性能够好了吧 。 但我还想性能更加好,能不能连一次数据拷贝都不用?

我看你就是在刁难Binder , 一次数据都不拷贝那明显是不行的。任何毫无关系的两方需要通信肯定是需要借助第三方的 。比如你看我的文章现在就是要借助平台 , 文章就是通信的内容 , 如果没有内容你也不知道我在说什么,也就无法通信了。 所以binder机制也是如此,不发生数据的拷贝肯定是无法通信的。 好了,干说了这么多原理还是没提。先别急,再来说说上图的内存映射吧。

mmap 内存映射,简而言之就是将用户空间的一段内存区域映射到内核空间,映射成功后,用户对这段内存区域的修改可以直接反映到内核空间,同样,内核空间对这段区域的修改也直接反映用户空间。那么对于内核空间<---->用户空间两者之间需要大量数据传输等操作的话效率是非常高的。

再来一张图 image.png 现在可以用一句很长的话来总结Binder原理了 。

server端Binder 实体借助Binder驱动与内核空间mmap建立映射关系,然后注册到ServiceManager中。client端从ServiceManager中获取到server的binder代理(或者引用),通过copy_from_user将数据copy到内核空间, server因为存在映射关系也能获取到内核空间的数据,server通过修改内存间接的修改内核空间数据 ,双方通过内核空间的数据进行通信。

啊原来Binder 这么简单 , 我懂了,我这就去手写一个。

但Binder源码横跨 应用 、framework、JNI 、Native、内核 、物理内存层 。牵涉到JAVA 、C++ 、C 语言。 一句话也只能说个大概原理。接下来我们就以实际问题去分析Binder吧。
分析实际问题之前先说说Binder的总体架构。

Binder的总体架构

先来看下Binder总体架构图和总体类图 image.png 关键类图 image.png 可以看到无论是Framework 和 Native 层 ,其实都是对Binder的封装而已。通过层层调用,最终都是通过ioctl去操作内核层达到通信的目的。 对Binder中的架构和类有个大概了解后,我们就以实际问题去分析Binder吧。
还记得文章开始的那个在java层使用的demo吗 , server端的Binder 实体 , 通过onBind 返回给客户端 。我们知道客户端使用的是BinderProxy ,并不是server端的Binder 实体,两个进程无论如何都不能使用同一个对象, 那么它是如何在变成BinderProxy的呢? 带着这个问题我们分析下,binder 引用(句柄)是怎么从server端 传递到client的。

Binder 引用(句柄)的传递过程

先看下 server端的Binder 的实例化过程

    private MyBinder mBinder = new MyBinder();

MyBinder 继承自android.os.Binder , 我们看下android.os.Binder的构造函数

    public Binder(@Nullable String descriptor)  {
        mObject = getNativeBBinderHolder();
    }

getNativeBBinderHolder 会调到/frameworks/base/core/jni/android_util_Binder.cpp 的方法,返回的是binder在native对象的引用地址。 image.png JavaBBinderHolder 持有JavaBBinder ,JavaBBinder 才是关键对象, 我们看下JavaBBinder 就行了。

// frameworks/base/core/jni/android_util_Binder.cpp
class JavaBBinder : public BBinder
{
public:
    // 构造函数
    JavaBBinder(JNIEnv* env, jobject /* Java Binder */ object)
        : mVM(jnienv_to_javavm(env)), mObject(env->NewGlobalRef(object))
    {
        ......
    }

    // 获取我们 Java 中实例化的 Binder 对象
    jobject object() const
    {
        return mObject;
    }

protected:
    // Native 层 JavaBBinder 的 onTransact 函数
    virtual status_t onTransact(
        uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0)
    {   
        // 将 JavaVM convert2 JNIEnv
        JNIEnv* env = javavm_to_jnienv(mVM);
        // 回调 java 中 Binder 对象的 execTransact 方法
        jboolean res = env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact,
            code, reinterpret_cast<jlong>(&data), reinterpret_cast<jlong>(reply), flags);
        
        // ......
        if (code == SYSPROPS_TRANSACTION) {
            // 回调父类的 onTransact 函数
            BBinder::onTransact(code, data, reply, flags);
        }
        
        return res != JNI_FALSE ? NO_ERROR : UNKNOWN_TRANSACTION;
    }

private:
    JavaVM* const   mVM;      // JavaVM 指针对象, 用于获取 JNIEnv*
    jobject const   mObject;  // Java 中的 Binder 对象, 即我们在 Java 中创建的实例
};

从上述代码可以看到 JavaBBinder 继承了 BBinder, 它内部持有了 JavaVM 和 Java 中的 Binder 对象实例, 因此 JavaBBinder 是 Native 层 BBinder 与 Java 层 Binder 连接的桥梁。

总结

java层的Binder 对象与 Native 层的 BBinder 对应。 还记得在service 的onBind 方法中将 Java 层 Binder 对象返回吗?那么这个对象返回到哪里去呢? 这个时候就要先看看bindService 流程了,流程图如下。

这里的进程需要先搞清楚,AMS、WMS、PMS等都是在 system_server进程 , 而system_server进程是由Zygote fork 创建出来的。Zygote 和serviceManager 都是由天字一号进程init创建出来的。 如图 image.png

server onBind 被调用时机

onBind方法在server进程的ActivityThread 中被调用 image.png ActivityManager.getService() 就是我们的AMS的binder Proxy 对象 , 接下来看看AMS的binder Proxy 对象 的publishService方法。 注意这里不能直接查看AMS.java类中的publishService方法 image.png 而是先看IActivityManager的Proxy类中publishService的方法, 调用AMS的publishService方法需要先经过aidl编译生成的IActivityManager.Stub.Proxy.class, image.png 这里才是调到AMS的publishService方法。 这个_arg2就是client的binder引用对象。看以看到_arg2会传给AMS , 而AMS又会把这个传给我们的client 端 。看来client端的binder对象就是在AMS中生成的 。 我们跟进 _arg2 = data.readStrongBinder()看看 。最终又会调到native 层的 readStrongBinder方法。

//android.os.Parcel
static jobject android_os_Parcel_readStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr)
{
    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
    if (parcel != NULL) {
        return javaObjectForIBinder(env, parcel->readStrongBinder());
    }
    return NULL;
}

native 层 parcel->readStrongBinder()

javaObjectForIBinder 方法就是将native 的IBinder转换成jave的IBinder 并返回。我们来看下Parcel 类的 readStrongBinder方法。 image.png readNullableStrongBinder -> unflatten_binder(ProcessState::self(), *this, val)

status_t unflatten_binder(const sp<ProcessState>& proc,
    const Parcel& in, sp<IBinder>* out)
{
    const flat_binder_object* flat = in.readObject(false);

    if (flat) {
        switch (flat->hdr.type) {
             // 我们在flatten中的Type就是这个!
            case BINDER_TYPE_BINDER:
                *out = reinterpret_cast<IBinder*>(flat->cookie);
                return finish_unflatten_binder(nullptr, *flat, in);
            case BINDER_TYPE_HANDLE:
                *out = proc->getStrongProxyForHandle(flat->handle);
                return finish_unflatten_binder(
                    static_cast<BpBinder*>(out->get()), *flat, in);
        }
    }
    return BAD_TYPE;
}

最终会调到Parcel#finish_unflatten_binder , 直接返回一个NO_ERROR image.png 回到最初分析的android_os_Parcel_readStrongBinder方法 , 此方法内部会调用一个javaObjectForIBinder方法, 这个方法是在android_util_Binder.cpp类中

//android_util_Binder.cpp
jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val)
{
    if (val == NULL) return NULL;

    if (val->checkSubclass(&gBinderOffsets)) {
        jobject object = static_cast<JavaBBinder*>(val.get())->object();
        return object;
    }

    BinderProxyNativeData* nativeData = new BinderProxyNativeData();
    nativeData->mOrgue = new DeathRecipientList;
    nativeData->mObject = val;

    jobject object = env->CallStaticObjectMethod(gBinderProxyOffsets.mClass,
            gBinderProxyOffsets.mGetInstance, (jlong) nativeData, (jlong) val.get());

    return object;
}

搜索gBinderProxyOffsets image.png 可以看到javaObjectForIBinder 这个方法就是在native中构建出java 层的BinderProxy 对象将返回给java层 。 在client 端BinderProxy与 native层的BpBinder建立联系 。 AMS 将BinderProxy 返回给clien端代码如下。

//com.android.server.am.ActivityManagerService   
void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
        try {
            if (r != null) {
                IntentBindRecord b = r.bindings.get(filter);
                if (b != null && !b.received) {
                 //调用client端
                 c.conn.connected(r.name, service, false);               
                }
            }
        }
    }

  //下面的代码在client 进程调用 , IServiceConnection.Stub
  private static class InnerConnection extends IServiceConnection.Stub {
            @UnsupportedAppUsage
            final WeakReference<LoadedApk.ServiceDispatcher> mDispatcher;

            InnerConnection(LoadedApk.ServiceDispatcher sd) {
                mDispatcher = new WeakReference<LoadedApk.ServiceDispatcher>(sd);
            }

            public void connected(ComponentName name, IBinder service, boolean dead)
                    throws RemoteException {
                LoadedApk.ServiceDispatcher sd = mDispatcher.get();
                if (sd != null) {
                    sd.connected(name, service, dead);
                }
            }
        }

 //android.app.LoadedApk
  public void doConnected(ComponentName name, IBinder service, boolean dead) {
      
            if (service != null) {
                //调用client onServiceConnected 将service返回
                mConnection.onServiceConnected(name, service);
            }
        }
总结

server端的Binder 实体对象 ,在AMS 所属的system_server进程被转换成BinderProxy 对象然后传给了client端 。server 端Binder 和client 端BinderProxy 对象的联系如下图。 image.png

你以为这就完了 ? 还有client端的BinderProxy的transact 方法流程还没分析。

BinderProxy#transact 方法分析

client端的BinderProxy的transact方法中只有这一个return 。 image.png transactNative 顾名思义应该是调用到native中去了。

static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
        jint code, jobject dataObj, jobject replyObj, jint flags) // throws RemoteException
{


    IBinder* target = getBPNativeData(env, obj)->mObject.get();
    if (target == NULL) {
        //binder被回收了
        jniThrowException(env, "java/lang/IllegalStateException", "Binder has been finalized!");
        return JNI_FALSE;
    }
    
    //这里会调到BpBinder#transact方法
    status_t err = target->transact(code, *data, reply, flags);
  
    return JNI_FALSE;
}

进入BpBinder#transact方法 image.png 调用到IPCThreadState#transact 方法

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

    err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, nullptr);

    
    // 默认情况下,都是采用非oneway的方式, 也就是需要等待服务端的返回结果
    if ((flags & TF_ONE_WAY) == 0) {
        if (reply) {
            err = waitForResponse(reply);
        } else {
            Parcel fakeReply;
            err = waitForResponse(&fakeReply);
        }
      
    } else {
        //不需要等待服务端的返回结果的情况	
        err = waitForResponse(nullptr, nullptr);
    }

    return err;
}

transact主要过程: 先执行writeTransactionData()已向Parcel数据类型的mOut写入数据,此时mIn还没有数据; 然后执行waitForResponse()方法,循环执行,直到收到应答消息. 调用talkWithDriver()跟驱动交互,收到应答消息,便会写入mIn, 则根据收到的不同响应吗,执行相应的操作。 IPCThreadState#waitForResponse方法

status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
    int32_t cmd;
    int32_t err;

    while (1) {
        if ((err=talkWithDriver()) < NO_ERROR) break; 
        err = mIn.errorCheck();
        if (err < NO_ERROR) break; //当存在error则退出循环

         //每当跟Driver交互一次,若mIn收到数据则往下执行一次BR命令
        if (mIn.dataAvail() == 0) continue;

        cmd = mIn.readInt32();

        switch (cmd) {
        case BR_TRANSACTION_COMPLETE:
            //只有当不需要reply, 也就是oneway时 才会跳出循环,否则还需要等待.
            if (!reply && !acquireResult) goto finish; break;

        case BR_DEAD_REPLY:
            err = DEAD_OBJECT;         goto finish;
        case BR_FAILED_REPLY:
            err = FAILED_TRANSACTION;  goto finish;
        case BR_REPLY: ...             goto finish;

        default:
            err = executeCommand(cmd); 
            if (err != NO_ERROR) goto finish;
            break;
        }
    }

finish:
    if (err != NO_ERROR) {
        if (reply) reply->setError(err); //将发送的错误代码返回给最初的调用者
    }
    return err;
}

在这个过程中, 收到以下任一BR_命令,处理后便会退出waitForResponse()的状态:

  • BR_TRANSACTION_COMPLETE: binder驱动收到BC_TRANSACTION事件后的应答消息; 对于oneway transaction,当收到该消息,则完成了本次Binder通信;
  • BR_DEAD_REPLY: 回复失败,往往是线程或节点为空. 则结束本次通信Binder;
  • BR_FAILED_REPLY:回复失败,往往是transaction出错导致. 则结束本次通信Binder;
  • BR_REPLY: Binder驱动向Client端发送回应消息; 对于非oneway transaction时,当收到该消息,则完整地完成本次Binder通信;

talkWithDriver 方法 , 用于与BinderDriver 交互。

//mOut有数据,mIn还没有数据。doReceive默认值为true
status_t IPCThreadState::talkWithDriver(bool doReceive)
{
    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) {
        //接收数据缓冲区信息的填充。当收到驱动的数据,则写入mIn
        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 {
        //ioctl执行binder读写操作,经过syscall,进入Binder驱动。调用Binder_ioctl
        if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
            err = NO_ERROR;
        else
            err = -errno;
        ...
    } 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;
}

调用ioctl去操作BinderDriver , BinderDriver 方面就不去分析了 ,后面再出文章分析 。
放一张大致流程图。 image.png

对于此文如果分析有出入请务必指出 , 另外文中的图涉及到版权问题请联系我删除。

部分参考文章:

Service 启动流程

java层Binder——BinderProxy类

Android Binder框架实现之Binder的设计思想

Binder(2)--sayHello之BinderProxy的创建过程

系统服务管理者:ServiceManager进程

Linux 内存映射函数 mmap()函数详解