Skip to content

Instantly share code, notes, and snippets.

@chuanwang66
Last active August 29, 2015 14:07
Show Gist options
  • Save chuanwang66/b048d10c19bfcfc69113 to your computer and use it in GitHub Desktop.
Save chuanwang66/b048d10c19bfcfc69113 to your computer and use it in GitHub Desktop.
背景文章:看android内核需要看什么——
《Android系统和linux内核的关系详解》
http://blog.sciencenet.cn/blog-618303-626146.html
<Binder IPC Mechanism>
http://www.angryredplanet.com/~hackbod/openbinder/docs/html/BinderIPCMechanism.html
<Android's Binder>
http://lukejin.wordpress.com/2011/03/13/android%E4%B8%AD%E7%9A%84binder/
<技术内幕: Android的IPC机制-Binder>
http://www.linuxidc.com/Linux/2011-08/40508.htm
内核代码:
http://www.cs.fsu.edu/~baker/devices/lxr/http/source/linux/drivers/staging/android/binder.c
===========================================================================================
看代码理解Android Binder:
以下内容参见http://blog.csdn.net/yangwen123/article/details/9316987
1. Android IPC和Binder基本概念
Android系统共有三种IPC机制:
- 标准Linux Kernel IPC接口y
- 标准D-BUS接口
- Binder接口
Binder是OpenBinder的Google精简实现。
Binder框架定义了四个角色:Server,Client,ServiceManager(以后简称SMgr)以及驱动。其中 Server,Client,SMgr运行于用户空间,驱动运行于内核空间。这四个角色的关系和互联网类似:Server是服务器,Client是客户端,SMgr是域名服务器(DNS),驱动是路由器。
Binder使用Client-Server通信方式。一个进程作为Server提供诸如视频/音频解码,视频捕获,地址本查询,网络连接等服务;多个进程作为Client向Server发起服务请求,获得所需要的服务。实现这种Client-Server通信的要点:一是Server必须有确定的访问接入点(或者说是地址)来接受Client的请求,并且Client可以通过某种途径获知Server的地址;二是制定Command-Reply协议来传输数据。
2. 什么是Binder: Server中的Binder对象
Binder:对于Server, 可以看作Server提供的实现某个服务的访问接入点;对于Client,可以看作通向Server的管道入口。
与其他IPC不同,Binder使用了面向对象的思想来描述作为访问接入点的Binder及其在Client的入口:Binder是一个实体位于Server中的对象,该对象提供了一套方法用来实现对服务的请求,就像类的成员函数。遍布于client中的入口可以看作指向这个Binder对象的指针,一点那获得了这个“指针”/“引用”/“句柄”就可以调用该对象的方法访问Server。在Client看来,通过Binder“指针”/Binder“引用”/Binder“句柄”调用其提供的方法和通过指针调用其他任何本地对象的方法并无区别,尽管前者的实体位于远端Server中,而后者实体位于本地内存中。从通信的角度看,Client中的Binder“指针”/Binder“引用”/Binder“句柄”可以看作是Server Binder的“代理”,在本地代表远端Server为Client提供服务。
面向对象思想的引入将进程间通信转化为通过对某个Binder对象的引用调用该对象的方法,而其独特之处在于Binder对象是一个可以跨进程引用的对象,它的实体位于一个进程中,而它的引用却遍布于系统的各个进程之中。最诱人的是,这个引用和java里引用一样既可以是强类型,也可以是弱类型,而且可以从一个进程传给其它进程,让大家都能访问同一Server,就象将一个对象或引用赋值给另一个引用一样。Binder模糊了进程边界,淡化了进程间 通信过程,整个系统仿佛运行于同一个面向对象的程序之中。形形色色的Binder对象以及星罗棋布的引用仿佛粘接各个应用程序的胶水,这也是Binder 在英文里的原意。
3. 什么是Binder驱动
和路由器一样,Binder驱动虽然默默无闻,却是通信的核心。尽管名为“驱动”,实际上和硬件设备没有任何联系,只是实现方式和设备驱动程序是一样的:它工作于内核态,提供open(), mmap(), poll(), ioctl()等标准文件操作,以字符驱动设备中的misc设备注册在设备目录/dev下,遵循Linux设备驱动模型,用户通过/dev/binder访问它。
Binder驱动负责进程间Binder通信的建立,Binder在进程之间的传递,Binder引用计数管理,数据包在进程之间的传递和交互等一系列底层支持。
驱动和应用程序之间定义了一套接口协议,主要功能通过binder_ioctl函数与用户空间的进程交换数据,不提供read(),write()接口,因为ioctl灵活方便,,且能够一次调用实现先写后读以满足同步交互,不必分别调用write()和read()。BINDER_WRITE_READ用来读写数据,数据包中有个cmd用于区分不同的请求。
binder_thread_write函数用于发送请求和返回结果,其中调用binder_transaction函数来转发请求并返回结果。当服务进程收到请求时,binder_transaction函数会通过对象的handle找到对象所在进程,如果handle为0,就认为请求的是ServiceManager进程。
binder_thread_read函数用户读取结果。
4. ServiceManager和实名Binder
和DNS类似,ServiceManager的作用是将字符形式的Binder名字转化为Client中对该Binder的引用,使Client能通过Binder名字获得对Server中Binder实体的引用。注册名字的Binder叫实名Binder。Server创建了Binder实体,为其去一个字符形式、可读易记的名字,将这个Binder连同名字以数据包的形式通过Binder驱动发送给ServiceManager,通知ServiceManager注册一个名字叫“张三”的Binder+它位于某个Server中。Binder驱动为这个穿过进程边界的Binder创建位于内核中的实体节点以及ServiceManager对实体的引用,将名字和新建的引用传递给ServiceManager。ServiceManager收到数据包后,从中取出名字和引用填入一张查找表中。
细心的读者可能会发现其中的蹊跷:ServiceManager是一个进程,Server是另一个进程,Server向ServiceManager注册Binder必然会涉及进程间通信。当前实现的是进程间通信却又要用到进程间通信,这就好像蛋可以孵出鸡的前提是要找只鸡来孵蛋。Binder实现比较巧妙:预先创造一只鸡来孵蛋。ServiceManager和其他进程同样采用Binder通信,此时Binder是Server端,有自己的Binder实体,其他进程都是Client,需要通过这个Binder的引用来实现Binder的注册、查询和获取。
ServiceManager提供的Binder比较特殊,它没有名字也不需要注册,当一个进程使用BINDER_SET_CONTEXT_MGR命令将自己注册成ServiceManager时Binder驱动会自动为它创建Binder实体(这就是那只预先造好的鸡)。其次这个Binder的引用在所有的Client中都固定位0而无须通过其他手段获取。也就是说,一个Server若要向ServiceManager注册自己的Binder就必须通过0这个引用和ServiceManager的Binder通信。类比网络通信,0号引用好比域名服务器的地址,你必须手工或动态配置好。
注:这里说的Client是相对ServiceManager而言的,一个应用程序是个提供服务的Server,但对ServiceManager来说它就是个Client
5. Client获取实名Binder的引用
Server向ServiceManager注册了Binder实体及名字后,Client就可以通过名字获得该Binder的引用了。Client也利用保留的0号引用向ServiceManager请求访问某个Binder:我申请获取名字叫“张三”的Binder引用。ServiceManager收到这个连接请求,从请求数据包中获取Binder的名字,在查找表里找到该名字对应的Binder引用,将该引用作为回复发送给发起请求的Client。
从面向对象的角度,这个Binder对象现在有了两个引用:一个位于ServiceManager中,一个位于发起请求的Client中。如果接下来更多的Client请求该Binder,系统中就会有更多的引用指向该Binder,就像Java里一个对象存在多个引用一样。类似的这些指向Binder的引用是强类型,从而确保只要有引用Binder实体就不会被释放掉。
6. 匿名Binder
并不是所有Binder都需要注册给ServiceManager广而告之的。Server端可以通过已经建立的Binder连接将创建的Binder实体传给Client,当然这条已经建立的Binder连接必须是通过实名Binder实现的。由于这个Binder没有向ServiceManager注册名字,所以是个匿名Binder。Client将会收到这个匿名Binder的引用,通过这个引用向位于Server中的实体发送请求。匿名Binder为通信双方建立一条私密通道,只要Server没有把匿名Binder发给别的进程,别的进程就无法通过穷举或猜测等任何方式获得该Binder的引用,向Binder发送请求。
7. Binder Object
Binder Object: 进程间传输的数据被称为Binder对象, 是一个flat_binder_object:
common-android-3.10/drivers/staging/android/uapi/binder.h
struct flat_binder_object {
/* 8 bytes for large_flat_header. */
unsigned long type; //有5种Binder对象的类型,BINDER_TYPE_BINDER/BINDER_TYPE_WEAK_BINDER/BINDER_TYPE_HANDLE/BINDER_TYPE_WEAK_HANDLE/BINDER_TYPE_FD
unsigned long flags; //传输方式:enum transaction_flags
/* 8 bytes of data. */
union {
void *binder; /* local object */
signed long handle; /* remote object */
};
/* extra data associated with local object */
void *cookie;
};
binder_transaction_data: Binder对象的传递是通过binder_transaction_data实现的,即Binder对象实际上是封装在binder_transaction_data结构体中的,这个数据结构才是真正要传输的数据:\
common-android-3.10/drivers/staging/android/uapi/binder.h
struct binder_transaction_data {
/* The first two are only used for bcTRANSACTION and brTRANSACTION,
* identifying the target and contents of the transaction.
*/
union {
__u32 handle; /* target descriptor of command transaction */
binder_uintptr_t ptr; /* target descriptor of return transaction */
} target;
binder_uintptr_t cookie; /* target object cookie */
__u32 code; /* transaction command 描述Binder对象执行操作的命令*/
/* General information about the transaction. */
__u32 flags;
pid_t sender_pid;
uid_t sender_euid;
binder_size_t data_size; /* number of bytes of data */
binder_size_t offsets_size; /* number of bytes of offsets */
/* If this transaction is inline, the data immediately
* follows here; otherwise, it ends with a pointer to
* the data buffer.
*/
union {
struct {
/* transaction data */
binder_uintptr_t buffer;
/* offsets from buffer to flat_binder_object structs */
binder_uintptr_t offsets;
} ptr;
__u8 buf[8];
} data;
};
每一个flat_binder_object对象内核都有一个唯一的binder_node对象,每一个binder_node对象内核也会有一个唯一的binder_ref对象。
8. Binder协议
Binder协议的基本格式是(命令+数据),使用static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)函数实现交互,命令由参数cmd承载,数据由参数arg承载,后者随cmd不同而不同。下表列举了所有命令及对应的数据:
命令 含义 arg
BINDER_WRITE_READ 该命令向Binder写入或读取数据。参数分为两段:写部分和读部分。如果write_size不为0就先将write_buffer里的数据写入Binder;如果read_size不为0再从Binder中读取数据存入read_buffer中。write_consumed和read_consumed表示操作完成时Binder驱动实际写入或读出的数据个数。 struct binder_write_read {
...
};
BINDER_SET_MAX_THREADS 该命令告知Binder驱动接收方(通常是Server端)线程池中最大的线程数。由于Client是并发向Server端发送请求的,Server端必须开辟线程池为这些并发请求提供服务。告知驱动线程池的最大值是为了让驱动在线程达到该值时不要再命令接收端启动新的线程。 int max_threads;
BINDER_SET_CONTEXT_MGR 将当前进程注册为SMgr。系统中同时只能存在一个SMgr。只要当前的SMgr没有调用close()关闭Binder驱动就不能有别的进程可以成为SMgr。 —
BINDER_THREAD_EXIT 通知Binder驱动当前线程退出了。Binder会为所有参与Binder通信的线程(包括Server线程池中的线程和Client发出请求的线程)建立相应的数据结构。这些线程在退出时必须通知驱动释放相应的数据结构。 —
BINDER_VERSION 获得Binder驱动的版本号。 —
这些命令中,最常用的是BINDER_WRITE_READ。该命令的参数包括两部分数据:一部分是向Binder写入的数据,一部分是要从Binder读出的数据,驱动程序先处理写部分再处理读部分。这样安排的好处是应用程序可以很灵活地处理命令的同步和异步。例如若要发送异步命令可以只填入写部分而将read_size置为0;若要只从Binder获取数据可以将写部分write_size置为0;若要发送请求并同步等待返回数据可以将两部分都置上。
common-android-3.10/drivers/staging/android/uapi/binder.h
struct binder_write_read {
binder_size_t write_size; /* bytes to write */
binder_size_t write_consumed; /* bytes consumed by driver */
binder_uintptr_t write_buffer;
binder_size_t read_size; /* bytes to read */
binder_size_t read_consumed; /* bytes consumed by driver */
binder_uintptr_t read_buffer;
};
9. Binder驱动接口和用户接口
本部分详见代码common-android-3.10/drivers/staging/android/binder.c static int __init binder_init(void)函数
驱动接口和用户接口:
Android Binder设备驱动接口函数是device_initcall(binder_init);
驱动程序的一个主要功能就是向用户控件的程序提供接口操作,这个接口是标准的。对于Android Binder驱动,包含的接口有:
- proc接口 (/proc/binder)
./proc/binder/state
./proc/binder/stats
./proc/binder/transactions
./proc/binder/transaction_log
./proc/binder/failed_transaction_log
./proc/binder/proc/
- 设备接口 (/dev/binder)
binder_open
binder_release
binder_flush
binder_mmap
binder_poll
binder_ioctl
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment