Android 图解IPC机制,Binder机制

99 阅读6分钟

1.1 普通跨进程二次拷贝的过程:

数据从用户空间拷贝到内核空间:第一次拷贝:copy_from_user()

数据从内核空间拷贝到用户空间:第二拷贝:copy_to_user()

这种传统的 IPC 通信方式有两个问题:

  1. 性能低下,一次数据传递需要经历:内存缓存区 --> 内核缓存区 --> 内存缓存区,需要 2 次数据拷贝;
  2. 接收数据的缓存区由数据接收进程提供,但是接收进程并不知道需要多大的空间来存放将要传递过来的数据,

因此只能开辟尽可能大的内存空间或者先调用 API 接收消息头来获取消息体的大小,这两种做法不是浪费空间就是浪费时间。

1.2 一次拷贝的过程:

Binder IPC正是基于内存映射(mmap)来实现的

**内存映射:**Binder IPC 机制中涉及到的内存映射通过mmap() 来实现,mmap() 是操作系统中一种内存映射的方法。内存映射简单的讲就是将用户空间的一块内存区域映

射到内核空间。映射关系建立后,用户对这块内存区域的修改可以直接反应到内核空间;反之内核空间对这段区域的修改也能直接反应到用户空间。

1). 首先Binder 驱动在内核空间创建一个数据接收缓存区;

2). 接着在内核空间开辟一块内核缓存区,建立内核缓存区和内核中数据接收

缓存区之间的映射关系,以及内核中数据接收缓存区和接收进程用户空间地址的映射关系;(数据缓存区-------->数据接收缓存区)

3). 发送方进程通过系统调用copyfromuser() 将数据copy 到内核中的内核缓存区,由于内核缓存区和接收进程的用户空间存在内存映射,因此也

就相当于把数据发送到了接收进程的用户空间,这样便完成了一次进程间的通信

2. AIDL角度讲解4者的关系和binder机制

详细解读:ServiceManager、Binder Client、Binder Server 处于不同的进程,他们三个都在用户空间,而Binder 驱动在内核空间。

总结4者的关系 :

1). 注册到serverManager : server会把自己注册到serverManager(通过binder驱动):

2). 获取Server实体: Client 进程向ServiceManager 查询Server 进程(通过binder驱动)Binder 实体的引用

3). 服务端代理: Server进程中的真实对象转换成代理对象Proxy,返回这个代理对象给Client 进程

4). Client进程拿到了这个代理对象Proxy,然后调用这个代理对象的方法(通过binder驱动)

3. 从native层完全讲解binder机制

1). servermanager注册, 成为 Binder 驱动管理者

2). servermanager进入循环等待其他service注册

3). 找到serverManager, 通过binder驱动 (在binder驱动中),通过handler值=0找到serverManager

4). server通过binder驱动注册到servermanager, 通过bp binder, 打开驱动, 得到了servermanager就可以添加 , 同时把handler, name, 写入binder驱动,

5). client请求aidl连接: 通过servermanager查询, 拿到服务代理, 返回给客户端

6). BinderProxy 将我们的请求参数发送给 ServiceManager, 通过共享内存的方式使用内核方法 copy_from_user() 将我们的参数先拷贝到内核空间,这时我们的客户端进入等待状态,

7).内存映射: 服务端拿到client的数据, 执行完之后把执行结果通过 copy_to_user() 将内核的结果拷贝到用户空间(这里只是执行了拷贝命令,并没有拷贝数据,binder只进行一次拷贝),唤醒等待的客户端并把结果响应回来

4. 代理模式和AIDL原理

4.1 核心类

1). IInterface , IInterface 代表的就是 Server 进程对象具备什么样的能力

public interface IInterface
{
   public IBinder asBinder();
}

2) .IBinder, IBinder 是一个接口,代表了一种跨进程通信的能力。只要实现了这个借口,这个对象就能跨进程传输。

public class Binder implements IBinder {
在这里,如果链接成功返回回来的。
private val mConnection: ServiceConnection = object : ServiceConnection {
    override fun onServiceConnected(name: ComponentName, service: IBinder) {
        val imageManager: IImageManager = IImageManager.Stub.asInterface(service)

3).Binder Java 层的 Binder 类,代表的其实就是 Binder 本地对象

4).stub: 自动生成的, 而这个类最核心的成员是 Stub类 和 Stub的内部代理类Proxy。

这个类继承了 Binder,它实现了 IInterface 接口!

Stub充当服务端角色,持有Binder实体(本地对象)。远程进程执行onTransact()函数

