Android消息机制和Binder机制
Android通信方式
本文参考于:Android 操作系统架构开篇
Android的IPC方式(进程间通信)和Linux类似,只是多了Binder IPC机制。
Android系统中的Zygote进程的IPC采用的是Socket机制。
在上层system
server和media server以及上层App之间更多的是采用Binder
IPC方式来完成跨进程间的通信。
对于Android上层架构中,很多时候是在同一个进程的线程之间需要相互通信,例如同一个进程的主线程与工作线程之间的通信,往往采用的Handler消息机制。
Socket
Socket通信方式是C/S架构,Android中采用此方式主要有:
- zygote 用于孵化进程,system_server创建进程是通过socket向zygote进程发起请求
- installd 用于安装App的守护进程,上层PackageManagerService很多实现最终都是交给它来完成
- lmkd lowmemorykiller的守护进程,Java层的LowMemoryKiller最终都是由lmkd来完成
- adbd 用于服务adb
- logcatd 用于服务logcat
- vold 即volume Daemon,是存储类的守护进程,用于负责如USB、Sdcard等存储设备的事件处理
Socket方式更多的用于Android Framework层与Native层之间的通信。Socket通信方式相对于Binder比较简单,这里省略。
Handler
Binder/Socket用于进程间通信,而Handler消息机制用于同进程的线程间通信。
Handler消息机制是由一组MessageQueue、Message、Looper、Handler共同组成的。
framework层
参考:Handler的Java层
消息机制主要包含:
- Message:消息分为硬件产生和软件产生的
- MessageQueue:消息队列,主要功能是向消息池投递消息和取走消息
- Handler:消息处理器,主要功能是向消息池发送和处理消息事件
- Looper:消息循环器,按分发机制将消息分发给目标处理者
- Handler通过sendMessage()发送Message到MessageQueue
- Looper通过loop()不断取出达到触发条件的Message并交给对应的target来处理
- 经过dispatchMessage()后,交回给target Handler的handleMessage()来进行相应地处理
- 在enqueueMessage()中可能会唤醒loop线程,而在MessageQueue的next()函数里调用的nativePollOnce()可能会阻塞之
native层
注:虚线表示关联关系,实线表示调用关系。
在这些类中MessageQueue是Java层与C++层维系的桥梁,MessageQueue与Looper相关功能都通过MessageQueue的Native方法来完成,而其他虚线连接的类只有关联关系,并没有直接调用的关系(它们发生关联的桥梁是MessageQueue)。
NativeMessageQueue构造方法会设置Looper对象,而Looper的构造方法会创建管道和epoll实例以进行监听,这保证了loop循环不会让线程卡死。
消息队列是由MessageQueue类来描述的,MessageQueue是Android消息机制Java层和C++层的纽带,其中很多核心方法都交由native方法实现。
消息循环是建立在Looper之上的,Looper对象存储在线程的TLS区,Looper可以为线程添加一个消息循环的功能,通常只要继承自Thread类并添加Handler成员且在run函数中实例化并提供handleMessage函数的重载以供应用程序的消息处理。
Handler实战
这里略过。
Binder
参考:Binder系列剖析, Android进程间通信机制Binder
IPC通过系统调用陷入内核态,内核空间开辟一块可共享的内存缓冲区以进行数据传输然後返回用户态。
Binder通信采用C/S架构,包含Client,Server,ServiceManager以及Binder驱动,其中ServiceManager用于管理系统中的各种服务。如图:
虚线代表不是直接交互而是通过Binder驱动间接交互。
- 注册服务(addService):Server进程向ServiceManager注册
- 获取服务(getService):Client进程向ServiceManager中获取服务
- 使用服务:建立通路,Client进程与Service交互
Binder机制的意义:
- 高性能,一次拷贝
- 稳定性,基于C/S架构
- 安全性,权限控制
Binder内存机制:
Binder driver
Binder驱动通过Linux的动态内核可加载模块来实现。
binder核心函数
Binder
Driver初探
Binder驱动以misc设备进行注册,作为虚拟字符设备,没有直接操作硬件,只是对设备内存的处理:
内存映射mmap:文件或者其它对象及虚拟内存和物理地址之间的映射。
ioctl:是一种专用于设备输入输出的系统调用,该调用传入一个跟设备功能有关的请求码。
binder通信协议
Binder协议包含在IPC数据中,分为两类:
- BINDER_COMMAND_PROTOCOL:
Binder请求码,以
"BC_"开头,简称BC码,用于从IPC层传递到Binder Driver层 - BINDER_RETURN_PROTOCOL:
Binder响应码,以
"BR_"开头,简称BR码,用于从Binder Driver层传递到IPC层
Binder IPC通信至少是两个进程的交互:
- Client进程执行binder_thread_write,根据BC_XXX命令,生成相应的binder_work
- Server进程执行binder_thread_read,根据binder_work.type类型,生成BR_XXX,发送到用户空间处理
Service Manager
ServiceManager是Binder IPC通信过程中的守护进程,集中管理系统内的所有服务,通过权限控制进程是否有权注册服务,通过字符串名称来查找对应的Service;由于ServiceManger进程与所有向其注册的服务建立死亡通知,每个Client通过查询ServiceManager可获取Server进程的情况。
启动过程
- binder_open(): 打开binder驱动,并调用mmap()方法分配内存映射空间
- binder_become_context_manager(): 通知binder驱动使其成为守护进程
- 验证selinux权限,判断进程是否有权注册或查看指定服务
- binder_loop(): 进入循环状态,等待Client端的请求
ServiceManager最核心的两个功能:
- 注册服务:记录服务名和handle信息,保存到svclist列表
- 查询服务:根据服务名查询相应的的handle信息
获取ServiceManager
讲解略过。
注册与获取服务
在Native层的服务以media服务为例简要讲解。
media服务注册
过程说明:
- 获取ServiceManager
- 注册多媒体服务
- 启动Binder线程池并将当前线程加入
服务注册(addService)核心功能:在服务所在进程创建binder_node,在servicemanager进程创建binder_ref,其中binder_ref的desc在同一个进程内是唯一的。
- 每个进程binder_proc所记录的binder_ref的handle值是从1开始递增的
- 所有进程binder_proc所记录的handle=0的binder_ref都指向ServiceManager
- 同一个服务的binder_node在不同进程的binder_ref的handle值可以不同
Media服务注册的过程涉及到MediaPlayerService(作为Client进程)和ServiceManager(作为Service进程),通信流程图如下所示:
获取Media服务
请求服务(getService)过程,就是向ServiceManager进程查询指定服务,当执行binder_transaction()时,会区分请求服务所属进程情况。
服务获取可以使用两种方式:
- BpServiceManager::getService()
如果服务不存在则会阻塞数秒 - BpServiceManager::checkService()
检索指定服务是否存在,不会阻塞
具体查看:获取服务(getService)
framework层分析
Binder在framework层,采用JNI技术来调用native(C/C++)层的Binder架构,从而为上层应用程序提供服务。
framework Binder架构
framework层的核心逻辑都是交予native层来处理。
startService中的Binder
彻底理解Android
Binder通信架构
Binder架构采用类似TCP/IP的分层架构设计:
Binder的使用
此处略过,详情参考:如何使用Binder
Binder线程池
Binder线程池工作过程
进程由Zygote的fork()创建完成后,便会在新进程中执行onZygoteInit()的过程中,启动Binder线程池。只有第一个Binder主线程是由应用程序主动创建,Binder线程池的普通线程都是由Binder驱动根据IPC通信需求创建,Binder线程的创建流程图如下: