Android Binder设计(四)

下表总结了当flat_binder_object结构穿过驱动时驱动所做的操作:
    驱动对flat_binder_object的操作

    Binder 类型( type 域)  在发送方的操作  在接收方的操作  
    BINDER_TYPE_BINDER

       BINDER_TYPE_WEAK_BINDER
       只有实体所在的进程能发送该类型的Binder。如果是第一次发送驱动将创建实体在内核中的节点,并保存binder,cookie,flag域。 如果是第一次接收该Binder则创建实体在内核中的引用;将handle域替换为新建的引用号;将type域替换为 BINDER_TYPE_(WEAK_)HANDLE
BINDER_TYPE_HANDLE

       BINDER_TYPE_WEAK_HANDLE
       获得Binder引用的进程都能发送该类型Binder。驱动根据handle域提供的引用号查找建立在内核的引用。如果找到说明引用号合法,否则 拒绝该发送请求。 如果收到的Binder实体位于接收进程中:将ptr域替换为保存在节点中的binder值;cookie替换为保存在节点中的cookie 值;type替换为BINDER_TYPE_(WEAK_)BINDER。

       如果收到的Binder实体不在接收进程中:如果是第一次接收则创建实体在内核中的引用;将handle域替换为新建的引用号

       BINDER_TYPE_FD 验证handle域中提供的打开文件号是否有效,无效则拒绝该发送请求。 在接收方创建新的打开文件号并将其与提供的打开文件描述结构绑定。

       5.2.1 文件形式的 Binder
       除了通常意义上用来通信的Binder,还有一种特殊的Binder:文件Binder。这种Binder的基本思想是:将文件看成Binder实 体,进程打开的文件号看成Binder的引用。一个进程可以将它打开文件的文件号传递给另一个进程,从而另一个进程也打开了同一个文件,就象Binder 的引用在进程之间传递一样。

       一个进程打开一个文件,就获得与该文件绑定的打开文件号。从Binder的角度,linux在内核创建的打开文件描述结构struct file是Binder的实体,打开文件号是该进程对该实体的引用。既然是Binder那么就可以在进程之间传递,故也可以用 flat_binder_object结构将文件Binder通过数据包发送至其它进程,只是结构中type域的值为BINDER_TYPE_FD,表明 该Binder是文件Binder。而结构中的handle域则存放文件在发送方进程中的打开文件号。我们知道打开文件号是个局限于某个进程的值,一旦跨 进程就没有意义了。这一点和Binder实体用户指针或Binder引用号是一样的,若要跨进程同样需要驱动做转换。驱动在接收Binder的进程空间创 建一个新的打开文件号,将它与已有的打开文件描述结构struct file勾连上,从此该Binder实体又多了一个引用。新建的打开文件号覆盖flat_binder_object中原来的文件号交给接收进程。接收进 程利用它可以执行read(),write()等文件操作。

       传个文件为啥要这么麻烦,直接将文件名用Binder传过去,接收方用open()打开不就行了吗?其实这还是有区别的。首先对同一个打开文件共享 的层次不同:使用文件Binder打开的文件共享linux VFS中的struct file,struct dentry,struct inode结构,这意味着一个进程使用read()/write()/seek()改变了文件指针另一个进程的文件指针也会改变;而如果两个进程分别使用 文件名打开同一文件则有各自的struct file结构,从而各自独立维护文件指针,互不干扰。其次是一些特殊设备文件要求在struct file一级共享才能使用,例如android的另一个驱动ashmem,它和Binder一样也是misc设备,用以实现进程间的共享内存。一个进程打 开的ashmem文件只有通过文件Binder发送到另一个进程才能实现内存共享,这大大提高了内存共享的安全性,道理和Binder增强了IPC的安全 性是一样的。

       5.3 Binder 在驱动中的表述
       驱动是Binder通信的核心,系统中所有的Binder实体以及每个实体在各个进程中的引用都登记在驱动中;驱动需要记录Binder引用 ->实体之间多对一的关系;为引用找到对应的实体;在某个进程中为实体创建或查找到对应的引用;记录Binder的归属地(位于哪个进程中);通过 管理Binder的强/弱引用创建/销毁Binder实体等等。

        驱动里的Binder是什么时候创建的呢?前面提到过,为了实现实名Binder的注册,系统必须创建第一只鸡 – 为SMgr创建的,注册实名Binder专用的Binder实体,负责实名Binder注册过程中的进程间通信。既然创建了实体也要有对应的引用:驱动将 所有进程中的0号引用都预留给该Binder实体,即一开始所有进程的0号引用都指注册实名Binder专用的Binder,无须特殊操作任何进程通过0 号引用都可以注册实名Binder。接下来随着应用程序通过不断地注册实名Binder,不断向SMgr索要Binder的引用,不断将Binder从一 个进程传递给另一个进程,越来越多的Binder以传输结构 – flat_binder_object的形式穿越驱动做跨进程的迁徙。由于binder_transaction_data中data.offset数组 的存在,所有流经驱动的Binder都逃不过驱动的眼睛。Binder将对每个穿越进程边界的Binder做如下操作:检查传输结构的type域,如果是 BINDER_TYPE_BINDER或BINDER_TYPE_WEAK_BINDER则创建Binder的实体;如果是 BINDER_TYPE_HANDLE或BINDER_TYPE_WEAK_HANDLE则创建Binder的引用;如果是 BINDER_TYPE_HANDLE则为进程打开文件,无须创建任何数据结构。详细过程可参考表7。随着越来越多的Binder实体或引用穿过驱动在进 程间传递,驱动会在内核里创建越来越多的节点或引用,当然这个过程对用户来说是透明的。

       5.3.1 Binder 实体在驱动中的表述
       驱动中的Binder实体也叫‘节点’,隶属于提供实体的进程,由struct binder_node结构来表示:

Binder节点描述结构:binder_node
成员含义
int debug_id;用于调试
struct binder_work work;当本节点引用计数发生改变,需要通知所属进程时,通过该成员挂入所属进程的to-do队列里,唤醒所属进程执行Binder实体引用计数的修改。
union { struct rb_node rb_node;struct hlist_node dead_node;};每个进程都维护一棵红黑树,以Binder实体在用户空间的指针,即本结构的ptr成员为索引存放该进程所有的Binder实体。这样驱动可以根据 Binder实体在用户空间的指针很快找到其位于内核的节点。rb_node用于将本节点链入该红黑树中。 销毁节点时须将rb_node从红黑树中摘除,但如果本节点还有引用没有切断,就用dead_node将节点隔离到另一个链表中,直到通知所有进程 切断与该节点的引用后,该节点才可能被销毁。
struct binder_proc *proc;本成员指向节点所属的进程,即提供该节点的进程。
struct hlist_head refs;本成员是队列头,所有指向本节点的引用都链接在该队列里。这些引用可能隶属于不同的进程。通过该队列可以遍历指向该节点的所有引用。
int internal_strong_refs;用以实现强指针的计数器:产生一个指向本节点的强引用该计数就会加1。
int local_weak_refs;驱动为传输中的Binder设置的弱引用计数。如果一个Binder打包在数据包中从一个进程发送到另一个进程,驱动会为该Binder增加引用计 数,直到接收进程通过BC_FREE_BUFFER通知驱动释放该数据包的数据区为止。
int local_strong_refs;驱动为传输中的Binder设置的强引用计数。同上。
void __user *ptr;指向用户空间Binder实体的指针,来自于flat_binder_object的binder成员
void __user *cookie;指向用户空间的附加指针,来自于flat_binder_object的cookie成员
unsigned has_strong_ref; unsigned pending_strong_ref;unsigned has_weak_ref;unsigned pending_weak_ref这一组标志用于控制驱动与Binder实体所在进程交互式修改引用计数
unsigned has_async_transaction;该成员表明该节点在to-do队列中有异步交互尚未完成。驱动将所有发送往接收端的数据包暂存在接收进程或线程开辟的to-do队列里。对于异步交 互,驱动做了适当流控:如果to-do队列里有异步交互尚待处理则该成员置1,这将导致新到的异步交互存放在本结构成员 – asynch_todo队列中,而不直接送到to-do队列里。目的是为同步交互让路,避免长时间阻塞发送端。
unsigned accept_fds表明节点是否同意接受文件方式的Binder,来自flat_binder_object中flags成员的 FLAT_BINDER_FLAG_ACCEPTS_FDS位。由于接收文件Binder会为进程自动打开一个文件,占用有限的文件描述符,节点可以设置 该位拒绝这种行为。
int min_priority设置处理Binder请求的线程的最低优先级。发送线程将数据提交给接收线程处理时,驱动会将发送线程的优先级也赋予接收线程,使得数据即使跨了进 程也能以同样优先级得到处理。不过如果发送线程优先级过低,接收线程将以预设的最小值运行。 该域的值来自于flat_binder_object中flags成员。
struct list_head async_todo异步交互等待队列;用于分流发往本节点的异步交互包

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值