Binder机制和共享内存 native

 

匿名Binder:

即没有向ServiceManager注册的Binder。

Binder通信并不绝对依赖ServiceManager,它只是一个域名解析器。可有可无,有更方便。

所以可以看到ContextImpl$ApplicationThread,ContentProvider$Transport都是没有向ServiceManager addService,只要client进程能获取proxy即可。如果本来已经建立Binder通信的,就可以直接获取到proxy而不用通过ServiceManager。

 

实名Binder:

 

即通过ServiceManager#addService的Binder子类。

client获取proxy:

ServiceManagerNative#getService

public IBinder getService(String name) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IServiceManager.descriptor);
        data.writeString(name);
        mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0);
        IBinder binder = reply.readStrongBinder();
        reply.recycle();
        data.recycle();
        return binder;
    }

android_os_parcel.cpp#android_os_Parcel_readStrongBinder

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

上面的javaObjectForIBinder方法会把native的Ibinder对象变成一个java层的Ibinder,native层的IBinder应该是被封装在so库里,在源码中搜索不到。

 

Parcel.cpp#readStrongBinder()

sp Parcel::readStrongBinder() const
{
    sp val;
    unflatten_binder(ProcessState::self(), *this, &val);
    return val;
}

Parcel.cpp#unflatten_binder

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

    if (flat) {
        switch (flat->type) {
            case BINDER_TYPE_BINDER:
                *out = reinterpret_cast(flat->cookie);
                return finish_unflatten_binder(NULL, *flat, in);
            case BINDER_TYPE_HANDLE:
                *out = proc->getStrongProxyForHandle(flat->handle);
                return finish_unflatten_binder(
                    static_cast(out->get()), *flat, in);
        }
    }
    return BAD_TYPE;
}

ProccessState.cpp#getStrongProxyForHandle

sp ProcessState::getStrongProxyForHandle(int32_t handle)
{
    sp result;

    AutoMutex _l(mLock);

    handle_entry* e = lookupHandleLocked(handle);

    if (e != NULL) {
        // We need to create a new BpBinder if there isn't currently one, OR we
        // are unable to acquire a weak reference on this current one.  See comment
        // in getWeakProxyForHandle() for more info about this.
        IBinder* b = e->binder;
        if (b == NULL || !e->refs->attemptIncWeak(this)) {
            if (handle == 0) {
                // Special case for context manager...
                // The context manager is the only object for which we create
                // a BpBinder proxy without already holding a reference.
                // Perform a dummy transaction to ensure the context manager
                // is registered before we create the first local reference
                // to it (which will occur when creating the BpBinder).
                // If a local reference is created for the BpBinder when the
                // context manager is not present, the driver will fail to
                // provide a reference to the context manager, but the
                // driver API does not return status.
                //
                // Note that this is not race-free if the context manager
                // dies while this code runs.
                //
                // TODO: add a driver API to wait for context manager, or
                // stop special casing handle 0 for context manager and add
                // a driver API to get a handle to the context manager with
                // proper reference counting.

                Parcel data;
                status_t status = IPCThreadState::self()->transact(
                        0, IBinder::PING_TRANSACTION, data, NULL, 0);
                if (status == DEAD_OBJECT)
                   return NULL;
            }

            b = new BpBinder(handle); 
            e->binder = b;
            if (b) e->refs = b->getWeakRefs();
            result = b;
        } else {
            // This little bit of nastyness is to allow us to add a primary
            // reference to the remote proxy when this team doesn't have one
            // but another team is sending the handle to us.
            result.force_set(b);
            e->refs->decWeak(this);
        }
    }

    return result;
}

上面的方法就根据handle产生了一个BpBinder

可以看到从ServiceManager得到的proxy是一个BpBinder,而后面是怎么从java层调用到这个BpBinder的,在java层的Binder的代码中没有体现出来。在proxy的代码中,最终会调用mRemote.transact(...),然而在java层的Binder代码中transact方法代码如下:

public final boolean transact(int code, Parcel data, Parcel reply,
            int flags) throws RemoteException {
        if (false) Log.v("Binder", "Transact: " + code + " to " + this);
        if (data != null) {
            data.setDataPosition(0);
        }
        boolean r = onTransact(code, data, reply, flags);
        if (reply != null) {
            reply.setDataPosition(0);
        }
        return r;
    }

 

 

 

Proxy中mRemote的由来:

mRemote是一个BinderProxy对象,BinderProxy在Binder.java中。

mRemote是怎么来的呢,看下面:

在传递IBinder对象时(比如:Proxy对象中的mRemote,即服务端),会使用Parcel的方法把IBinder写到Binder驱动中。

/**
     * Write an object into the parcel at the current dataPosition(),
     * growing dataCapacity() if needed.
     */
    public final void writeStrongBinder(IBinder val) {
        nativeWriteStrongBinder(mNativePtr, val);
    }

jni如下:

static void android_os_Parcel_writeStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object)
{
    Parcel* parcel = reinterpret_cast(nativePtr);
    if (parcel != NULL) {
        const status_t err = parcel->writeStrongBinder(ibinderForJavaObject(env, object));
        if (err != NO_ERROR) {
            signalExceptionForError(env, clazz, err);
        }
    }
}

 

native源码如下:

 

 

status_t Parcel::writeStrongBinder(const sp& val)
{
    return flatten_binder(ProcessState::self(), val, this);

}

 

 

 

status_t flatten_binder(const sp& /*proc*/,
    const sp& binder, Parcel* out)
{
    flat_binder_object obj;

    obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
    if (binder != NULL) {
        IBinder *local = binder->localBinder();
        if (!local) {
            BpBinder *proxy = binder->remoteBinder();
            if (proxy == NULL) {
                ALOGE("null proxy");
            }
            const int32_t handle = proxy ? proxy->handle() : 0;
            obj.type = BINDER_TYPE_HANDLE;
            obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */
            obj.handle = handle;
            obj.cookie = 0;
        } else {
            obj.type = BINDER_TYPE_BINDER;
            obj.binder = reinterpret_cast(local->getWeakRefs());
            obj.cookie = reinterpret_cast(local);
        }
    } else {
        obj.type = BINDER_TYPE_BINDER;
        obj.binder = 0;
        obj.cookie = 0;
    }

    return finish_flatten_binder(binder, obj, out);
}

上面的方法就是将IBinder扁平化,有点像序列化,就是将IBinder子类对象(即服务端对象)作类似序列化,但是不是完全按照原来的样子序列化,还会做其他创建对象或者修改的操作。

 

然后client端,拿到这个从服务端进程传过来的服务端对象,需要通过Parcel#readStrongBinder()来获取可以跟驱动打交道,还可以让驱动知道要找哪个服务端的IBinder对象。这个获取到IBinder就是Proxy中的mRemote,这是一个BinderProxy对象。看Parcel#readStrongBinder()

/**
     * Read an object from the parcel at the current dataPosition().
     */
    public final IBinder readStrongBinder() {
        return nativeReadStrongBinder(mNativePtr);
    }

jni方法如下(在parcel.cpp):

 

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

 

native方法如下:

 

 

sp Parcel::readStrongBinder() const
{
    sp val;
    unflatten_binder(ProcessState::self(), *this, &val);
    return val;
}

 

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

    if (flat) {
        switch (flat->type) {
            case BINDER_TYPE_BINDER:
                *out = reinterpret_cast(flat->cookie);
                return finish_unflatten_binder(NULL, *flat, in);
            case BINDER_TYPE_HANDLE:
                *out = proc->getStrongProxyForHandle(flat->handle);
                return finish_unflatten_binder(
                    static_cast(out->get()), *flat, in);
        }
    }
    return BAD_TYPE;
}

 

现在看看

ibinderForJavaObject(env, object) //把java层 IBinder变成native层的IBinder对象

javaObjectForIBinder(env, parcel->readStrongBinder())//把native层的IBinder对象变成java层的IBinder对象

就是ibinderForJavaObject方法把服务端IBinder扁平化写到Binder驱动,而javaObjectForIBinder根据在Binder驱动中扁平化的服务端IBinder,创建一个BinderProxy对象。

最终BinderProxy使用transact方法和Binder驱动交互,并最终将信息传到对应的服务端Binder,会调用Binder#execTransact()->Binder子类#onTransact()

下面是BinderProxy#transact

public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
        return transactNative(code, data, reply, flags);
    }

jni方法(android_util_binder.cpp):

static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
        jint code, jobject dataObj, jobject replyObj, jint flags) // throws RemoteException
{
    if (dataObj == NULL) {
        jniThrowNullPointerException(env, NULL);
        return JNI_FALSE;
    }

    Parcel* data = parcelForJavaObject(env, dataObj);
    if (data == NULL) {
        return JNI_FALSE;
    }
    Parcel* reply = parcelForJavaObject(env, replyObj);
    if (reply == NULL && replyObj != NULL) {
        return JNI_FALSE;
    }

    IBinder* target = (IBinder*)
        env->GetLongField(obj, gBinderProxyOffsets.mObject);
    if (target == NULL) {
        jniThrowException(env, "java/lang/IllegalStateException", "Binder has been finalized!");
        return JNI_FALSE;
    }

    ALOGV("Java code calling transact on %p in Java object %p with code %" PRId32 "\n",
            target, obj, code);


    bool time_binder_calls;
    int64_t start_millis;
    if (kEnableBinderSample) {
        // Only log the binder call duration for things on the Java-level main thread.
        // But if we don't
        time_binder_calls = should_time_binder_calls();

        if (time_binder_calls) {
            start_millis = uptimeMillis();
        }
    }

    //printf("Transact from Java code to %p sending: ", target); data->print();
    status_t err = target->transact(code, *data, reply, flags);
    //if (reply) printf("Transact from Java code to %p received: ", target); reply->print();

    if (kEnableBinderSample) {
        if (time_binder_calls) {
            conditionally_log_binder_call(start_millis, target, code);
        }
    }

    if (err == NO_ERROR) {
        return JNI_TRUE;
    } else if (err == UNKNOWN_TRANSACTION) {
        return JNI_FALSE;
    }

    signalExceptionForError(env, obj, err, true /*canThrowRemoteException*/, data->dataSize());
    return JNI_FALSE;
}

 

