1.Binder的设计方案
在理解Binder架构前,我们来考虑下,如果是你,该如何设计一个Binder的进程间通信机制。
要实现一个IPC通信那么需要几个核心要素:
1)发起端:肯定包括发起端所从属的进程,以及实际执行传输动作的线程
2)接收端:接收发送端的数据。
3)待传输的数据
4)内存映射,内核态
首先先画一个最简单的IPC通信图:
进程Process1和进程Process2 通过IPC通信机制进行通信。
再进行扩展调整,把IPC机制换成Binder机制,那么就变成如下的图形:
由于Android存在进程隔离,那么两个进程之间是不能直接传输数据的,Process1需要得到Process2的代理,Process2需要一个实体。
为了实现RPC,我们的代理都是提供接口,称为"接口代理",实体需要提供"接口实体",如下图所示:
我们把代理改成BpBinder,实体改成BBinder,接口代理改成BpInterface,接口实现体改成BnInterface。
我们都知道两个进程的数据共享,需要陷入内核态,那就需要一个驱动设备"/dev/binder",同时需要一个守护进行来进行service管理,我们成为ServiceManager。
进一步演变为:
假如我们想要把通过Process1 的微信信息发送给Process2的微信,我们需要做下面几步:
0)Process2在腾讯服务器中进行注册(包括微信名称、当前活动的IP地址等)
1)Process1从朋友列表中中查找到 Process2的名称,这就是Process2的别名:"service_name"
2)Process1 编写消息消息内容,点击发送。 这里的消息内容就是IPC数据
3)数据会发送到腾讯的服务器,服务器理解为Binder驱动
4)服务器从数据库中解析出IPC数据,找到Process2信息,转到Process2注册的地址, 数据库理解为ServiceManager
5)把数据发给Process2,完成Process1和Process2的通信
我们可以简单的把上面的顺序内容进行转换:
1)Binder驱动---腾讯服务器
2)数据库--ServiceManager
3)Service_name: Process2的微信名称
4)IPC数据:Process1 发送的微信消息
Native C/C++和内核进行通信需要通过系统调用,ServiecManager的主要用来对Service管理,提供了add\find\list等操作。Native进程的数据直接可以通过系统调用陷入内核态,进入图像转换,变为如下:
上面列举的是Native C/C++空间的进程进行Binder通信机制,那么JAVA层是如何通信的呢,Native层的Binder提供的是libbinder.so,那么从JAVA到Native,需要经过JNI、Framework层的封装,
JNI层的命名通常为android_util_xxx,我们这里是binder机制,那么JNI层的文件为 android_util_binder,同时Native的BBinder不能直接传给JAVA层,在JNI里面转换了一个JavaBBinder对象。
Framework层给应用层提供时,其实提供的也是一个代理,我们也称之为BinderProxy。
在JAVA侧要对应一个Binder的实体,称之为Binder。
JAVA侧的服务进行也需要一个管理者,类似于Native,创建了JAVA的ServiceManager,那么设计如下:
2.Binder何时初始化
我们已经讲了这么多关于binder的通信的案例了,那么Binder到底是在什么时候初始化呢?
Binder初始化一般是指binder驱动的初始化,大家在使用binder的过程中,我们从来没有执行过new Binder的方式来实现Binder初始化,原因很简单:binder初始化有它自身独立的特点。
每一个应用进程启动的时候,都是通过zygote fork产生的,所以,当fork产生进程后app进程的代码就开始执行,就开始运行的地方如下:
arduino
public static final Runnable zygoteInit(int targetSdkVersion,
long[] disabledCompatChanges,
String[] argv, ClassLoader classLoader) {
if (RuntimeInit.DEBUG) {
Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from zygote");
}
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ZygoteInit");
RuntimeInit.redirectLogStreams();
RuntimeInit.commonInit();//初始化运行环境
ZygoteInit.nativeZygoteInit();//启动Binder ,方法在 androidRuntime.cpp中注册
// 通过反射创建程序入口函数的 Method 对象,并返回 Runnable 对象
//ActivityThread.main();
return RuntimeInit.applicationInit(targetSdkVersion, disabledCompatChanges, argv,
classLoader);
}
大家可以看到,会执行ZygoteInit.nativeZygoteInit()函数,而nativeZygoteInit函数执行appRuntime的onZygoteInit代码,也就是App_main.cpp中的 onZygoteInit()函数,函数如下:
scss
virtual void onZygoteInit()
{
sp<ProcessState> proc = ProcessState::self();
ALOGV("App process: starting thread pool.\n");
proc->startThreadPool();
}
在ProcessState的self函数里面就会初始化ProcessState(),而这个初始化的一个非常重要的动作就是启动binder驱动和并构建binder的Map映射。具体代码如下:
scss
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)
, mBinderContextCheckFunc(nullptr)
, mBinderContextUserData(nullptr)
, mThreadPoolStarted(false)
, mThreadPoolSeq(1)
, mCallRestriction(CallRestriction::NONE)
{
// TODO(b/139016109): enforce in build system
#if defined(__ANDROID_APEX__)
LOG_ALWAYS_FATAL("Cannot use libbinder in APEX (only system.img libbinder) since it is not stable.");
#endif
if (mDriverFD >= 0) {
//调用mmap接口向Binder驱动中申请内核空间的内存
// mmap the binder, providing a chunk of virtual address space to receive transactions.
mVMStart = mmap(nullptr, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
if (mVMStart == MAP_FAILED) {
// *sigh*
ALOGE("Using %s failed: unable to mmap transaction memory.\n", mDriverName.c_str());
close(mDriverFD);
mDriverFD = -1;### 1.Binder的设计方案
在理解Binder架构前,我们来考虑下,如果是你,该如何设计一个Binder的进程间通信机制。
要实现一个IPC通信那么需要几个核心要素:
1)发起端:肯定包括发起端所从属的进程,以及实际执行传输动作的线程
2)接收端:接收发送端的数据。
3)待传输的数据
4)内存映射,内核态
首先先画一个最简单的IPC通信图:
![1.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/67cf292805cf4d78ad91a76b3394c904~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=720&h=514&s=31134&e=jpg&b=fefefe)
进程Process1和进程Process2 通过IPC通信机制进行通信。
再进行扩展调整,把IPC机制换成Binder机制,那么就变成如下的图形:
![2.jpg](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/512c70f9d16b466ea54792509a1d9689~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=290&h=142&s=5844&e=jpg&b=fefefe)
由于Android存在进程隔离,那么两个进程之间是不能直接传输数据的,Process1需要得到Process2的代理,Process2需要一个实体。
![2.png]()
为了实现RPC,我们的代理都是提供接口,称为"接口代理",实体需要提供"接口实体",如下图所示:
![3.jpg](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3bd8ab751aca4353bf6e4e05a6d4b004~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=293&h=244&s=10955&e=jpg&b=fefefe)
我们把代理改成BpBinder,实体改成BBinder,接口代理改成BpInterface,接口实现体改成BnInterface。
我们都知道两个进程的数据共享,需要陷入内核态,那就需要一个驱动设备"/dev/binder",同时需要一个守护进行来进行service管理,我们成为ServiceManager。
进一步演变为:
![4.jpg](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ea710668c1f44738a5588b0e2d731832~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=314&h=387&s=16740&e=jpg&b=fefefe)
假如我们想要把通过Process1 的微信信息发送给Process2的微信,我们需要做下面几步:
0)Process2在腾讯服务器中进行注册(包括微信名称、当前活动的IP地址等)
1)Process1从朋友列表中中查找到 Process2的名称,这就是Process2的别名:"service_name"
2)Process1 编写消息消息内容,点击发送。 这里的消息内容就是IPC数据
3)数据会发送到腾讯的服务器,服务器理解为Binder驱动
4)服务器从数据库中解析出IPC数据,找到Process2信息,转到Process2注册的地址, 数据库理解为ServiceManager
5)把数据发给Process2,完成Process1和Process2的通信
**我们可以简单的把上面的顺序内容进行转换:**
1)Binder驱动---腾讯服务器
2)数据库--ServiceManager
3)Service_name: Process2的微信名称
4)IPC数据:Process1 发送的微信消息
Native C/C++和内核进行通信需要通过系统调用,ServiecManager的主要用来对Service管理,提供了add\find\list等操作。Native进程的数据直接可以通过系统调用陷入内核态,进入图像转换,变为如下:
![5.jpg](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c6ba190bb43c4739a2cc4cb269c62c7f~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=493&h=404&s=26828&e=jpg&b=fefefe)
上面列举的是Native C/C++空间的进程进行Binder通信机制,那么JAVA层是如何通信的呢,Native层的Binder提供的是libbinder.so,那么从JAVA到Native,需要经过JNI、Framework层的封装,
JNI层的命名通常为android_util_xxx,我们这里是binder机制,那么JNI层的文件为 android_util_binder,同时Native的BBinder不能直接传给JAVA层,在JNI里面转换了一个JavaBBinder对象。
Framework层给应用层提供时,其实提供的也是一个代理,我们也称之为BinderProxy。
在JAVA侧要对应一个Binder的实体,称之为Binder。
JAVA侧的服务进行也需要一个管理者,类似于Native,创建了JAVA的ServiceManager,那么设计如下:
![6.jpg](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/484f2267e477410788c0f1f5daf55c4e~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=543&h=499&s=40136&e=jpg&b=fdfdfd)
### []()2.Binder何时初始化
我们已经讲了这么多关于binder的通信的案例了,那么Binder到底是在什么时候初始化呢?
Binder初始化一般是指binder驱动的初始化,大家在使用binder的过程中,我们从来没有执行过new Binder的方式来实现Binder初始化,原因很简单:binder初始化有它自身独立的特点。
每一个应用进程启动的时候,都是通过zygote fork产生的,所以,当fork产生进程后app进程的代码就开始执行,就开始运行的地方如下:
public static final Runnable zygoteInit(int targetSdkVersion, long[] disabledCompatChanges, String[] argv, ClassLoader classLoader) { if (RuntimeInit.DEBUG) { Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from zygote"); }
scss
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ZygoteInit");
RuntimeInit.redirectLogStreams();
RuntimeInit.commonInit();//初始化运行环境
ZygoteInit.nativeZygoteInit();//启动Binder ,方法在 androidRuntime.cpp中注册
// 通过反射创建程序入口函数的 Method 对象,并返回 Runnable 对象
//ActivityThread.main();
return RuntimeInit.applicationInit(targetSdkVersion, disabledCompatChanges, argv,
classLoader);
}
scss
大家可以看到,会执行ZygoteInit.nativeZygoteInit()函数,而nativeZygoteInit函数执行appRuntime的onZygoteInit代码,也就是App_main.cpp中的 onZygoteInit()函数,函数如下:
virtual void onZygoteInit() { sp proc = ProcessState::self(); ALOGV("App process: starting thread pool.\n"); proc->startThreadPool(); }
javascript
在ProcessState的self函数里面就会初始化ProcessState(),而这个初始化的一个非常重要的动作就是启动binder驱动和并构建binder的Map映射。具体代码如下:
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) , mBinderContextCheckFunc(nullptr) , mBinderContextUserData(nullptr) , mThreadPoolStarted(false) , mThreadPoolSeq(1) , mCallRestriction(CallRestriction::NONE) {
// TODO(b/139016109): enforce in build system #if defined(ANDROID_APEX) LOG_ALWAYS_FATAL("Cannot use libbinder in APEX (only system.img libbinder) since it is not stable."); #endif
if (mDriverFD >= 0) {
//调用mmap接口向Binder驱动中申请内核空间的内存 // mmap the binder, providing a chunk of virtual address space to receive transactions. mVMStart = mmap(nullptr, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0); if (mVMStart == MAP_FAILED) { // sigh ALOGE("Using %s failed: unable to mmap transaction memory.\n", mDriverName.c_str()); close(mDriverFD); mDriverFD = -1; mDriverName.clear(); } }
#ifdef ANDROID LOG_ALWAYS_FATAL_IF(mDriverFD < 0, "Binder driver '%s' could not be opened. Terminating.", driver); #endif }
arduino
所以,总的来说,Binder的初始化是在进程已创建就完成了。创建进程后会第一时间为这个进程打开一个binder驱动,并调用mmap接口向Binder驱动中申请内核空间的内存。
mDriverName.clear();
}
}
#ifdef __ANDROID__
LOG_ALWAYS_FATAL_IF(mDriverFD < 0, "Binder driver '%s' could not be opened. Terminating.", driver);
#endif
}
所以,总的来说,Binder的初始化是在进程已创建就完成了。创建进程后会第一时间为这个进程打开一个binder驱动,并调用mmap接口向Binder驱动中申请内核空间的内存。
今日分享到此结束,对你有帮助的话,点个赞再走呗,如遇侵权联系删除
关注公众号:Android老皮解锁 《Android十大板块文档》 ,让学习更贴近未来实战。已形成PDF版
内容如下:
1.Android车载应用开发系统学习指南(附项目实战)
2.Android Framework学习指南,助力成为系统级开发高手
3.2023最新Android中高级面试题汇总+解析,告别零offer
4.企业级Android音视频开发学习路线+项目实战(附源码)
5.Android Jetpack从入门到精通,构建高质量UI界面
6.Flutter技术解析与实战,跨平台首要之选
7.Kotlin从入门到实战,全方面提升架构基础
8.高级Android插件化与组件化(含实战教程和源码)
9.Android 性能优化实战+360°全方面性能调优
10.Android零基础入门到精通,高手进阶之路