前言
Android中Activity,Service等组件需要和AMS通信,这种跨进程通信都是通过Binder完成的。
- Binder是一种进程通信机制
- Binder是一个虚拟物理设备驱动
- Binder.java是发起通信的Java类,根据声明的AIDL文件,帮助我们生成Binder子类,完成进程通信。
- Binder是链接Client Server ServiceManager和Binder驱动程序,形成C/S通信架构
多进程:
- 突破进程内存限制,如图库占用内存太多(大图浏览)
- 功能稳定,独立的通信进程保持长链接的稳定性(微信通信,push)
- 规避内存泄漏,独立的Webview,播放器,进程阻隔内存泄漏导致的问题
- 隔离风险:不稳定的功能放到独立进程,避免导致主进程崩溃
Linux进程通信方式:
- 管道Pipe
- Socket,基于C/S架构通用接口,传输率低,开销大;数据需两次拷贝;依赖上层协议访问接入点是开放的,不安全。
- 信号量
- 共享内存:数据无需拷贝;需要自行处理并发问题;不安全。
- Binder
管道 Socket 信号量,通信原理一样;讷河空间通过系统进行两次拷贝。用户空间的数据复制到内核空间(copy from user),内核空间数据复制到用户空间(copy to user)。
Binder优势
- 数据拷贝一次
- C/S架构,简单。Client与Server独立,稳定性好
- Binder(一次拷贝)性能比共享内存(无拷贝)差一些,其他都优于共享内存
- 安全,内核添加身份(UID/PID)标识。
标题 | Binder | 共享内存 | Socket |
---|---|---|---|
性能 | 拷贝一次 | 无需拷贝 | 拷贝两次 |
稳定 | C/S架构 稳定性好 | 复杂,并发同步问题 | C/S架构,效率低,开销大 |
安全性 | 可靠(UID/PID) | 接入点开放不安全 | 接入点开放不安全 |
进程间通信
进程间内存是隔离的,线程间内存共享的,内存分为:用户空间和内核空间,用户空间之间内存隔离,内核空间内存共享。
- 内存空间(用户空间):用户程序代码运行的地方
- 内核空间:内核代码运行的地方
用户空间和内核空间都是虚拟内存,所有app的内核空间映射物理内存是同一个。 每个进程的用户空间都是独立的,也是隔离的,用户空间和内核空间也是隔离的,所有的内核空间是共享的。
它们是隔离的,即使用户空间崩溃了,内核空间不受影响。
进程间通信:A进程将自己用户空间数据交给内核空间,让内核空间交给接收放进程的用户空间B,完成进程通信,期间发生两次拷贝 copy_from_user,copy_to_user。
- 32位系统:2的32次幂,可访问地址4G,内核空间1G,用户空间3G
- 64位系统:低位0-47可变有效地址(寻址空间257T),高位48-63位全部补0或者补1。
Binder如何拷贝一次完成通信
内核空间通过系统调用将A进程用户空间数据复制到内核空间(copy from user) 内核空间虚拟内存最终必须关联到同一个物理内存(所有的app内核空间都映射同一个物理空间)利用MMU,将虚拟内存转换成物理内存。
内核空间将数据放到内核空间虚拟内存,内核空间的这个虚拟内存映射的物理内存地址和接收方B进程的用户空间中虚拟内存映射的物理内存地址是同一个(如何映射:通过mmap(零拷贝计数)完成映射)。内核往里面放,相当于放到了B进程用户空间的虚拟内存中,B进程可直接访问。
传统IO操作
传统IO,用户空间是不能直接操作文件的。所以传统IO执行两次数据拷贝,虚拟内存拷贝到内核空间虚拟内存,再由内核空间虚拟内存拷贝到物理地址的文件;用户态到内核态,内核态到物理态两次copy都是耗时的,所以IO操作是耗时的。
mmap原理(Binder通信原理)
Linux通过将一个虚拟内存区域与文件描述符关联起来,初始化这个虚拟内存区域的内容,这个过程称内存映射(memory mapping);
通过mmap(系统体提供的Api)将文件和用户空间虚拟内存映射区域建立联系,用户空间直接操作这个文件(无需通过copy数据到内核空间,再由内核空间到文件的过程);
对文件进行mmap会在进程的虚拟内存分配地址空间创建映射关系,采用指针的方式读写操作这段内存,系统会自动回写到对应的文件磁盘上。
mmap本质:能够让虚拟内存和指定物理内存直接联系起来。像一个电话本。
mmap对传统IO优势:
- mmap对文件读写操作只需要从磁盘到用户主存的一次数据拷贝过程(只有一次copy from user),减少数据的拷贝次数(无copy to user),提高文件操作效率;
- mmap使用逻辑内存对磁盘进行映射,操作内存就相当于操作文件,不需要开启线程,操作mmap的速度和操作内存的速度一样快。
- mmap提供一段可供随写的内存块,app只管往里面写数据,由操作系统(如内存不足,进程退出等时候)负责将内存回写到文件
共享内存如何实现零拷贝
发送方和接收方,内核共同映射同一块物理内存就可以实现零拷贝。
Binder驱动是如何启动的
linux中一切皆文件,Binder驱动其实也是一个文件,可以像mmap指定一块内存和这个文件进行联系。 binder_init(),binder_open(),binder_mmap(),binder_ioctll()四个方法来启动。
binder_init():Binder启动的是misc设备,它是没有具体硬件的,是一块内存,misc注册简单;binder_init做了三件事,分配内存、初始化设备、添加设备链表binder_devices;
binder_open():驱动的打开;主要做四件事:初始化binder_proc对象,将当前进程信息保存到proc中,及那个proc信息保存到flip中,将binder_proc添加到binder_procs链表中。
binder_mmap():通过binder_update_page_range分配物理内存,通过binder_insert_free_buffer计算可以使用的buffer大小。binder_mmap主要做三件事:根据用户空间虚拟内存大小分配一块内核空间同等大小的虚拟内存,分配了一块4kb的物理内存,这块物理内存分别映射到用户空间的虚拟内存和内核空间的虚拟内存。4kb只是先分配,真正使用的时候根据需要进行分配。
binder_ioctl():进行文件的读写操作。
通过int_register_android_os_Binder实现Java和native的相互通信。
MMKV核心实现
#include <jni.h>
#include <string>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <android/log.h>
int8_t *m_ptr;
int32_t m_size;
int m_fd;
extern "C"
JNIEXPORT void JNICALL
Java_com_test_llc_1binder_MainActivity_writeTest(JNIEnv *env, jobject thiz) {
std::string file = "/sdcard/a.txt";
//打开文件
m_fd = open(file.c_str(), O_RDWR | O_CREAT, S_IRWXU);
//获得一页内存大小
//Linux采用了分页来管理内存,即内存的管理中,内存是以页为单位,一般的32位系统一页为 4096个字节
m_size = getpagesize();
//将文件设置为 m_size这么大
ftruncate(m_fd, m_size); // 100 1000 10000
// m_size:映射区的长度。 需要是整数页个字节 byte[]
m_ptr = (int8_t *) mmap(0, m_size, PROT_READ | PROT_WRITE, MAP_SHARED, m_fd,
0);
std::string data("mars刚刚写入的数据");
//将 data 的 data.size() 个数据 拷贝到 m_ptr
//Java 类似的:
// byte[] src = new byte[10];
// byte[] dst = new byte[10];
// System.arraycopy(src, 0, dst, 0, src.length);
memcpy(m_ptr, data.data(), data.size());
__android_log_print(ANDROID_LOG_ERROR, "mars", "写入数据:%s", data.c_str());
}
extern "C"
JNIEXPORT void JNICALL
Java_com_test_llc_1binder_MainActivity_readTest(JNIEnv *env, jobject thiz) {
//申请内存
char *buf = static_cast<char *>(malloc(100));
memcpy(buf, m_ptr, 100);
std::string result(buf);
__android_log_print(ANDROID_LOG_ERROR, "mars", "读取数据:%s", result.c_str());
//取消映射
munmap(m_ptr, m_size);
//关闭文件
close(m_fd);
}
网友评论