匿名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通信,首先是应用程序把数据通过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原理分析
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