Parcel类

 

这个类和Parcelable类是完全不同的,这个是为Binder通信定制的类,native层对应了Parcel.cpp。这个类可以产生BpBinder

 

关于ServiceManager与Binder的关系看下面的优秀博文即可,不重复写了。

 

BinderInternal.getContextObject()

 

Binder IPC通信:形式上对象只能传递parcel和IBinder类型的参数,但是parcel中可以传递object类型,也就是可以传递任何类型的变量。

 

binder通信和共享内存的区别和联系

 下图是binder通信,首先是应用程序把数据通过parcel,应该是通过malloc分配一个空间,虽然是在使用jni调用,并最终在native实现的程序中去把一个object进行扁平化存储在上面分配的空间的,但是无论是java层还是native,只要是是非内核程序(内核程序包括操作系统的核心进程和驱动程序),而在parcel进行的分配空间和对object扁平化时,程序是运行在用户空间的(cpu处于用户态),因为这个应用程序主动调用的程序,而这个此时还不涉及到系统调用,所以没有主动进入内核态运行。所以parcel的write方法写入的东西都是写入了应用进程的用户态空间,最终会把把这些东西通过系统调用去访问binder驱动设备,并写入属于binder驱动的内核空间中,而具体是binder驱动的内核空间的哪一块,就得看binder怎么分了。binder会为每个binder通信的进程分一块空间,而应用进程访问binder驱动并且把东西写入驱动的内核空间时,是写入到binder驱动内核空间中分给接收应用进程的那一块内存。这就是通过把用户空间中的数据复制到内核空间中,然后把binder驱动作为一个中介存储空间;即发送进程把数据写到binder驱动的内和空间中,然后接收进程从内核空间中把内容复制到属于自己的用户空间。

共享内存:有两种方式,一种是shmget,这种方式是直接把多个进程各自的一个逻辑地址(各个进程的逻辑地址都不同)映射到同一个物理内存空间,这种方式是读写是最快,而且各进程无需去复制到自己的进程空间中,多进程通信的效率比较高,但是使用起来比较复杂,shmget方式和mmap内存映射方式就类似低级语言和高级语言的区别,性能和代码读写维护的便利的权衡。mmap内存映射,使用相对于shmget方式比较简便,而且虽然最终映射的文件还是说占用物理内存空间,但是一个文件可以分块载入到内存中,那么mmap就可以使用更大的共享内存区了,而shmget是直接映射物理内存,所以共享区大小受限。而且mmap因为是映射磁盘的文件,所以在关闭或者关机时载入到内存中的文件会保存到磁盘中,就是mmap实现的共享内存中的内容可以断电永久保存。

shmget的api:

(1)通过int shmget(key_t key, size_t size, int shmflg);在物理内存创建一个共享内存,返回共享内存的编号。
(2)通过void *shmat(int shmid, constvoid shmaddr,int shmflg);连接成功后把共享内存区对象映射到调用进程的地址空间
(3)通过void *shmdt(constvoid* shmaddr);断开用户级页表到共享内存的那根箭头。
(4)通过int shmctl(int shmid, int cmd, struct shmid_ds* buf);释放物理内存中的那块共享内存。

 

总结mmap和shm:
1、mmap是在磁盘上建立一个文件,每个进程地址空间中开辟出一块空间进行映射。
而对于shm而言,shm每个进程最终会映射到同一块物理内存。shm保存在物理内存,这样读写的速度要比磁盘要快,但是存储量不是特别大。
2、相对于shm来说,mmap更加简单,调用更加方便,所以这也是大家都喜欢用的原因。
3、另外mmap有一个好处是当机器重启,因为mmap把文件保存在磁盘上,这个文件还保存了操作系统同步的映像,所以mmap不会丢失,但是shmget就会丢失。

内存的知识可以查看:https://blog.csdn.net/qq_34228570/article/details/72995997

鱼思故渊的专栏:linux内存映射mmap原理分析

Binder机制和共享内存 native_第1张图片

 

Android系统进程间通信Binder机制在应用程序框架层的Java接口源代码分析

https://blog.csdn.net/freshui/article/details/55051268:parcel

Android Bander设计与实现 - 设计篇设计

 

 

Binder相关的jni代码和c代码及头文件路径:

frameworks/base/core/jni

frameworks/native/include

frameworks/native/libs/binder

 

 

 

你可能感兴趣的:(android系统相关)