Android这个庞大的系统中会涉及非常多的进程间通信,是什么让各个进程间通信起来毫无障碍且有条不紊的呢?传统的IPC(即”进程间的通信”缩写),例如Pipe和Socket,执行一次通信需要两次数据的拷贝,举个例子如,Client要将一块内存数据传递给Server,一般的做法是,Client将这块数据从它的进程空间拷贝到内核空间中,然后内核再将这个数据从内核空间拷贝到Server的进程空间,这样,Server就可以访问这个数据了,但是在这种方法中,执行了两次内存拷贝操作。而采用Binder机制,只需要把Client进程空间的数据拷贝一次到内核空间,然后Server与内核共享这个数据就可以了,整个过程只需要执行一次内存拷贝,提高了效率。Binder的主要核心有两部分,分别是IPC(进程间通信)和RPC(远程过程调用)。
IPC 进程间的通信,以AB两个进程间通信作为例子,数据传输有三要素:
源:A进程(要去访问B进程提供的LED驱动接口)目的: B进程(进程A是如何得知是进程B提供LED访问呢?): B进程向ServiceManager注册led服务 A进程向ServiceManager查询led服务,得到一个handle(句柄)数据传输:使用bufferRPC 远程调用(在IPC的基础上做了层封装),例如调用led_open / led_ctl,但它并没有权限,所以需要做如下处理:
封装(构造)数据A进程发送数据(通过IPC通道发送给B进程),B进程会取出数据然后调用led_open / led_ctl,就好像A直接操作LED一样RPC:远程调用涉及的过程:
调用哪一个函数:根据Server的函数编号(led_ctl、led_open)传给给它什么参数:通过IPC的buffer传输(哪个灯,亮还是灭)返回值:通过IPC的buffer传输(返回成功与否)Binder系统涉及4个部分:
1. Client(如上文提及的A进程),程序操作如下:
Open binder驱动(涉及进程间的通信都需要open)获取服务 a) 向servicemanager查询服务 b) 获得一个handle(句柄)向handle发数据2. ServiceManager(一个特殊的service,告诉A如何找到B),程序操作如下:
Open binder驱动告诉binder驱动程序,它是ServiceManagerWhile循环,读驱动获取数据,解析数据,调用下面两个函数 a) Server注册服务(在链表中记录服务名) b) Client获取服务 i. 在链表中查询有无服务 ii. 返回server进程的handle3. Server(上文提及的B进程),程序操作如下:
Open binder 驱动注册服务 a) 向servicemanager发送服务名While循环,读驱动,解析数据,调用对应的函数4. Binder(上面三部分的通信通过binder驱动实现)
下面用系统自带的binder程序来理解整个调用过程,代码是使用C语言来实现的,它在系统文件中的目录如下:
android\frameworks\native\cmds\servicemanager\
调用过程分析只涉及下面三个文件:
service_manager.c bctest.c(半成品,可以根据它写出我们的Client和Server程序) binder.c(封装好的C库)
上面三个文件整个调用过程对应的源码简析如下:
************************************** service_manager.c ************************************** a. binder_open b. binder_become_context_manager c. binder_loop(bs, svcmgr_handler); c.1 res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr); c.2 binder_parse // 解析 // 处理 : svcmgr_handler SVC_MGR_GET_SERVICE/SVC_MGR_CHECK_SERVICE : 获取服务 SVC_MGR_ADD_SERVICE : 注册服务 // 回复 ******************************************* bctest.c ******************************************* 注册服务的过程: a. binder_open b. binder_call (bs, &msg, &reply, 0, SVC_MGR_ADD_SERVICE) // bs: 打开的device的binder fd // msg: 含有服务的名字 // reply: 它会含有servicemanager回复的数据 // 0: 表示servicemanager // SVC_MGR_ADD_SERVICE(code): 表示要调用servicemanager中的"addservice函数" 获取服务的过程: a. binder_open b. binder_call(bs, &msg, &reply, target, SVC_MGR_CHECK_SERVICE) // bs: 打开的device的binder fd // msg: 含有服务的名字,表示要获得哪一个名字 // reply: 它会含有servicemanager回复的数据, 表示提供服务的进程 // target :里面是0,表示servicemanager // SVC_MGR_CHECK_SERVICE(code): 表示要调用servicemanager中的"checkservice函数" ******************************************* binder.c ******************************************* binder_call:远程调用 int binder_call(struct binder_state *bs, struct binder_io *msg, struct binder_io *reply, uint32_t target, uint32_t code) //target :向谁发数据 //code:调用哪个函数 //msg:提供什么参数 //reply:返回值 binder_call最终会构造一个binder_write_read结构体,然后调用ioctl发送出去 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; }; Read_buffer里面调用binder_transaction_data(形参里面有code和用户构造的参数),根据code可以决定调用什么函数 如何使用binder进行数据传输: 1. 构造参数:一般放在buf里面,有个结构体叫binder_io来描述 2. 调用ioctl来发数据 res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr); 3. Ioctl不仅能发数据,也可以收数据,收到一个binder_write_read结构体数据,需要转换为binder_io(即将收到的数据构造成一个binder_io)怎么写APP:
client a) binder_open b) 获得服务:handle c) 构造参数:binder_io d) 调用binder_call函数,形参中包含:handle(发给哪个进程),code(想调用进程的哪个函数),binder_io(发送的参数) e) binder_call会返回binder_io,取出返回值server a) binder_open b) 注册服务 c) ioctl(这里会读到client发来的数据,会读到handle,code, binder_io这些参数) d) 解析数据得出code和参数 binder_write_read. read_buffer->binder_transaction_data->code 和 参数,这个参数会转换为binder_io e) 根据code,决定调用哪个函数,从binder_io取出参数,这个参数是传给调用的那个函数的 f) 把返回值再次转换为binder_io发给client,client就可以从binder_call得到这个binder_io了下一章会具体实现编写代码的过程