本文共 2490 字,大约阅读时间需要 8 分钟。
至少在五六年前就听说过binder机制,但从未想去深入了解。这次忘了是怎么和同事聊起这个东西的不过作为一个码农我想如果连google OS最为精髓的IPC都不知道,真不好以后出去跟人打招呼,于是乎决心了解下,一开始网上搜了些资料,感觉基本上都没讲出些啥东西,没办法只好 read the fucking code, 上周一直忙着签证的事情,没怎么定下心看因而节假日放出大招。
保留一贯的风格不喜欢整篇的paste code,大部分说一些个人的理解 源码靠大家自己主要分四部分
binder是个啥东西?
binder就是个IPC,给进程间通信用的,它的框架包含client,server,servicemanger和binder driver。 前面三个都是user space的进程,线程,后面的binder driver工作在kernel态。这三个用户态的东西,尤其是servicemanager不是可有可无的,不是说为了应用层的服务框架设计方便所以
多搞出的一个东西,这个binder driver的设计其实是有考虑类似的三个不同角色的分类的。从图上可以看出所有三个用户态的对象在通信的时候都必须要经过binder driver,简单来说每个binder实体都可以通过一个handle来进行标识,但是这个handle是如何获得的就比较复杂了。
可以认为一个process就对应一个binder_proc的实体,这个实体通过open dev/binder这个文件动态创建,所以在C(client), S(server)和M(service manager)启动的初期都必须分别open binder这个文件从而在kernel里面获得binder_proc实体, 但是这个时候并没有handle产生,所以没办法互相通信,这个时候kernel会有一个特殊的handle 0,对应于servicemanager,整个系统只能有一个service manager, 通过M去ioctl前面打开的fd去注册自己为servicemanager,这个特殊的ioctl是BINDER_SET_CONTEXT_MGR,完成了这个特殊的handle的初始化后client和sever就可以向services manager发送命令了,ioctl的cmd为BINDER_WRITE_READ,handle 为0。从上面的分析看binder确实只是个IPC,没啥特殊啊。错,由我来带大家慢慢看。
*首先从mem copy上看普通的IPC socket pipe msgQ都需要两次拷贝(shemem不需要拷贝),binder框架下只需要一次copy,发生在用户态到内核态,而内核态到用户态不需要copy。
*binder是有thread概念的,内核态中binder_thread对应到每个binder_proc,下面会详细说 *binder有transaction的概念 *binder中内嵌了服务发布的机制以上的这些特性非常方便应用层去做基于client server的应用程序的开发。
binder是有thread概念的,内核态中binder_thread对应到每个binder_proc,这就好比我可以在一个进程里面有很多个thread,它们都请求一个server的服务,这个时候每个thread都通过一个fd,就是open /dev/binder时获得的,去和同一个server发请求,此时kernel里面的binder_proc里面就会有一个rbt去记录这些thread的信息,一个thread一个binder_thread结构,并且这边有些限制,比如一个binder_proc中最多能创建多少threads,通过IOCTL中BINDER_SET_MAX_THREADS来进行设置,每次新的线程被create出来后,调用了binder相关的IOCTL后会自动创建binder_thread的结构,但是此时此thread还不能用来收发信息,通过BC_REGISTER_LOOPER使能此thread。
还有个作用是当一个binder_proc对应的binder_thread有在pending的时候(代表threads数目大于实际请求的所以有thread会空闲)userspace不会增加thread,当所有的threads都忙的时候通过BR_SPAWN_LOOPER来告诉用户态此时需要再新建新的thread来处理新的请求。 图中的binder_proc和binder_thread实则为kernel的结构体,为描述方便实体关系放在了user space。binder中的transaction主要是保证收发的依赖。
举个例子 A 的 A1给 B的 B1发请求,此时B回复必须要用B的B1回复。 首先binder_proc 下面有很多的binder_thread每个代表一个线程,要处理的信息既可以放在thread的todo list也可以放在binder_proc的todo list。 假设A的A1(client 发送请求时必然会选择一个线程发送)要给B发信息,此时M(message)会放在proc对应的todo list里面,此时唤醒B中等待的一个线程(假设为B1)去处理这个消息。 所有的A,A1,B,B1的信息都会放在一个binder_transaction在发送发A,A1会被记录,在接受方 B和B1会记录,这样回城的时候B1会找到A1和A。handle的生成是通过binder_type_bind binder_type_handle两个flat_binder_object实现的