  • 获取客户端传过来的数据,根据方法 ID 执行相应操作。
  • 将传过来的数据取出来,调用本地写好的对应方法。
  • 将需要回传的数据写入 reply 流,传回客户端。
4.2 具体的案例
public interface BookManager extends IInterface {

    List<Book> getBooks() throws RemoteException;

    void addBook(Book book) throws RemoteException;
}


public abstract class Stub extends Binder implements BookManager {

    private static final String DESCRIPTOR = "com.baronzhang.ipc.server.BookManager";

    public Stub() {
        this.attachInterface(this, DESCRIPTOR);
    }

    public static BookManager asInterface(IBinder binder) {
        if (binder == null)
            return null;
        IInterface iin = binder.queryLocalInterface(DESCRIPTOR);
        if (iin != null && iin instanceof BookManager)
            return (BookManager) iin;
        return new Proxy(binder);
    }

    @Override
    public IBinder asBinder() {
        return this;
    }

    @Override
    protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        switch (code) {

            case INTERFACE_TRANSACTION:
                reply.writeString(DESCRIPTOR);
                return true;

            case TRANSAVTION_getBooks:
                data.enforceInterface(DESCRIPTOR);
                List<Book> result = this.getBooks();
                reply.writeNoException();
                reply.writeTypedList(result);
                return true;

            case TRANSAVTION_addBook:
                data.enforceInterface(DESCRIPTOR);
                Book arg0 = null;
                if (data.readInt() != 0) {
                    arg0 = Book.CREATOR.createFromParcel(data);
                }
                this.addBook(arg0);
                reply.writeNoException();
                return true;

        }
        return super.onTransact(code, data, reply, flags);
    }

    
    public class Proxy implements BookManager {

    private static final String DESCRIPTOR = "com.baronzhang.ipc.server.BookManager";

    private IBinder remote;

    public Proxy(IBinder remote) {

        this.remote = remote;
    }

    public String getInterfaceDescriptor() {
        return DESCRIPTOR;
    }

    @Override
    public List<Book> getBooks() throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel replay = Parcel.obtain();
        List<Book> result;

        try {
            data.writeInterfaceToken(DESCRIPTOR);
            remote.transact(Stub.TRANSAVTION_getBooks, data, replay, 0);
            replay.readException();
            result = replay.createTypedArrayList(Book.CREATOR);
        } finally {
            replay.recycle();
            data.recycle();
        }
        return result;
    }

    @Override
    public void addBook(Book book) throws RemoteException {

        Parcel data = Parcel.obtain();
        Parcel replay = Parcel.obtain();

        try {
            data.writeInterfaceToken(DESCRIPTOR);
            if (book != null) {
                data.writeInt(1);
                book.writeToParcel(data, 0);
            } else {
                data.writeInt(0);
            }
            remote.transact(Stub.TRANSAVTION_addBook, data, replay, 0);
            replay.readException();
        } finally {
            replay.recycle();
            data.recycle();
        }
    }

    @Override
    public IBinder asBinder() {
        return remote;
    }
}

流程总结:

1.在 Proxy 中的 addBook() 方法中首先通过 Parcel 将数据序列化,然后调用 remote.transact()

  1. Client 进程通过系统调用陷入内核态,Client 进程中执行 addBook(), 里面 transact()的线程挂起等待返回;

  2. 驱动完成一系列的操作之后唤醒 Server 进程,调用 Server 进程本地对象的 onTransact()。最终又走到了 Stub 中的 onTransact() 中

  3. onTransact() 根据函数编号调用相关函数(在 Stub 类中为 BookManager 接口中的每个函数中定义了一个编号,只不过上面的源码中我们简化掉了;在跨进程调用的时候,不会传递函数而是传递编号来指明要调用哪个函数

onTransact()代表回调, transact()代表主动!

5. 跨进程方式优缺点比较

👀关注公众号:Android老皮!!!欢迎大家来找我探讨交流👀