ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

binder学习

2021-05-15 22:03:01  阅读:153  来源: 互联网

标签:调用 SM BpBinder 学习 binder sm data


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通过将一个虚拟内存区域与磁盘上的一个对象关联起来,以初始化这块虚拟内存的区域的内容,这个过程称之为内存映射。

先上一张结构图:
在这里插入图片描述
客户端的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机制大概需要理解下面这两张图
在这里插入图片描述
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驱动互动来实现读写。

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

在这里插入图片描述
上面从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;

在这里插入图片描述
在你的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的认知暂时就介绍到这里了。

标签:调用,SM,BpBinder,学习,binder,sm,data
来源: https://blog.csdn.net/qq_34606099/article/details/116765726

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有