2023-07-24 10:20:54 12阅读
首先我们来看下安卓应用进程在内存中的分布,如下图所示:
每个Android进程,只能运行在自己的进程所拥有的虚拟地址空间,如果是32位的系统,对应一个4GB的虚拟地址空间,其中3GB是用户空,1GB是内核空间,而内核空间的大小是可以通过参数配置的。对于用户空间,不同进程之间彼此是不能共享的,而内核空间确实可以共享的。Client进程与Server进程通信,恰恰是利用进程间可共享的内核内空间来完成底层通信工作的,Client端与Server端进程往往采用ioctl等方法跟内核空间的驱动进行。(Binder是Android系统提供的一种IPC机制。每个Android的进程,都可以有一块用户空间和内核空间。用户空间在不同进程间不能共享,内核空间可以共享。Binder就是一个利用可以共享的内核空间,完成高性能的进程间通信的方案。)
1、传统IPC:匿名管道(PIPE)、信号(signal)、有名管道(FIFO) 2、AT&T Unix:共享内存,信号量,消息队列 3、BSD Unix:Socket
性能:目前Linux支持的IPC包括传统的管道,System V IPC(包括消息队列/共享内存/信号量)以及socket,但是只有socket支持Client/Server的通信方式,由于socket是一套通用当初网络通信方式,其效率低下,且消耗比较大(socket建立连接过程和中断连接过程都有一定的开销),明显在手机上不适合大面积使用socket。而消息队列和管道采用"存储-转发" 方式,即数据先从发送方缓存区拷贝到内核开辟的缓存区中,然后再从内核缓存中拷贝到接收方缓存中,至少有两次拷贝过程。共享内存虽然无需拷贝,但控制复杂,难以使用。 安全性:在安全性方面,Android作为一个开放式,拥有众多开发者的平台,应用程序的来源广泛,确保智能终端的安全是非常重要的。终端用户不希望从网上下载的程序在不知情的情况下偷窥隐私数据,连接无线网络,长期操作底层设备导致电池很快耗尽的情况。传统IPC没有任何安全措施,完全依赖上层协议来去报。首先传统IPC的接受方无法获取对方进程可靠的UID/PID(用户ID/进程ID),从而无法鉴别对方身份。Android为每个安装好的应用程序分配了自己的UID,故进程的UID是鉴别进程的身份的重要标志。使用传统IPC只能由用户在数据包里填入UID/PID,但这样不可靠,容易被恶意程序利用。可靠的身份标记只由IPC机制本身在内核中添加。其次传统IPC访问接入点是开放的,无法建立私有通道。比如命名管道、system V的键值,socket的ip地址或者文件名都是开放的,只要知道这些接入点的程序都可以对端建立连接,不管怎样都无法阻止恶意程序通过接收方地址获得连接。
相比于传统的跨进程通信手段,通信双方必须要处理线程同步,内存管理等问题,工作量大,而且问题多,就像我们前面介绍的传统IPC 命名管道(FIFO) 信号量(semaphore) 消息队列已经从Android中去掉了,同其他IPC相比,Socket是一种比较成熟的通信手段了,同步控制也很容易实现。Socket用于网络通信非常合适,但是用于进程间通信就效率很低。 Android在架构上一直希望模糊进程的概念,取而代之以组件的概念。应用也不需要关心组件存放的位置、组件运行在那个进程中、组件的生命周期等问题。随时随地的,只要拥有Binder对象,就能使用组件的功能。Binder就像一张网,将整个系统的组件,跨进程和线程的组织在一起。
/** Construct the stub at attach it to the interface. */
publicStub()
{
this.attachInterface(this,DESCRIPTOR);
}
/**
* Cast an IBinder object into an android.content.pm.IPackageStatsObserver interface,
* generating a proxy if needed.
*/
public staticandroid.content.pm.IPackageStatsObserverasInterface(android.os.IBinder obj)
{
if((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if(((iin!=null)&&(iininstanceofandroid.content.pm.IPackageStatsObserver))) {
return((android.content.pm.IPackageStatsObserver)iin);
}
return newandroid.content.pm.IPackageStatsObserver.Stub.Proxy(obj);}
概念
|
作用
|
Binder代理对象
|
类型为BpBinder,在用户空间创建,且执行在Client进程中.会被Client进程中的其他对象引用,另外会引用Binder驱动程序中的Binder引用对象.
|
Binder引用对象
|
类型为binder_ref,在Binder驱动程序中创建,被Binder代理对象引用.
|
Binder实体对象
|
类型为binder_node,在Binder驱动程序中创建,被Binder引用对象所引用
|
Binder本地对象
|
类型为BBinder,在用户空间中创建,且执行在Server进程中.会被Server进程中其他对象引用,还会被Binder实体对象引用.
|
一次Binder通信最大可以传输多大的数据
1MB-8KB的限制来源于哪里呢?(PS:8k是两个pagesize,一个pagesize是申请物理内存的最小单元)
#defineBINDER_VM_SIZE ((1 * 1024 * 1024) - sysconf(_SC_PAGE_SIZE) * 2)//这里的限制是1MB-4KB*2
ProcessState::ProcessState(constchar*driver)
{
if(mDriverFD>=0){
// mmap the binder, providing a chunk of virtual address space to receive transactions.
// 调用mmap接口向Binder驱动中申请内核空间的内存
mVMStart=mmap(0,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();
}
}
}
virtualvoidonZygoteInit()
{
sp<ProcessState>proc=ProcessState::self();
ALOGV("App process: starting thread pool.\n");
proc->startThreadPool();
}
2023-07-24 10:20:54 12阅读
首先我们来看下安卓应用进程在内存中的分布,如下图所示:
每个Android进程,只能运行在自己的进程所拥有的虚拟地址空间,如果是32位的系统,对应一个4GB的虚拟地址空间,其中3GB是用户空,1GB是内核空间,而内核空间的大小是可以通过参数配置的。对于用户空间,不同进程之间彼此是不能共享的,而内核空间确实可以共享的。Client进程与Server进程通信,恰恰是利用进程间可共享的内核内空间来完成底层通信工作的,Client端与Server端进程往往采用ioctl等方法跟内核空间的驱动进行。(Binder是Android系统提供的一种IPC机制。每个Android的进程,都可以有一块用户空间和内核空间。用户空间在不同进程间不能共享,内核空间可以共享。Binder就是一个利用可以共享的内核空间,完成高性能的进程间通信的方案。)
1、传统IPC:匿名管道(PIPE)、信号(signal)、有名管道(FIFO) 2、AT&T Unix:共享内存,信号量,消息队列 3、BSD Unix:Socket
性能:目前Linux支持的IPC包括传统的管道,System V IPC(包括消息队列/共享内存/信号量)以及socket,但是只有socket支持Client/Server的通信方式,由于socket是一套通用当初网络通信方式,其效率低下,且消耗比较大(socket建立连接过程和中断连接过程都有一定的开销),明显在手机上不适合大面积使用socket。而消息队列和管道采用"存储-转发" 方式,即数据先从发送方缓存区拷贝到内核开辟的缓存区中,然后再从内核缓存中拷贝到接收方缓存中,至少有两次拷贝过程。共享内存虽然无需拷贝,但控制复杂,难以使用。 安全性:在安全性方面,Android作为一个开放式,拥有众多开发者的平台,应用程序的来源广泛,确保智能终端的安全是非常重要的。终端用户不希望从网上下载的程序在不知情的情况下偷窥隐私数据,连接无线网络,长期操作底层设备导致电池很快耗尽的情况。传统IPC没有任何安全措施,完全依赖上层协议来去报。首先传统IPC的接受方无法获取对方进程可靠的UID/PID(用户ID/进程ID),从而无法鉴别对方身份。Android为每个安装好的应用程序分配了自己的UID,故进程的UID是鉴别进程的身份的重要标志。使用传统IPC只能由用户在数据包里填入UID/PID,但这样不可靠,容易被恶意程序利用。可靠的身份标记只由IPC机制本身在内核中添加。其次传统IPC访问接入点是开放的,无法建立私有通道。比如命名管道、system V的键值,socket的ip地址或者文件名都是开放的,只要知道这些接入点的程序都可以对端建立连接,不管怎样都无法阻止恶意程序通过接收方地址获得连接。
相比于传统的跨进程通信手段,通信双方必须要处理线程同步,内存管理等问题,工作量大,而且问题多,就像我们前面介绍的传统IPC 命名管道(FIFO) 信号量(semaphore) 消息队列已经从Android中去掉了,同其他IPC相比,Socket是一种比较成熟的通信手段了,同步控制也很容易实现。Socket用于网络通信非常合适,但是用于进程间通信就效率很低。 Android在架构上一直希望模糊进程的概念,取而代之以组件的概念。应用也不需要关心组件存放的位置、组件运行在那个进程中、组件的生命周期等问题。随时随地的,只要拥有Binder对象,就能使用组件的功能。Binder就像一张网,将整个系统的组件,跨进程和线程的组织在一起。
/** Construct the stub at attach it to the interface. */
publicStub()
{
this.attachInterface(this,DESCRIPTOR);
}
/**
* Cast an IBinder object into an android.content.pm.IPackageStatsObserver interface,
* generating a proxy if needed.
*/
public staticandroid.content.pm.IPackageStatsObserverasInterface(android.os.IBinder obj)
{
if((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if(((iin!=null)&&(iininstanceofandroid.content.pm.IPackageStatsObserver))) {
return((android.content.pm.IPackageStatsObserver)iin);
}
return newandroid.content.pm.IPackageStatsObserver.Stub.Proxy(obj);}
概念
|
作用
|
Binder代理对象
|
类型为BpBinder,在用户空间创建,且执行在Client进程中.会被Client进程中的其他对象引用,另外会引用Binder驱动程序中的Binder引用对象.
|
Binder引用对象
|
类型为binder_ref,在Binder驱动程序中创建,被Binder代理对象引用.
|
Binder实体对象
|
类型为binder_node,在Binder驱动程序中创建,被Binder引用对象所引用
|
Binder本地对象
|
类型为BBinder,在用户空间中创建,且执行在Server进程中.会被Server进程中其他对象引用,还会被Binder实体对象引用.
|
一次Binder通信最大可以传输多大的数据
1MB-8KB的限制来源于哪里呢?(PS:8k是两个pagesize,一个pagesize是申请物理内存的最小单元)
#defineBINDER_VM_SIZE ((1 * 1024 * 1024) - sysconf(_SC_PAGE_SIZE) * 2)//这里的限制是1MB-4KB*2
ProcessState::ProcessState(constchar*driver)
{
if(mDriverFD>=0){
// mmap the binder, providing a chunk of virtual address space to receive transactions.
// 调用mmap接口向Binder驱动中申请内核空间的内存
mVMStart=mmap(0,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();
}
}
}
virtualvoidonZygoteInit()
{
sp<ProcessState>proc=ProcessState::self();
ALOGV("App process: starting thread pool.\n");
proc->startThreadPool();
}