binder学习

binder是什么?

进程间通信机制
是一个驱动
Binder.java–>实现了Ibinder–夸进程能力

linux进程间通讯有哪些?

管道、socket、信号量、共享内存…

共享内存逻辑控制太复杂,易用性差,通信的时候直接读取,不同拷贝

传统IPC例如socket需要拷贝两次,基于c/s架构,传输效率低,开销大

上面两种都是基于上层协议,访问接入点是开放的,不安全。

Binder,只需拷贝一次,基于c/s架构,易用性高,为每个APP分配UID,同时支持匿名和实名。

进程间是怎么通讯的?

进程A想调用进程B的方法?

内存划分:内存被操作系统划分为两块,用户空间和内核空间,用户空间是用户程序代码运行的地方,内核空间是内核代码运行的地方。
为了安全,它们之间是隔离的,即使用户的程序崩溃了,内核也不受影响。

上面AB两个进程间的通信,两次拷贝就是把A的物理内存拷贝从A的用户空间拷贝到内核空间的数据缓存区,再拷贝到B的用户空间。

而Binder的一次拷贝实际上是通过mmap这个技术把A的数据从A用户空间拷贝到内核空间,这个地方通过mmap的映射给B,B可以直接调用,所以才说是一次拷贝。

MMAP:linux通过将一个虚拟内存区域与磁盘上的一个对象关联起来,以初始化这块虚拟内存的区域的内容,这个过程称之为内存映射。

先上一张结构图:
binder学习
客户端的BpBinder是服务端BBinder的代理对象,通过服务返回获取到的。

所有的流程都没有贴源码,都是C、C++源码,了解大概的流程原理即可,看不懂前面贴的流程没关系,后面看完java层的例子和总结再回来看看流程基本就能理解七七八八了。

驱动层:

binder_init
1、分配内存
2、初始化设备
3、放入链表binder_devices 驱动列表

binder_open(谁调用就是谁,相当于把调用者的信息存储到列表中)
1、创建binder_proc对象
2、当前进程信息,保存到proc
3、filp->private_data = proc;
4、添加到binder_procs链表中

binder_mmap
vma–>进程的虚拟内存,4m,驱动定的,应用定的是1m-8k,binder传输超过1m就报错,intent就是通过binder去传输的,所以不能超过1m。

流程:
1、通过用户空间的虚拟内存大小–》分配一块内核的虚拟内存,一样大小
2、分配了一块物理内存,4kb,初始值,因为还没有真正工作,都不知道最终需要多大的空间,初始值先分配4kb大小来存放数据。
3、把这块物理内存映射到上面用户和内核的两个虚拟内存,关联起来。

binder_ioctl
读写操作,通过判断对应的case,当read_size>0或者write_size>0,则进行相应的读写操作。

serviceManager–sm 注册
1、打开驱动,内存映射,设置大小(128K)跟一般的系统服务的内存大小不一样
2、设置sm为大管家(设置为守护线程) 作用,为了管理系统服务 调用looper不停地循环检测,默认handler == 0的标志指令就是调用binder
1、创建binder_node结构体
2、proc–>binder_node
3、创建work和todo–》l类似messageQueue
3、BC_ENTER_LOOPER命令 开启for循环
1、写入状态Loop
2、去读数据 ret = wait… (这个时候没有数据)wait等待

sm获取–native获取
获取sm的情况
1、注册服务到sm–native
2、通过sm去获取服务–java

  1. ProcessState::self()->getContextObject(NULL)、
    1. ProcessState::self()
      1. 打开驱动:binder
      2. 设置线程最大数目:15个 不包括主线程
      3. mmap – 设置共享内存大小 — (1M-8K)到这里的时候内存大小改变了,初始128K
    2. getContextObject
      1. 创建一个BpBinder — 客户端的对象
  2. interface_cast
    1. new BpServiceManager(new BpBinder) --》 new Proxy(binder==BinderProxy)
    2. remote.transact -->远程调用
    3. remote == BpBinder
  3. java 层 — ServiceManager.addService
    1. new ServiceManagerProxy(new BinderProxy)
    2. mRemote == BinderProxy
    3. BinderProxy.mObject == BpBinder
    4. mRemote.transact == BpBinder.transact

服务的注册和获取,线程池管理

java–AMS如何注册到SM中

getIserviceManager().addService(name,service,false)

  • getIServiceManager — new ServiceManagerProxy(new BinderProxy())
    • ServiceManagerNative.asInterface(BinderInternal.getContextObject())
      • BinderInternal.getContextObject — 返回 BinderProxy 对象
        • ProcessState::self()->getContextObject:创建一个BpBinder
        • javaObjectForIBinder – BinderProxy 和 BpBinder 互相绑定
      • ServiceManagerNative.asInterface
        • 返回 ServiceManagerProxy
          #NAME?
    • data.writeStrongBinder(service); – service == AMS — 将AMS 放入 data中
    • mRemote.transact — mRemote == BinderProxy
      • 获取BpBinder — IPCThreadState::transact
        • 1.writeTransactionData — out 写入命令 --write — cmd == BC_TRANSACTION
        • 2.waitForResponse
          • talkWithDriver – 源码核心 — 代码非常长
            • binder_transaction
              • handle == 0 --》 sm
                1. target_node
                2. proc
                3. todo,wait
                4. 创建t,tcomplete,
                5. 数据拷贝
                6. binder_transaction_binder --> handle
                7. thread->transaction_stack = t; —> 方便sm找到client
                8. t->work.type = BINDER_WORK_TRANSACTION; – 给sm – 做事
                9. tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE; – 给client–挂起
                10. wake_up_interruptible 唤醒sm
        • client挂起
          • BR_NOOP ,BR_TRANSACTION_COMPLETE
          • wait_event_freezable — 挂起
        • sm处理添加服务
          • BINDER_WORK_TRANSACTION — 要处理 cmd == BR_TRANSACTION
            1. reply初始化
            2. res = func(bs, txn, &msg, &reply); — 函数指针 — svcmgr_handler作用:获取或者添加 service
              1. sm是用 svclist 保存所有服务的
            3. binder_send_reply — bc_reply
            4. t->work.type = BINDER_WORK_TRANSACTION; — 给Client
              list_add_tail(&t->work.entry, target_list);
              tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE; – 给SM — 被挂起
              list_add_tail(&tcomplete->entry, &thread->todo);
            5. wake_up_interruptible(target_wait); – 唤醒 Client
        • client 被唤醒
          • BINDER_WORK_TRANSACTION — cmd = BR_REPLY;

SM 处理 onTransact

  • IPCThreadState::executeCommand

    • error = reinterpret_cast<BBinder*>(tr.cookie)->transact(tr.code, buffer,
      &reply, tr.flags);

    • JavaBBinder.onTransact — C++

    • jboolean res = env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact,
      code, reinterpret_cast(&data), reinterpret_cast(reply), flags); – Binder.java.execTransact 方法

整个binder机制大概需要理解下面这两张图
binder学习
1、客户端拿的是服务端给的代理对象:ServiceManagerProxy,里面包含BinderProxy,又包含BpBinder。
2、BpBinder实际上是服务端BBinder的代理对象,BBinder实际上是注册服务返回的SM。
3、调用是通过SM来进行的,SM有一个默认指令handler == 0即是调用binder,它是binder的大管家,调用的时候会把当前服务添加到SM里面,它维护了一个列表,例如上面距离的AMS,当你在获取AMS的时候不仅实例化了AMS,还会调用addService添加到SM中。
4、SM中主要通过ioctl跟binder驱动互动来实现读写。

binder学习
命令流程:
1、客户端注册完SM的时候SM实际上是wait状态
2、客户端通过发送1的指令给binder驱动,binder接收到指令后产生两条指令2和3。
3、客户端接收到指令2进行挂起,servive接收到指令3进行数据读写,同时被唤醒。
4、service读写玩返回指令4,binder驱动又发出了两条指令。
5、service接收到5进行挂起休眠
6、客户端接收指令6,同时被唤醒,进行指令6的操作读写(可能有返回值)。

