一、进程通信
1、进程隔离:
进程隔离是为保护操作系统中进程互不干扰而设计的一组不同硬件和软件的技术。进程数据不共享,进程A的虚拟地址和进程B的虚拟地址不同,这样就防止进程A将数据信息写入进程B,保证了数据的安全性。
- 进程空间分为内核空间和用户空间,内核空间(Kernel)是系统内核运行的空间。用户空间(User Space)是用户程序运行的空间,他们之间是隔离的。
- 内核有访问的所有权限,用户空间也可以通过系统接口去访问内核空间。用户空间可以通过内核空间(类似于中介者)进行相互访问。
2、Linux进程间IPC方式:
- 管道
- 消息队列
- 共享内存
- 套接字
- 信号量
- 信号
Linux 下的传统 IPC 通信原理:
传输过程:
内存缓存区 --> 内核缓存区 --> 内存缓存区,需要 2 次数据拷贝;
3、Binder机制的特点
(1)传输性能
- Binder数据拷贝只需要一次,而消息队列、管道、Socket等需要两次,共享内存一次拷贝都不需要。Binder性能仅次于共享内存。
(2)稳定性
- Binder基于C/S架构,Server端和Client端相对独立,稳定性好。
- 共享内存没有Server端和Client端的区分,可能存在同步死锁等问题。Binder稳定性优于共享内存。
(3)安全性
- 传统的Linux通信方式无法获取对方进程的UID,所以访问接入点是开放的,不安全。
- Android为每个应用程序分配了自己的UID,作为自身的标识。Binder的方式可以通过UID建立私有通道,Binder的安全性更高。
二、Binder机制实现原理
1、内存映射
Binder IPC正是基于内存映射(mmap)来实现的
Binder通信过程:
- 首先 Binder 驱动在内核空间创建一个数据接收缓存区;
- 接着在内核空间开辟一块内核缓存区,建立内核缓存区和内核中数据接收缓存区之间的映射关系,以及内核中数据接收缓存区和接收进程用户空间地址的映射关系;
- 发送方进程通过系统调用 copy_from_user() 将数据 copy 到内核中的内核缓存区,由于内核缓存区和接收进程的用户空间存在内存映射,因此也就相当于把数据发送到了接收进程的用户空间,这样便完成了一次进程间的通信。
2、Binder通信模型
- 包括 Client、Server、ServiceManager、Binder 驱动。
- 其中 Client、Server、Service Manager 运行在用户空间,Binder 驱动运行在内核空间。
- 对于Client,Binder是Server本地对象的一个引用,这个引用实际上是一个代理对象,Client通过这个代理对象来间接访问Server的本地对象。
- 对于Server,Binder是提供具体实现的本地对象,需向ServiceManager注册。
- Binder驱动是连接Client来Server的桥梁,负责将代理对象转化为本地对象,并将Server的执行结果返回给Client。
- ServiceManager它保存了Server Binder字符名称和Binder引用的映射,Client通过它来找到Server的Binder引用。
3、Binder通信过程:
- 一个进程使用 BINDERSETCONTEXT_MGR 命令通过 Binder 驱动将自己注册成为 ServiceManager;
- Server 通过驱动向 ServiceManager 中注册 Binder,表明可以对外提供服务。驱动为这个 Binder 创建位于内核中的实体节点以及 ServiceManager 对实体的引用,将名字以及新建的引用打包传给 ServiceManager,ServiceManger 将其填入查找表。
- Client 通过名字,在 Binder 驱动的帮助下从 ServiceManager 中获取到对 Binder 实体的引用,通过这个引用就能实现和 Server 进程的通信。
三、AIDL
1、AIDL使用的基本步骤
(1)生成AIDL接口(new->AIDL->AIDL File)
interface MyWindowManager {
void sysout();
}
生成AIDL文件之后,比起以前多了一个叫做 aidl 的包,而且他的层级是和 java 包相同的。
(2)编译MyWindowManager.aidl生成Java文件
public interface MyWindowManager extends android.os.IInterface {
/** Local-side IPC implementation stub class. */
//Stub 继承 Binder, 说明它是一个 Binder 本地对象;实现 IInterface 接口,表明Server可以提供的方法
public static abstract class Stub extends android.os.Binder
implements com.example.myview.binder.MyWindowManager {
private static final java.lang.String DESCRIPTOR = "com.example.myview.binder.MyWindowManager";
/** Construct the stub at attach it to the interface. */
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
public static com.example.myview.binder.MyWindowManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.example.myview.binder.MyWindowManager))) {
return ((com.example.myview.binder.MyWindowManager) iin);
}
return new com.example.myview.binder.MyWindowManager.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
throws android.os.RemoteException {
......
return super.onTransact(code, data, reply, flags);
}
//Binder本地代理对象
private static class Proxy implements com.example.myview.binder.MyWindowManager {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public void sysout() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_sysout, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_sysout = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public void sysout() throws android.os.RemoteException;
}
- MyWindowManager继承了IInterface,是Client和Server通信的接口
- Stub为静态抽象内部类,继承了Binder。其子类需要实现MyWindowManager接口,是Server的Binder的本地对象
- Stub.Proxy为静态内部类,内部包含了IBinder对象,是Server在Client中的本地代理对象,将参数序列化后交给mRemote处理,实现了跟远程Stub的通信
- asInterface方法通常是Client在bindService成功后,由Client来调用的,作用是将绑定成功后返回的IBinder对象转换为具体的IInterface接口。Client拿到这个IInterface接口后跟Server进行通信
(3)Server端提供方法的具体实现
public class MyWindowManagerService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mWindowManager;
}
private final MyWindowManager.Stub mWindowManager = new MyWindowManager.Stub() {
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble,
String aString) throws RemoteException {
}
@Override
public void sysout() throws RemoteException {
Log.e("hj", "sysout: " );
}
};
}
(4)其他进程的Activity实现跟Service的通信
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(this, MyWindowManagerService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
MyWindowManager windowManager = MyWindowManager.Stub.asInterface(service);
try {
windowManager.sysout();
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
}
2、IBinder/IInterface/Binder/BinderProxy/Stub
- IBinder : IBinder 是一个接口,代表了一种跨进程通信的能力。只要实现了这个借口,这个对象就能跨进程传输。
- IInterface : IInterface 代表的就是 Server 进程对象提供了哪些方法
- Binder : Java 层的 Binder 类,代表的其实就是 Binder 本地对象。Proxy 类是 Binder 类的一个内部类,它代表远程进程的 Binder 对象的本地代理;这两个类都继承自 IBinder, 因而都具有跨进程传输的能力;实际上,在跨越进程的时候,Binder 驱动会自动完成这两个对象的转换。
- Stub : AIDL 的时候,编译工具会给我们生成一个名为 Stub 的静态内部类;这个类继承了 Binder, 说明它是一个 Binder 本地对象,它实现了 IInterface 接口,表明它具有 Server 承诺给 Client 的能力;Stub 是一个抽象类,具体的 IInterface 的相关实现需要自己实现。
参考文章:
Android Binder设计与实现 - 设计篇
为什么 Android 要采用 Binder 作为 IPC 机制?
写给 Android 应用工程师的 Binder 原理剖析