Android IPC --Binder之架构篇(二)

上一篇文章中,我们通过对比Linux进程间的通信方式,知道Android 中使用Binder进行进程间通信的好处。这篇文章我们就来看看Binder在Android中的架构设计。

Binder 是什么?

image.png Android 系统为了安全、稳定性、内存管理等原因,Android 应用和系统服务都是运行在独立的进程中的,但系统服务与应用进程之间,应用进程A与应用进程B之间需要通信和数据共享的。因此,Android 系统需要提供一套能够高效、安全的跨进程通信方案。于是Binder 应运而生。

  • 安全:每一个应用都是运行在单独的进程中的,系统为每一个进程分配了唯一的id 标识---UID
  • 稳定:真是因为每个app都运行在单独的进程中,因此某个APP发生错误,或者崩溃,不会对其他APP产生任何影响。
  • 内存管理:一个进程销毁了,将会从内存中移除该进程所占用的资源,并回收为其分配的内存。

注:实际上,单个app 中的不同组件(这里就举Activity、Service)可以运行在不同的进程中的。

image.png

Android 中不同的app是运行在不同的进程之中的。上图展示了应用与应用之间跨进程通信(IPC),应用层与FrameWork 层跨进程通信(IPC),FrameWork 与Native 层、应用层和Native层跨进程通信(IPC).这张图中的大部分场景下使用的都是Binder通信。由此可见,在Android系统中,Binder是何等的重要。

小结

Binder 就是Android中提供的一套高效、安全的跨进程通信框架。

Binder结构设计

image.png 如上图,分五层:

  • 应用层:开发者所要处理的客户端和服务端的逻辑
  • AIDL: 这是有aidl.exe文件自动帮我们把我们自己定义的AIDL文件转成Java文件.里面有两个内部类Proxy和Stub. 分别代表发送数据方和接受数据方.
  • FrameWork层: AIDL 层生成的Proxy 类中,将会调用FrameWork 层中的IBinder 中的方法, IBinder 在该层的实现类为BinderProxy, 因此会调用BinderProxy 中的方法. 在数据接收方,将是Binder类来处理,
  • Native 层:发送方是BpBinder, 接收方是BBinder.
  • 内核层: 就是Binder驱动.本文不做讲解

本文重点是要大家理清整体的流程.不做过多的细节分析.

image.png

Binder 通信

  1. 客户端想要与服务端建立链接通信

image.png

2.由于两个进程是无法直接进行通信的(读写数据),但是不同的应用可以通过内核传递数据来实现交互,因此可以使用Binder 驱动.

image.png Binder驱动通过/dev/binder提供了相关的open,release,poll,mmap,flush以及ioctl操作的api. 通过这些api 就可以实现进程间通信了。事实上,在进程中的大部分通信都是通过iotcl (binderFd, BINDER_WRITE_READ, &bwd)来进行的。 bwd 的定义如下:

struct binder_write_read {  
  signed long write_size;/* bytes to write */ 
  signed long write_consumed; /* bytes consumed by driver */  
  unsigned long write_buffer; 
  signed long read_size;  /* bytes to read */ 
  signed long read_consumed;  /* bytes consumed by driver */  
  unsigned long read_buffer;
};
复制代码

write_buffer 包含一系列的对驱动而言的指令

  • Book-keeping commands, e.g. inc/decrement binder object references, request/clear death notification, etc(增加或者减少binder对象引用,请求或者清除死亡通知等)不知道翻译的对不对。
  • 需要响应的指令,如BC_TRANSACTION 指令。

对应的,read_buffer 包含一系列对用户空间的指令

  • 请求处理响应的命令(即 BC_REPLY)或执行 嵌套(递归)操作

数据发送方和数据接收方并不想了解Binder驱动的协议等操作,于是,Android就使用了ProxyStub, 分别对应发送方接收方

image.png 这样做的优势是client 和service 都可以不直接与Binder打交道;但是聪明机制的Android设计者们更考虑到可能client端完全不需要知道Binder的存在,于是,Android系统提供了一个Manager来管理clients.这样Client只需要与Manager打交道就好了,其他的事不用操心,Android设计者真是为我们开发者操碎了心哈哈哈。

image.png 注:这个对于系统服务来说尤其如此,这些服务通常只将其API的一个子集暴漏给他们的Managers, 例如Activity 是通过ActivityManager来管理的,Window 是通过WindowManager 来处理的等等。 而这些XXManager 又通过Binder获取XXManagerService 来控制这些Actvitity,Window 的。 但是客户端如何获取与之通信的服务句柄呢?(也就是客户端怎么获取到这些ManagerService)。接下来我们讲解一下Binder 通信之 服务注册、服务发现、服务调用。这里就用到了servicemanager 来统一管理这些ManagerService 的。

image.png servicemanager:出于安全的原因,binder驱动只能接受一个servicemanager存在,因此,这个servicemanger 进程就需要在Android系统开启的时候就要开启。

可以通过adb shell ps 来验证一下:

image.png

image.png

image.png

image.png

由上面几张图可以得出一个结论,Android 系统的第一个启动的进程就是init 进程(PID=1);servicemanager 是由init进程启动的(PPID=1,也即父进程的PID=1, 就是init进程);zygote64 都是由init进程开启的,zygote64 的进程号为PID=1021; 查看system_server 进程父进程号为1021,也就是zygote64. 由此我们可以得出下图

image.png 看过 《Android进阶解密》这本书的第二章就讲解了这部分内容,我们通过adb shell ps 也可以验证书中所说,这部分我后期会写一篇博客专门介绍。

image.png 通过adb shell service list 可以看到我的手机中开启了259个系统服务注册在servicemanager 中。

image.png

服务注册

第一步: service通过调用serviceManager中的addService方法,然后调用ServiceManagerNative类中的addservice(name)方法。

第二步: ServiceManagerNative会通过Binder发送一条SVG_MGR_ADD_SERVICE的指令,然后通过svcmgr_handler()调用do_add_service()方法往svc_list中添加相应的service。

重点:所有的服务都要先注册到svc_list中才能被client调用到。svc_list以linkedlist的形式保存这些服务。

服务获取

第一步:client要请求服务,比如说在activity中调用context.getSystemService()方法,这个时候serviceManager就会使用getService(name),然后就会调用到native层中的ServiceManagerNative类中的getService(name)方法。

第二步:ServiceManagerNative会通过Binder发送一条SVG_MGR_GET_SERVICE的指令,然后通过svcmgr_handler()调用do_find_service()方法去svc_list中查找到相关的service。

第三步:查找到相应的服务后就会通过Binder将服务传给ServiceManagerNative,然后传给serviceManager,最后client就可以使用了。

注意: 服务实在svclist中保存的,svclist是一个链表,因此客户端调用的服务必须要先注册到svclist中。

服务调用

先通过mContext.getSystemService() 获取到服务,然后就可以调用其服务的方法了。

NotificationManager manager = mContext.getSystemService(NotificationManager.class);
ensureNotificationChannelExists(manager, DYNAMIC_MODE_NOTIF_CHANNEL_ID,
        R.string.dynamic_mode_notification_channel_name);

manager.notifyAsUser(TAG, DYNAMIC_MODE_NOTIFICATION_ID,
        buildNotification(DYNAMIC_MODE_NOTIF_CHANNEL_ID,
                R.string.dynamic_mode_notification_title,
                R.string.dynamic_mode_notification_summary,
                Intent.ACTION_POWER_USAGE_SUMMARY),
        UserHandle.ALL);
复制代码

咱们再看下图,这个图比较接近于我们客户端编码层面的流程了。至于这块的内容,我们在后续的文章中将详细讲解。先我们知道个整体的流程即可。 image.png

小结

Binder实现跨进程通信,看过上篇文章应该有所了解,主要的是mmap 内存映射,实现了只需一次拷贝(数据发送方通过copy_from_user() 将数据拷贝到内核进程中)就可以将数据从发送方拷贝到接收方。 难点在于,实现这个Binder通信的框架,怎么管理Binder的通信,客户端与服务端怎么样实现跨进程通信。三步走策略:服务注册、服务发现、服务调用。

下一篇文章我将从Android Framework 源码层面分析一下,systemserver 进程中开启的ActivityManagerService、WindonManagerService等几十个服务是怎么与serviceManager进程勾搭上的。

本文如有错误的地方,欢迎大家留言讨论,也再次感谢下面几篇文章作者。如果大家觉得这篇文章对你有所帮助,麻烦你点个赞&加个关注。 参考文献

(29条消息) 图解Android中的Binder机制_HankingHu的博客-CSDN博客_android binder

events.static.linuxfound.org/images/stor…

Andevcon-Binder (newandroidbook.com)

Guess you like

Origin juejin.im/post/7076249415672922142
IPC