一. 为什么会有Binder
首先我们来看一句话:Binder是Android中使用最广泛的IPC(进程间调用)机制。所以说白了,Binder的存在是为了Android系统中的跨进程函数(包括服务等)调用。这是操作系统的基本功能之一。在Android系统中的具体表现形式就是应用进程与系统服务进程之间的交互,或者是用户进程与用户进程之间的交互,又或者是系统进程与系统进程之间的交互。例如Activity Manager Service(AMS)对应用的Activity,Service以及Broadcast的管理。
二. Binder的原理及实现
1)Binder的基本组成
Binder基本上是由Binder驱动,Service Manager,Binder Client以及Binder Server组成。其实Binder机制运行过程和网络TCP/IP协议的网络运行过程比较类似。Binder驱动相当于路由器,Service Manager相当于DNS,Binder Client相当于客户端,负责向服务器发起请求,也就是向Binder Server进程发起服务请求,Binder Server相当于服务器,负责处理客户端发过来的请求,也就是处理Binder Client发过来的服务请求。
智能指针。Binder的实现中,还广泛地应用到了智能指针。智能指针可以自动的进行释放,较好地解决了1)指针没有得到初始化,2)指针没被释放,3)野指针等问题。其实现比较简单,将指针封装到类中,同时在智能指针模板类中加入了计数引用。这里要区分下强指针和弱指针。
强指针和弱指针的出现是为了防止循环引用导致内存空间得不到释放。
对于强指针,当其强指针计数为0时,不管弱指针计数为多少,都可以将该强指针释放掉。对于弱指针,当其要访问对象时,必须要升级为强指针才能进行访问。
数据传递载体Parcel。在同一个进程间对象的传递可以通过引用或者指针传递,但是进程间传递对象,指针或引用是行不通的。所以我们需要将数据进行封装,从进程A传递到进程B。
Parcel负责数据的打包和重组。打包和重组要遵从相同的协议(协议要配套),才能正确地进行数据传递。此外Parcel底层的数据读写操作都是通过本地代码(c++)实现的。
- Parcel数据存储方式
- 具体示例(String和Binder)
writeString()/readString()和writeStrongBinder()/readStrongBinder()。
2)Binder驱动(路由器)
Binder驱动是一个标准的Linux驱动,并运行于内核态。它会将自己注册成一个misc device,并向上层提供一个/dev/binder节点。其不对应于真实的设备,但是可以提供诸如,open(), ioctl(), mmap()等常用的文件操作。全部的可用操作如下:
static const struct file_operations binder_fops={
.owner = THIS_MODULE,
.poll = binder_poll,
.unlocked_ioctl = binder_ioctl,
.map = binder_mmap,
.open = binder_open,
.flush = binder_flush,
.release = binder_flush,
};
打开(binder_open)。在内核态中完成打开,也就是分配的是内核空间的地址,方便内存共享。每个进程对应于一个binder管理实体(结构体为binder_proc,如下所示)。我们可以adb连接手机设备后在/sys/kernel/debug/binder(貌似4.0以后的系统就是这个路径了,以前在/proc目录下)目录下看到其管理文件。这里面的文件值得好好分析,包括了各个进程于binder交互的状态(./proc目录下)。还有全局的状态,还有binder操作日志,包括全部的和失败的。不失为debug利器。
struct binder_proc {
struct hlist_node proc_node;
struct rb_root threads;
struct rb_root nodes;
struct rb_root refs_by_desc;
struct rb_root refs_by_node;
int pid;
struct vm_area_struct *vma;
struct mm_struct *vma_vm_mm;
struct task_struct *tsk;
struct files_struct *files;
struct hlist_node deferred_work_node;
int deferred_work;
void *buffer;
ptrdiff_t user_buffer_offset;
struct list_head buffers;
struct rb_root free_buffers;
struct rb_root allocated_buffers;
size_t free_async_space;
struct page **pages;
size_t buffer_size;
uint32_t buffer_free;
struct list_head todo;
wait_queue_head_t wait;
struct binder_stats stats;
struct list_head delivered_death;
int max_threads;
int requested_threads;
int requested_threads_started;
int ready_threads;
long default_priority;
struct dentry *debugfs_entry;
};
映射(binder_mmap)。binder_mmap(struct file * filp, struct vm_area_struct *vma),其中vma是应用程序中的一段虚拟地址。该函数将会在内核中给这段虚拟地址分配内核空间的物理地址。同样地,对于其他进程,也会发生同样的事情。如此就完成了进程间的内存共享。
- binder_mmap()函数首次只会分配一页的内核空间物理地址,也算是一种缓慢分配的方法,对嵌入式设备还是非常有必要的。
- list_head proc->buffers管理映射的所有内存块,rb_root proc->free_buffers管理所有可用的空闲内存,rb_root proc->allocated_buffers管理所有已经分配了的内存。
I/O操作(binder_ioctl)。binder_ioctl()可以满足应用进程于binder之间的所有I/O操作,包括读,写等等。
3)Service Manager(DNS)
Service Manager本身就是一个Binder Server,所以其负责为Binder Client提供服务,且Binder驱动中有专门为SM服务的一些命令。所以,Service Manager在Binder被使用前就应该ready。SM的启动是在init进程解析init.rc的过程中。
4)Binder Client(客户端)
5)Binder Server(服务器)
三. 对Binder的分析
相比与其他进程间通信方式,如信号量,管道,Socket等方式的优缺点。