[RK3399][Android7.1]通信机制 Binder
本博客首先介绍了进程间的通信机制 Binder
,其次介绍了 JNI
机制以及传感器模 块实现中是如何使用的,最后以数据传递为主线分析了各模块的设计与实现。
平台 | 内核版本 | 安卓版本 |
---|---|---|
RK3399 | Linux4.4 | Android7.1 |
文章目录
1、进程间通信机制
Binder
作为 Android
中另外一个庞大的体系,虽然代码量多、跨度广,但也 同样需要自己的地基——Binder
驱动。它以服务端客/服端的模式运行,其中,提供 服务的一方为 server
端,与 server
端进行交互的一端为客户端。
Android
系统是建立在 Linux
的基础上,所以 Binder
驱动也应该是一个标准的 驱动。Binder
驱动是简单的字符设备驱动,并创建/dev/binder
的节点供上层使用。 值得一提的是 Binder
驱动不对应具体的硬件设备。Binder
驱动为上层提供 open
, mmap
等操作。
Binder
的函数操作的结构体如下:
static const file_operations binder_fops={
.own = THIS_MODULE,
.poll = binder_poll,
.unlocked_ioctl = binder_ioctl,
.compat_ioctl=binder_ioctl,
.mmap = binder_mmap,
.open = binder_open,
.flush = binder_flush,
.release = binder_release;
};
当打开 binder
设备时,由 binder_fops
结构体可知将会调用 binder_open
函数。 由于每个进程都有一个 binder_proc
的结构体,该结构记录了内存信息和 binder
节点的相关信息。接下来对新生成的 proc
对象进行各种初始化操作,有一个全局的结构 体记录所有的 binder_proc
变量,binder_proc
也会被记录在 open
函数的 file
结构体 的 private_data
变量中,下次调用 mmap
或者 ioctl
函数时可以直接在通过private_data
变量得到。
当打开设备后,还需要进行 binder_mmap
操作。Binder
驱动中使用 mmap
函数 可以减少数据copy
的次数。当两个进程 A
和 B
,其中 B
进程通过 open
和 mmap
后 与 binder
驱动建立联系。对于应用程序,调用 mmap
就会返回一个虚拟内存地址, 这个地址通过虚拟内存转换后就可以转换成物理地址。同时 binder
驱动中也申请一 段虚拟地址,将这段地址映射为与应用程序指向同一块物理地址。此时,如果进程 A
复制一段数据到 Binder
驱动指针(binder_proc->buffer
)指向的虚拟地址。该数据 可以被 B
直接访问,从而减少了一次数据复制。
当用户空间调用 mmap
函数时,需要制 mmap
空间的大小,系统会将这个地址 封装在结构体 struct vm_area_struct *vma
变量中,该结构体记录了系统分配给应用程序的虚拟内存,其中 vma
的 vm_start
和 vm_end
分别是这块连续的虚拟内存的起 止点。mmap
函数中会判断应用程序申请的内存大小是否大于 4M
空间。当申请的 内存超过 4M
时,mmap
函数没有直接退出,而是只分配 4M
的内存。proc
的 buffer
变量用于记录内核中获取的虚拟内存空间,内核通过 get_vm_area
获取。但是此时 不会分配真正的内存,为了便于数据的复制,将会记录该地址与用户空间地址的偏 移量。最后在 binder_update_page_range
函数中,将用户空间地址,内核地址和物理 内存关联起来。函数接口如下所示:
int binder_update_page_range(struct binder_proc *proc,int allocate, void* start,void *end, struct vm_area_struct *vma)
其中,proc
代表申请内存的进程所持有的 binder proc
对象,start
代表 get_vma_area
返回内存地址的起点,end
代表 get_vma_area
返回地址内存的终点, vma
代表应用程序中虚拟内存段的描述。从 mmap
函数中可以看出,最多可以分 配 4M
的虚拟内存。但是在本函数中只分配了一页物理内存,待物理页不够时,再向系统申请一页,这是出于节省空间的考虑。最后将用户空间地址,内核地址和 物理内存关联起来。在 binder_proc
中管理着三条链表,list_head buffers
所有内存块 需 要 在 这 里 备 案 , rb_root free_buffers
所 有 可 用 的 空 闲 内 存 , rb_root allocated_buffers
所有已经分配了的内存。在 binder_mmap
函数中对分配到的内存进 行管理,主要工作是将其加入相应的链表。
命令 | 说明 |
---|---|
BINDER_WRITE_READ |
读写操作,可以用此命令向 Binder 读写或者 写入数据 |
BINDER_SET_MAX_THREADS |
设置最大的线程数。因为客户端可以并发向 服务器端发送请求,如果当前线程的数量超 过阈值将会停止创建线程 |
BINDER_SET_CONTEXT_MGR |
Service Manager 通过传递该命令将自己注册 为服务的管理者 |
BINDER_THREAD_EXIT |
通知线程退出。每个线程在退出时都应该告知 Binder 驱动。这样才能释放相关的资源 |
BINDER_VERSION |
获取 Binder 的版本号 |
进程通过设备节点提供的 ioctl
接口传递命令,Binder
驱动的大部分的工作都是 在这个函数中完成的。binder_ioctl
提供的命令如上表
在 Android
系统中客户端和服务器端是通过 Binder
驱动进行命令传递和数据交互。客户端如果需要和服务端进行交互,需要向 Service Manager
查询,获得服务代理,通过代理进行通信。如果没有 Service Manager
,服务就不能注册,而客户端也不能获得服务代理。Service Manager
是系统中服务的管理者,它是进程间通信的关键。
ServiceManager
是一个特殊的服务进程。它负责管理系统的服务,例如管理 Activity
的服务、管理 Package
的服务等,所有的服务都是 ServiceManager
服务的客户端。 ServiceManager
是在 Linux
守护进程 init 进程启动时,解析配置文件 init.rc
时启动 的,配置文件如下所示:
/*init.rc*/
service servicemanager /system/bin/servicemanager
class core
user system
group system
critical
onrestart restart zygote
onrestart restart media
onrestart restart surfaceflinger
onrestart restart drm
ServiceManager
启动过程主要分为三步:
第一步,打开/dev/binder
驱动节点,然后调用 mmap
设置 128k
内存缓冲区;
第二步,将 ServiceManager
注册成为 Binder
机制的服务的管理者,通过向 Binder Driver
发送 BINDER_SET_CONTEXT_MGR
的ioctl
命令。因为 ServiceManager
是在 init
进程中被启动的,所以可以保证它是第 一个想 binder driver
注册成为管理者的程序;
第三步,通过 binder_loop
循环等待来 自客服端的请求。它的消息循环和典型的基于事件驱动的程序循环框架类似流程如上图。
通过以上三步,ServiceManager
被注册为 Binder
的上下文管理者。并在相关变 量中记录服务管理者的相关信息,例如进程号等。当其他的服务也通过 ioctl
进行注 册时上下文管理者时,此时 binder_context_mgr_node
已经不为空,拒绝注册,从而 保证了服务管理者的唯一性。
IServiceManager 提供函数 |
功能 |
---|---|
getService(const String16& name) |
客户端传入服务名称,接收到服务名称后, 调用 checkService 成员函数。当 Binder IPC 失败时,重新尝试。该函数返回值是一个指 向 BpBinder 实例的指针 |
checkService(const String16& name) |
接收服务名称,然后从 ContextManager 查看 服 务 信 息 , 最 后 返 回 服 务 代 理 , 和 getService 功能类似 |
status_t addService(const String16& name,const sp<IBinder>& service) |
注册服务,以服务名和服务实例指针为参 数,返回注册是否成功 |
vector<String16> listService |
该参数是无参函数,系统中注册的所有服 务,并以数组的形式返回。 |
客户端和 ServiceManager
都是继承 IServiceManager
。IServiceManager
定义了服务端的功能。IServiceManager
定义成员函数如上表所示:
ServiceManager
提 供 了 的 如上 表几 种 功 能 , 客 服 端 通 过 代 理 BpServiceseManager
来完成这些功能。客服端通过 defaultServiceManager
来获代 理,函数实现如下:
defaultServiceManager()
{
if(gDefaultServiceManager 不为空){
返回 gDefaultServiceManager
}
if(gDefaultServiceManager 为空) {
向 ContextManager 查找 ServiceMnager 服务
}返回 gDefaultServiceManager;
}
通过以上代码可以看出,BpServiceManager
是通过单例模式实现的,这样可以 减少对象生成的个数。生成 gDefaultServiceManager
主要需要三步:
-
ProcessState::self()
通 过 单 利 模 式 创 建ProcessState
对 象 , 用 全 局 变 量ProcessState
来保存该变量。ProcessState
的构造函数打开BinderDriver
,将返回的文 件描述符保存在mDriverFD
变量中,然后通过mmap
函数获得一块mmap
区域。当 进程间通信需要传递数据到Binder
驱动中时,该块区域被用作保存区域,并将区域 的首地址保存在成员变量mVmStart
变量中。上述的创建过程如下图所示。 -
通过
getStrongProxyObject
函数并传递0
,获得ServiceManager
的BpBinder
。Process
中通过全局列表来记录Binder
对象的信息,每个表项是一个handle_entry
。 当表项中不存在时,创建BpBinder
对象,并保存在表项中。ServiceManager
在Binder Driver
中的handler
为 0。 -
interface_cast<IServiceManage>
是一个宏定义的模板函数,调用该函数时最终调用的IServiceManager
的asInterface
成员函数。通过转换生成BpServiceManager
实例对象。
推荐阅读
-
[RK3399][Android7.1]通信机制 Binder
-
Android系统进程间通信(IPC)机制Binder中的Server和Client获得Service Manager接口之路
-
Android系统进程间通信Binder机制在应用程序框架层的Java接口源代码分析
-
Android系统进程间通信(IPC)机制Binder中的Client获得Server远程接口过程源代码分析
-
浅谈Service Manager成为Android进程间通信(IPC)机制Binder守护进程之路
-
Android系统进程间通信(IPC)机制Binder中的Server启动过程源代码分析
-
Android进程间通信(IPC)机制Binder简要介绍
-
Android进程间通信(IPC)机制Binder简要介绍
-
浅谈Service Manager成为Android进程间通信(IPC)机制Binder守护进程之路
-
浅谈Service Manager成为Android进程间通信(IPC)机制Binder守护进程之路