binder学习
上面从java层到native到binder驱动层的一个图解。
1、java层的客户端进行通信,是绿色路线,从binderProxy到native的BpBinder到驱动,驱动调用service的native的BBinder指向JavaBinder最终指向java层的Binder。

2、如果是native层自己的通讯,则直接从客户端的BpServiceManager调用BpBinder到驱动层再到Service的BnServiceManager。

名字有点多,看起来也有一点乱,native层就是SM在管理,所有的调用都是最终调用native的SM,然后SM返回给你代理对象,SM再自己调用驱动层。
其实上面就是java层获取SM和和native获取SM的不同的对象来源路劲,很明显,native层获取SM比java层少。

如果是我们自己去实现这种binder的调用流程会比较麻烦,所以android为我们提供了AIDL。

AIDL需要注意一些事项:
定义自己的接口方法,AIDL文件
如果是自定义类,记住实现序列化
下面文件是AIDL

// TestAidl.aidl
package com.example.startservicefromaotherapp;
//即使在同样的包中也需要导包
import com.example.startservicefromaotherapp.Info;

// Declare any non-default types here with import statements

interface TestAidl {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void addInfo(in Info info);
    List<Info> getInfoList();
}
// Info.aidl
package com.example.startservicefromaotherapp;

// Declare any non-default types here with import statements

parcelable Info;

binder学习
在你的main目录下创建一个Folder的AIDL文件,复制包路劲创建一个Package,然后创建你需要的接口,是aidl文件,自定义类需要序列化传输,最后把你的AIDL和info全部在另一端复制,记住路劲要一致。

   @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }

    private IBinder binder = new TestAidl.Stub() {
        @Override
        public void addInfo(Info info) throws RemoteException {

        }

        @Override
        public List<Info> getInfoList() throws RemoteException {
            return null;
        }
    };

上面的addInfo和getInfoList就是我们定义的方法啦,通过这种service的绑定把我们服务端的代理对象给客户端

 @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        if (iBinder == null) {
            iBinder = TestAidl.Stub.asInterface(service);
        }
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        iBinder = null;
    }
}

绑定之后随便用,跟我们寻常的binderService简直不要太相似。
看一下AIDL为什么生成的方法asInterface

public static com.example.startservicefromaotherapp.TestAidl asInterface(android.os.IBinder obj)
    {
      if ((obj==null)) {
        return null;
      }
      //判断是否在同一个进程内,如果是则直接返回普通binder对象
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
      if (((iin!=null)&&(iin instanceof com.example.startservicefromaotherapp.TestAidl))) {
        return ((com.example.startservicefromaotherapp.TestAidl)iin);
      }
      //否则返回Proxy的binder的代理对象,区别就是能跨进程传输,通过native获取到的
      return new com.example.startservicefromaotherapp.TestAidl.Stub.Proxy(obj);
    }

obj就是onServiceConnected方法,服务端返回的binder代理对象,说明Proxy持有了它

 @Override public void addInfo(com.example.startservicefromaotherapp.Info info) throws android.os.RemoteException
      {
      //包装的数据集 data是发送的数据 reply接收返回的数据
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          if ((info!=null)) {
            _data.writeInt(1);
            info.writeToParcel(_data, 0);
          }
          else {
            _data.writeInt(0);
          }
          //transact会挂起,最终调用到onTransact
          boolean _status = mRemote.transact(Stub.TRANSACTION_addInfo, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
            getDefaultImpl().addInfo(info);
            return;
          }
          _reply.readException();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
      }

这里的互相调用其实就是上面说的指令命令,AIDL其实自动我们做了一些工作也不复杂,我们完全可以自己书写,具体有兴趣了解的朋友可以自己写一个简单AIDL查看源码,代码并不多。对于binder的认知暂时就介绍到这里了。

上一篇:我阿里P7了解到的Android面试的一些小内幕!知乎上转疯了!


下一篇:Android-Binder机制及AIDL使用,进阶学习资料!