注:基于Android5.1版本,Tiny4412平台。

 

一、学习笔记

1.Binder的核心是IPC和RPC
IPC: (Inter-Process Communication)进程间通信,指至少两个进程或线程间传送数据或信号的一些技术或方法。
RPC: (Remote-Process Communication)远程过程调用,类似于调用其它进程的函数。

ICP三要素:
源:A
目的:
B向ServiceManager注册led服务
A向ServiceManager查询led服务得到一个handle。
数据:buf[512]

RPC:
调用哪个函数:Server的函数编号
传给它什么参数:通过IPC的buf[]进行传输(使用的是binder驱动)。
返回结果:远端执行完返回值

2.系统自带的C实现的Binder程序:frameworks/native/cmds/servicemanager
service_manager.c 充当SM的角色,管理所有的Service,其本身也是一个服务。
binder.c 封装好的C库
bctest.c 半成品,演示怎样注册服务

3.int svcmgr_publish(struct binder_state *bs, uint32_t target, const char *name, void *ptr)
如果两个service的注册函数svcmgr_publish()的最后一个参数值相同,那么会报错。
正常情况下kill掉service_manager的时候,所有的service都会收到死亡通知,然后从链表中删除掉。但是若两个service指定为相同的ptr,
那么下次再重启service_manager的时候它会报这个服务已经存在了,由于相同的ptr导致kill掉service_manager时有一个并没有收到死亡通
知,也就不能从链表中删除。

4.binder应该是个内核线程,binder驱动中创建了一个单CPU的工作队列
# ps | grep binder
root 1073 2 0 0 c00a0668 00000000 S binder


5.驱动中数据结构表示
struct binder_ref : 表示引用
binder_node :表示一个Service
binder_proc :表示进程
binder_thread :表示线程的一个线程

6.handle是进程A(Client)对进程B(Service)提供的服务的引用,由handle可以对比desc成员找到binder_ref结构,
其*node成员指向表示某项服务的binder_node结构体,binder_node的proc成员指向表示进程的binder_proc结构体,其内部指向对应的进程
从而找到目的进程, 然后把数据给到目的进程的todo链表上,然后唤醒目的进程。

7.handler是per-process的

8.mmap使用户态可以直接操作内核态的内存。service mmap后,service用户空间就可以直接使用内核buff.所以binder通信只需要client端的
一次copy_from_user()一次拷贝。但是数据类型cmd还是要拷贝两次的。

9.发给驱动的命令和驱动发给用户的命令是不同的
A进程给B进程发送数据:A进程使用BC_TRANSACTION发送,经过binder驱动转换,B进程接收到是BR_TRANSACTION
B进程给A进程回复数据:B进程使用BC_REPLY发送,经过binder驱动转换,A进程接收到是BR_REPLY
只有这四种会涉及2个进程,其它的cmd都只涉及应用和驱动的通信。

10.每个进程在open("/dev/binder")时会给它创建一个binder_proc结构,每个线程在调用ioctl()的时候都会创建一个binder_thread结构。

11.Service可以分为匿名和具名Service. 前者没有注册到ServiceManager, 应用无法通过名字获取到访问该服务的Proxy对象。


12.但是创建子进程不能使用fork(),因为在service_manager.c中binder_open()中mmap()调用内核的binder_mmap():
这里指定了VM_DONTCOPY,在用户空间中使用fork()创建的子进程无法访问到service_manager.c中mmap()的内存 vma->vm_flags = (vma->vm_flags | VM_DONTCOPY) & ~VM_MAYWRITE; 只能使用pthread_create(),它会把mmap()的空间也拷贝过来。(Android11上好像没有这个flag了)

13.创建多线程Service读取到驱动发出的BR_SPAWN_LOOPER后使用pthread_create()创建多线程。驱动处理不过来的时候就会向Service发送这个
请求命令。驱动判断处理不过来的方法是Service端没有线程在阻塞等待。Service可以设置最大线程数量。

14.addService执行流程

(1) test_service中为每个service构造flat_binder_object结构体,其type=BINDER_TYPE_BINDER表示让binder驱动为我这个Service构造一个
binder_node结构体,每个不同的服务的*binder或cookie域不同。
(2) ioctl(BINDER_WRITE_READ)来发送数据。数据中包括flat_binder_object结构体和服务的名字,数据中的handle=0表示发给SM。
(3) binder驱动中为每一个flat_binder_object构造一个binder_node结构体,它表示一个Service。
(4) binder驱动根据handle=0找到SM,然后把数据发给SM。并为SM构造binder_ref结构体。
(5) SM应用程序中收到数据后记录下Service的名字和handle(desc)值,记录的数据保存在svlist链表上。

15.getService执行流程

(1) test_client构造数据,name为要获取的Service的名字,handle为0表示向SM获取服务。
(2) 调用ioctl(BINDER_WRITE_READ)将数据发给binder驱动
(3) binder驱动根据handle=0找到SM进程,然后把数据发给SM进程
(4) SM进程从svlist链表中根据名字找到对应Service的handle值,然后将其写给驱动(目的是发给Client)。写给驱动的数据格式也是一个
flat_binder_object结构体,其type=BINDER_TYPE_HANDLE表示让驱动为client进程创建一个binder_ref结构体。
(5) 驱动发现收到的数据中有flat_binder_object结构体且其type=BINDER_TYPE_HANDLE就会为Client进程创建一个binder_ref结构体,
其handle为数据中的handle,node域指向要获得Service的binder_node结构体。

16.client使用Service的执行流程

(1) 构造数据,参数:code表示调用哪个函数,handle表示使用哪个Service
(2) 使用ioctl(BINDER_WRITE_READ)将数据发给驱动
(3) 驱动中取出数据,根据handle找到binder_ref结构体(对比desc域),根据其node域找到binder_node结构体(表示一个Service)从而找到
对应的Service,然后把数据传给这个Service进程,并设置数据中的ptr和cookie为binder_node的ptr和cookie.
(4) Service进程中根据ptr和cookie值得知Client想调用哪个函数(服务)。

17.mm showcommands 编译,打印出头文件搜索路径等信息

18.Binder系统过程分析

(1)addService("Hello", *ptr),在C实现的Demo中调用bio_put_obj(),将ptr赋值给flat_binder_object.binder,cookie赋值为0。内核中
表示服务的binder_node结构的*ptr和*cookie的值就是由Service应用程序传参控制的,可用于区分不同的Service。

Binder学习——C实现
void bio_put_obj(struct binder_io *bio, void *ptr)
{
    /*内核中根据这个结构体创建binder_node结构体*/
    struct flat_binder_object *obj;

    obj = bio_alloc_obj(bio);
    if (!obj)
        return;

    obj->flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
    obj->type = BINDER_TYPE_BINDER;
    obj->binder = (uintptr_t)ptr;
    obj->cookie = 0; /*这里的binder和cookie都是由Service决定的*/
}
View Code

相关文章: