sheldon_blogs

Android : 跟我学Binder --- (4) 驱动情景分析


目录:

 

 

 

一、数据结构

  首先基于之前的c程序代码再添加一个goodbye服务,引入以下几个概念:

  • binder_ref
  • binder_node
  • binder_proc
  • binder_thread
  • binder_buffer

 

  1.test_server.c中实现goodbye服务处理函数:

int goodbye_service_handler(struct binder_state *bs,
                   struct binder_transaction_data *txn,
                   struct binder_io *msg,
                   struct binder_io *reply)
{
    /* 根据txn->code知道要调用哪一个函数
     * 如果需要参数, 可以从msg取出
     * 如果要返回结果, 可以把结果放入reply
     */

    /* saygoodbye
     * saygoodbye_to
     */
    
    uint16_t *s;
    char name[512];
    size_t len;
    uint32_t handle;
    uint32_t strict_policy;
    int i;


    // Equivalent to Parcel::enforceInterface(), reading the RPC
    // header with the strict mode policy mask and the interface name.
    // Note that we ignore the strict_policy and don't propagate it
    // further (since we do no outbound RPCs anyway).
    strict_policy = bio_get_uint32(msg);


    switch(txn->code) {
    case GOODBYE_SVR_CMD_SAYGOODBYE:
        saygoodbye();
        bio_put_uint32(reply, 0); /* no exception */
        return 0;

    case GOODBYE_SVR_CMD_SAYGOODBYE_TO:
        /* 从msg里取出字符串(16位转8位) */
        s = bio_get_string16(msg, &len);  //"IGoodbyeService"
        s = bio_get_string16(msg, &len);  // name
        if (s == NULL) {
            return -1;
        }
        for (i = 0; i < len; i++)
            name[i] = s[i];
        name[i] = '\0';

        /* 处理 */
        i = saygoodbye_to(name);

        /* 把结果放入reply */
        bio_put_uint32(reply, 0); /* no exception */
        bio_put_uint32(reply, i);
        
        break;

    default:
        fprintf(stderr, "unknown code %d\n", txn->code);
        return -1;
    }

    return 0;
}

  2.main函数中注册服务:

    /* add service */
    ret = svcmgr_publish(bs, svcmgr, "hello", hello_service_handler);
    if (ret) {
        fprintf(stderr, "failed to publish hello service\n");
        return -1;
    }
    ret = svcmgr_publish(bs, svcmgr, "goodbye", goodbye_service_handler);
    if (ret) {
        fprintf(stderr, "failed to publish goodbye service\n");
    }
......
  
   binder_loop(bs, test_server_handler); //通过函数test_server_handler处理消息

  其中 test_server_handler 会根据binder_transaction_data中的descriptor信息调用对应服务的处理函数:

int test_server_handler(struct binder_state *bs,
                   struct binder_transaction_data *txn,
                   struct binder_io *msg,
                   struct binder_io *reply)
{
    int (*handler)(struct binder_state *bs,
                   struct binder_transaction_data *txn,
                   struct binder_io *msg,
                   struct binder_io *reply);

    handler = (int (*)(struct binder_state *bs,
                   struct binder_transaction_data *txn,
                   struct binder_io *msg,
                   struct binder_io *reply))txn->target.ptr;
    
    return handler(bs, txn, msg, reply); // 根据txn->target.ptr转换成对应的处理函数
}

  3.test_client.c中获取服务并使用:

    /* get service */
    handle = svcmgr_lookup(bs, svcmgr, "goodbye");
    if (!handle) {
        fprintf(stderr, "failed to get goodbye service\n");
        return -1;
    }
    g_goodbye_handle = handle;
    fprintf(stderr, "Handle for goodbye service = %d\n", g_goodbye_handle);
/* use service */
void saygoodbye(void)
{
    unsigned iodata[512/4];
    struct binder_io msg, reply;

    /* 构造binder_io */
    bio_init(&msg, iodata, sizeof(iodata), 4);
    bio_put_uint32(&msg, 0);  // strict mode header
    bio_put_string16_x(&msg, "IGoodbyeService");

    /* 放入参数 */

    /* 调用binder_call */
    if (binder_call(g_bs, &msg, &reply, g_goodbye_handle, GOODBYE_SVR_CMD_SAYGOODBYE))
        return ;
    
    /* 从reply中解析出返回值 */

    binder_done(g_bs, &msg, &reply);
    
}

 

 通过分别对hello和goodbye服务的注册/获取,注册和获取打印的handle值并不对应相同,这就涉及到IPC的概念:①源(自己)、②目的(handle表示)、③数据;

  例如调用binder_call:

    if (binder_call(g_bs, &msg, &reply, g_goodbye_handle, GOODBYE_SVR_CMD_SAYGOODBYE))
        return ;

  其原型为:

/*
*@msg:提供的参数
*@reply:返回的数据
*@target:发送数据的目标(服务的引用)
*@code:调用函数
*/
int binder_call(struct binder_state *bs,
                struct binder_io *msg, struct binder_io *reply,
                uint32_t target, uint32_t code)

 

 binder驱动会根据handle找到目的进程,驱动内部通过 binder_ref 管理对应的引用:

struct binder_ref {
    int debug_id;
    struct rb_node rb_node_desc;
    struct rb_node rb_node_node;
    struct hlist_node node_entry;
    struct binder_proc *proc;
    struct binder_node *node;
    uint32_t desc;
    int strong;
    int weak;
    struct binder_ref_death *death;
};

 binder_ref添加逻辑,如图:

 

  获取binder_ref引用后,进而获取到binder_proc与binder_node信息,之后Client便可有目的的将binder_transaction事务插入到binder_proc的待处理列表,并且,如果进程正在睡眠,就唤起进程,其实这里到底是唤起进程还是线程也有讲究,对于Client向Service发送请求的状况,一般都是唤醒binder_proc上睡眠的线程,binder_nodebinder_proc  结构体定义如下
struct binder_node {
       int debug_id;
       struct binder_work work;
       union {
              struct rb_node rb_node;
              struct hlist_node dead_node;
       };
       struct binder_proc *proc; //对应server进程
       struct hlist_head refs;
       int internal_strong_refs;
       int local_weak_refs;
       int local_strong_refs;
       void __user *ptr;
       void __user *cookie;
       unsigned has_strong_ref:1;
       unsigned pending_strong_ref:1;
       unsigned has_weak_ref:1;
       unsigned pending_weak_ref:1;
       unsigned has_async_transaction:1;
       unsigned accept_fds:1;
       unsigned min_priority:8;
       struct list_head async_todo;
};
struct binder_proc {
       struct hlist_node proc_node;// list node for global binder_procs hlist
       struct rb_root threads; //红黑树保存每个申请服务对象对应的线程
       struct rb_root nodes;
       struct rb_root refs_by_desc;
       struct rb_root refs_by_node;
       ...
};

 

总体流程:

  ①server在内核态(驱动程序中)为每个服务创建binder_node, binder_node.proc = server进程;

  ②server_manager创建binder_ref,引用binder_node, binder_ref.desc = 1、2、3...

   在用户态创建服务链表对应上面的binder_ref(.name / .handle);

  ③client通过name向service_manager查询服务;

  ④service_manager返回handle给驱动程序;

  ⑤驱动程序在service_manager的binder_ref红黑树中根据handle找到binder_ref,

   再根据binder_ref.node找到binder_node,最后给client创建新的binder_ref,驱动返回的desc给client即为handle;

  ⑥client访问server进程:驱动->handle->binder_ref->binder_node->server进程,把数据放入server进程的binder_proc.todo,

   接下来server进程被唤醒,反之server同样类似此流程返回结果给client;

  

 

二、数据交互过程

  • 一般方法:(需要两次拷贝)

    ①client:构造数据 copy_from_user ---> 驱动;

    ②server:驱动 ---> copy_to_user 用户态处理

  • binder方法:(数据一次拷贝,数据头要两次)

    ①server mmp内核的xx内存,可以直接访问;

    ②client构造数据:驱动 ---> copy_from_user 到xx内存 (涉及binder_buffer)

 

  从前面编写的示例程序可知,与binder驱动交互主要是通过ioctl操作:

res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);

  传入的数据为 struct binder_write_read bwr,该结构体中的 write_buffer 指向数据本身,对应结构体如下:

    struct {
        uint32_t cmd;  //头4个字节表示数据类型
        struct binder_transaction_data txn;
    } __attribute__((packed)) writebuf;

 

 

三、服务注册/获取/使用过程

 1.将之前的示例代码仅保留注册hello服务,编译执行log:

 ./service_manager &
[   32.566620] service_manager (1362, 1362), binder_thread_write : BC_ENTER_LOOPER
[   32.566712] service_manager (1362, 1362), binder_thread_read : BR_NOOP
(开始休眠,等待别的进程注册服务...)
.
/test_server & [ 38.320197] test_server (1363, 1363), binder_thread_write : BC_TRANSACTION [ 38.320284] binder: 1363:1363 BC_TRANSACTION 2 -> 1362 - node 1, data beca6a5c-beca6a4c size 96-4 [ 38.320383] test_server (1363, 1363), binder_transaction , print data : [ 38.320454] 0000: 00 . 00 . 00 . 00 . 1a . 00 . 00 . 00 . 61 a 00 . 6e n 00 . 64 d 00 . 72 r 00 . [ 38.329064] 0016: 6f o 00 . 69 i 00 . 64 d 00 . 2e . 00 . 6f o 00 . 73 s 00 . 2e . 00 . 49 I 00 . [ 38.337917] 0032: 53 S 00 . 65 e 00 . 72 r 00 . 76 v 00 . 69 i 00 . 63 c 00 . 65 e 00 . 4d M 00 . [ 38.346771] 0048: 61 a 00 . 6e n 00 . 61 a 00 . 67 g 00 . 65 e 00 . 72 r 00 . 00 . 00 . 00 . 00 . [ 38.355646] 0064: 05 . 00 . 00 . 00 . 68 h 00 . 65 e 00 . 6c l 00 . 6c l 00 . 6f o 00 . 00 . 00 . [ 38.364475] 0080: 85 . 2a * 62 b 73 s 7f . 01 . 00 . 00 . e0 . 88 . 00 . 00 . 00 . 00 . 00 . 00 . [ 38.373350] test_server (1363, 1363), binder_thread_read : BR_NOOP [ 38.379521] service_manager (1362, 1362), binder_thread_read : BR_TRANSACTION [ 38.386633] service_manager (1362, 1362), binder_thread_read , print data : [ 38.393567] 0000: 00 . 00 . 00 . 00 . 1a . 00 . 00 . 00 . 61 a 00 . 6e n 00 . 64 d 00 . 72 r 00 . [ 38.402410] 0016: 6f o 00 . 69 i 00 . 64 d 00 . 2e . 00 . 6f o 00 . 73 s 00 . 2e . 00 . 49 I 00 . [ 38.411263] 0032: 53 S 00 . 65 e 00 . 72 r 00 . 76 v 00 . 69 i 00 . 63 c 00 . 65 e 00 . 4d M 00 . [ 38.420127] 0048: 61 a 00 . 6e n 00 . 61 a 00 . 67 g 00 . 65 e 00 . 72 r 00 . 00 . 00 . 00 . 00 . [ 38.428971] 0064: 05 . 00 . 00 . 00 . 68 h 00 . 65 e 00 . 6c l 00 . 6c l 00 . 6f o 00 . 00 . 00 . [ 38.437824] 0080: 85 . 2a * 68 h 73 s 7f . 01 . 00 . 00 . 01 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . [ 38.447188] test_server (1363, 1363), binder_thread_read : BR_INCREFS [ 38.453114] test_server (1363, 1363), binder_thread_read : BR_ACQUIRE [ 38.459748] test_server (1363, 1363), binder_thread_read : BR_TRANSACTION_COMPLETE [ 38.467270] service_manager (1362, 1362), binder_thread_write : BC_ACQUIRE [ 38.474004] test_server (1363, 1363), binder_thread_read : BR_NOOP [ 38.480122] service_manager (1362, 1362), binder_thread_write : BC_REQUEST_DEATH_NOTIFICATION [ 38.488626] service_manager (1362, 1362), binder_thread_write : BC_FREE_BUFFER [ 38.495828] service_manager (1362, 1362), binder_thread_write : BC_REPLY [ 38.502538] binder: 1362:1362 BC_REPLY 5 -> 1363:1363, data bed9fa4c-bed9fa3c size 4-0 [ 38.510404] service_manager (1362, 1362), binder_transaction , print data : [ 38.517345] 0000: 00 . 00 . 00 . 00 . [ 38.520994] test_server (1363, 1363), binder_thread_read : BR_REPLY [ 38.527250] test_server (1363, 1363), binder_thread_read , print data : [ 38.540003] 0000: 00 . 00 . 00 . 00 . [ 38.543642] service_manager (1362, 1362), binder_thread_read : BR_NOOP [ 38.550253] test_server (1363, 1363), binder_thread_write : BC_FREE_BUFFER [ 38.557063] service_manager (1362, 1362), binder_thread_read : BR_TRANSACTION_COMPLETE [ 38.566481] service_manager (1362, 1362), binder_thread_read : BR_NOOP [ 38.571459] test_server (1363, 1363), binder_thread_write : BC_ENTER_LOOPER [ 38.578368] test_server (1363, 1363), binder_thread_read : BR_NOOP svcmgr: add_service('hello'), handle = 1

 

2.根据以上log分析源码:

service_manager.c:(调用顺序如下)
① bs->fd = open("/dev/binder", O_RDWR);   (binder_proc指向当前进程)
② ioctl(bs->fd, BINDER_VERSION, &vers)
③ bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
④ ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
⑤ binder_thread_write : BC_ENTER_LOOPER
⑥ binder_thread_read : BR_NOOP
⑦ 开始休眠,等待其他进程来注册服务......
⑧ binder_thread_read : BR_TRANSACTION
⑨ binder_thread_write : BC_ACQUIRE
⑩ binder_thread_write : BC_REQUEST_DEATH_NOTIFICATION
 binder_thread_write : BC_FREE_BUFFER
 inder_thread_write : BC_REPLY
 binder_thread_read : BR_NOOP
 binder_thread_read : BR_TRANSACTION_COMPLETE
 binder_thread_read : BR_NOOP

   

test_server.c:
① open("/dev/binder", O_RDWR);
② ioctl(bs->fd, BINDER_VERSION, &vers)
③ mmap
④ binder_thread_write : BC_TRANSACTION
⑤ binder_thread_read : BR_NOOP
⑥ binder_thread_read : BR_INCREFS
⑦ binder_thread_read : BR_ACQUIRE
⑧ binder_thread_read : BR_TRANSACTION_COMPLETE
⑨ binder_thread_read : BR_NOOP
⑩ binder_thread_read : BR_REPLY
 binder_thread_write : BC_FREE_BUFFER
 binder_thread_write : BC_ENTER_LOOPER
 binder_thread_read : BR_NOOP

 

    

2.1 Binder 协议

  Binder协议基本格式是(命令+数据),使用ioctl(fd, cmd, 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{
signed long write_size;
signed long write_consumed;
unsigned long write_buffer;
signed long read_size;
signed long read_consumed;
unsigned long read_buffer;
};
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驱动的版本号。

--

 

2.2 BINDER_WRITE_READ 之写操作:
  Binder写操作的数据时格式同样也是(命令+数据)。这时候命令和数据都存放在binder_write_read 结构write_buffer域指向的内存空间里,多条命令可以连续存放。数据紧接着存放在命令后面,格式根据命令不同而不同。

    下表列举了Binder写操作支持的命令:

cmd 含义 arg

BC_TRANSACTION
BC_REPLY

BC_TRANSACTION用于Client向Server发送请求数据;BC_REPLY用于Server向Client发送回复(应答)数据。其后面紧接着一个binder_transaction_data结构体表明要写入的数据。 struct binder_transaction_data

BC_FREE_BUFFER
BC_ATTEMPT_ACQUIRE

--

--
BC_FREE_BUFFER 释放一块映射的内存。Binder接收方通过mmap()映射一块较大的内存空间,Binder驱动基于这片内存采用最佳匹配算法实现接收数据缓存的动态分配和释放,满足并发请求对接收缓存区的需求。应用程序处理完这片数据后必须尽快使用该命令释放缓存区,否则会因为缓存区耗尽而无法接收新数据。 指向需要释放的缓存区的指针;该指针位于收到的Binder数据包中
BC_INCREFS
BC_ACQUIRE
BC_RELEASE
BC_DECREFS
这组命令增加或减少Binder的引用计数,用以实现强指针或弱指针的功能。 32位Binder引用号
BC_INCREFS_DONE
BC_ACQUIRE_DONE
第一次增加Binder实体引用计数时,驱动向Binder实体所在的进程发送BR_INCREFS, BR_ACQUIRE消息;Binder实体所在的进程处理完毕回馈BC_INCREFS_DONE,BC_ACQUIRE_DONE

void *ptr:Binder实体在用户空间中的指针

void *cookie:与该实体相关的附加数据

BC_REGISTER_LOOPER
BC_ENTER_LOOPER
BC_EXIT_LOOPER

这组命令同BINDER_SET_MAX_THREADS一道实现Binder驱动对接收方线程池管理。BC_REGISTER_LOOPER通知驱动线程池中一个线程已经创建了;BC_ENTER_LOOPER通知驱动该线程已经进入主循环,可以接收数据;BC_EXIT_LOOPER通知驱动该线程退出主循环,不再接收数据。

--
BC_REQUEST_DEATH_NOTIFICATION 获得Binder引用的进程通过该命令要求驱动在Binder实体销毁得到通知。虽说强指针可以确保只要有引用就不会销毁实体,但这毕竟是个跨进程的引用,谁也无法保证实体由于所在的Server关闭Binder驱动或异常退出而消失,引用者能做的是要求Server在此刻给出通知。

uint32 *ptr; 需要得到死亡通知的Binder引用

void **cookie: 与死亡通知相关的信息,驱动会在发出死亡通知时返回给发出请求的进程。

BC_DEAD_BINDER_DONE 收到实体死亡通知书的进程在删除引用后用本命令告知驱动。 void **cookie

 

  在这些命令中,最常用的是BC_TRANSACTION/BC_REPLY命令对,Binder请求和应答数据就是通过这对命令发送给接收方。这对命令所承载的数据包由结构体struct binder_transaction_data定义。Binder交互有同步和异步之分,利用binder_transaction_data中flag域区分。如果flag域的TF_ONE_WAY位为1则为异步交互,即Client端发送完请求交互即结束, Server端不再返回BC_REPLY数据包;否则Server会返回BC_REPLY数据包,Client端必须等待接收完该数据包方才完成一次交互。

2.3 BINDER_WRITE_READ :从Binder读出数据

  从Binder里读出的数据格式和向Binder中写入的数据格式一样,采用(消息ID+数据)形式,并且多条消息可以连续存放。

  下表列举了从Binder读出的命令字及其相应的参数:

消息 含义 参数
BR_ERROR 发生内部错误(如内存分配失败) --
BR_OK
BR_NOOP
操作完成 --
BR_SPAWN_LOOPER 该消息用于接收方线程池管理。当驱动发现接收方所有线程都处于忙碌状态且线程池里的线程总数没有超过BINDER_SET_MAX_THREADS设置的最大线程数时,向接收方发送该命令要求创建更多线程以备接收数据。 --
BR_TRANSACTION
BR_REPLY
这两条消息分别对应发送方的BC_TRANSACTION和BC_REPLY,表示当前接收的数据是请求还是回复。 binder_transaction_data
BR_ACQUIRE_RESULT
BR_ATTEMPT_ACQUIRE
BR_FINISHED
-- --
BR_DEAD_REPLY 交互过程中如果发现对方进程或线程已经死亡则返回该消息 --
BR_TRANSACTION_COMPLETE     

发送方通过BC_TRANSACTION或BC_REPLY发送完一个数据包后,都能收到该消息做为成功发送的反馈。这和BR_REPLY不一样,是驱动告知发送方已经发送成功,而不是Server端返回请求数据。所以不管同步还是异步交互接收方都能获得本消息。

--
BR_INCREFS
BR_ACQUIRE
BR_RELEASE
BR_DECREFS
这一组消息用于管理强/弱指针的引用计数。只有提供Binder实体的进程才能收到这组消息。

void *ptr:Binder实体在用户空间中的指针

void *cookie:与该实体相关的附加数据

BR_DEAD_BINDER
BR_CLEAR_DEATH_NOTIFICATION_DONE
向获得Binder引用的进程发送Binder实体死亡通知书;收到死亡通知书的进程接下来会返回BC_DEAD_BINDER_DONE做确认。 void **cookie:在使用BC_REQUEST_DEATH_NOTIFICATION注册死亡通知时的附加参数。
BR_FAILED_REPLY

如果发送非法引用号则返回该消息

--

 

注:其中只有 BC_TRANSACTION、BC_REPLY、BR_TRANSACTION 、BR_REPLY 涉及两个进程,其它所有cmd只是APP和驱动的交互,用于改变或报告状态。

  

 

四、transaction_stack机制

  前面大致了解到两个进程A、B通信流程如下:

  

 

  1.client端发送数据给server进程时handle只表明了对应的进程,而server是多线程的,同时有多个binder_thread,所以分两种情况:

   (1)发送的数据一般放在进程的binder_proc.todo列表,唤醒等待于binder_proc.wait的空闲线程。

   (2)通过transaction_stack来判断为双向传输,则发送的数据放在binder_thread.todo列表,然后唤醒该线程。

 

    2.server端回复数据时,如果无handle表明目的进程,则通过传输栈transaction_stack保存的“发送者”信息回复,而信息则是在transaction_stack结构体中 .frome 、.to_proc、.to_thread保存,然后通过同一个 binder_transaction 的 .from_parent 放入发送者的栈,通过 .to_parent 放入接收者的栈。

 

Binder Driver总结:

1.  Binder node:

      前面说过Service 其实是一个存在于某个进程里的对象,因此,进程PID 和 对象地址可以唯一的标识一个Service 对象,除此之外,因为这个对象可能被很多应用所使用,必须有引用计数来管理他的生命周期。这些工作都必须在内核里完成,Binder node 就是这样一个结构体来管理每个Service 对象。

struct binder_node {
    int debug_id;              //kernel内部标识node的id
    struct binder_work work;   
    union {
        struct rb_node rb_node;
        struct hlist_node dead_node;
    };
    struct binder_proc *proc;  //Service所在进程的结构体
    struct hlist_head refs;    //双向链表头,链表里存放一系列指针,指向引用该Service的binder_ref对象,
    int internal_strong_refs;  //内部强引用计数
    int local_weak_refs;       //弱引用计数
    int local_strong_refs;     //强引用计数
    binder_ptr __user ptr;     //Service对象地址
    binder_ptr __user cookie;  
    unsigned has_strong_ref:1; 
    unsigned pending_strong_ref:1; 
    unsigned has_weak_ref:1;
    unsigned pending_weak_ref:1;
    unsigned has_async_transaction:1;
    unsigned accept_fds:1;
    unsigned min_priority:8;
    struct list_head async_todo;
};

 

 2. binder_ref

  binder_ref 描述了每个对服务对象的引用,对应与Client端。如上图所示,每个Ref通过node指向binder_node. 一个进程所有的binder_ref通过两个红黑树(RbTree)进行管理,通过binder_get_ref() 和 binder_get_ref_for_node快速查找。

struct binder_ref {
    /* Lookups needed: */
    /*   node + proc => ref (transaction) */
    /*   desc + proc => ref (transaction, inc/dec ref) */
    /*   node => refs + procs (proc exit) */
    int debug_id;
    struct rb_node rb_node_desc; 
    struct rb_node rb_node_node;
    struct hlist_node node_entry;   
    struct binder_proc *proc;           //应用进程
    struct binder_node *node;
    uint32_t desc;
    int strong;
    int weak;
    struct binder_ref_death *death;  //如果不为空,则client想获知binder的死亡
};

 

 3. binder_proc

   一个进程既包含的Service对象,也可能包含对其他Service对象的引用. 如果作为Service对象进程,它可能会存在多个Binder_Thread,这些信息都在binder_proc结构体进行管理。

struct binder_proc {
    struct hlist_node proc_node; //全局链表 binder_procs 的node之一
    struct rb_root threads; //binder_thread红黑树,存放指针,指向进程所有的binder_thread, 用于Server端
    struct rb_root nodes;   //binder_node红黑树,存放指针,指向进程所有的binder 对象
    struct rb_root refs_by_desc; //binder_ref 红黑树,根据desc(service No) 查找对应的引用
    struct rb_root refs_by_node; //binder_ref 红黑树,根据binder_node 指针查找对应的引用
    int pid;
    struct vm_area_struct *vma;
    struct mm_struct *vma_vm_mm;
    struct task_struct *tsk;
    struct files_struct *files;
    struct hlist_node deferred_work_node;
    int deferred_work;
    void *buffer;
    ptrdiff_t user_buffer_offset;

    struct list_head buffers;
    struct rb_root free_buffers;
    struct rb_root allocated_buffers;
    size_t free_async_space;

    struct page **pages;
    size_t buffer_size;
    uint32_t buffer_free;
    struct list_head todo; //task_list, binder_work链表,存放指针最终指向某个binder_transaction对象
    wait_queue_head_t wait;
    struct binder_stats stats;
    struct list_head delivered_death;
    int max_threads;
    int requested_threads;
    int requested_threads_started;
    int ready_threads;
    long default_priority;
    struct dentry *debugfs_entry;
};

 

4. binder_transaction

  每个transact() 调用在内核里都会生产一个binder_transaction 对象,这个对象会最终送到Service进程或线程的todo队列里,然后唤醒他们来最终完成onTransact()调用。

struct binder_transaction {
    int debug_id;             //一个全局唯一的ID
    struct binder_work work; // 用于存放在todo链表里
    struct binder_thread *from; //transaction 发起的线程。如果BC_TRANSACTION, 则为客户端线程,如果是BC_REPLY, 则为服务端线程。
    struct binder_transaction *from_parent; //上一个binder_transaction. 用于client端
    struct binder_proc *to_proc; //目标进程
    struct binder_thread *to_thread; //目标线程
    struct binder_transaction *to_parent; //上一个binder_transaction, 用于server端
    unsigned need_reply:1;
    /* unsigned is_dead:1; */    /* not used at the moment */

    struct binder_buffer *buffer;
    unsigned int    code;
    unsigned int    flags;
    long    priority;
    long    saved_priority;
    kuid_t    sender_euid;
};

 

5. binder_thread

  binder_proc里的threads 红黑树存放着指向binder_thread对象的指针。这里的binder_thread 不仅仅包括service的binder thread, 也包括访问其他service的调用thread. 也就是说所有与binder相关的线程都会在binder_proc的threads红黑树里留下记录。binder_thread里最重要的两个成员变量是 transaction_stack 和 wait.

struct binder_thread {
    struct binder_proc *proc;
    struct rb_node rb_node; //红黑树节点
    int pid;
    int looper;  //
    struct binder_transaction *transaction_stack; //transaction栈
    struct list_head todo;
    uint32_t return_error; 
    uint32_t return_error2; 
    wait_queue_head_t wait; //等待队列,用于阻塞等待
    struct binder_stats stats;
};

  在binder_proc里面我们也能看到一个wait 队列,是不是意味着线程既可以在proc->wait上等待,也可以在thread->wait上等待?binder driver 对此有明确的用法,所有的binder threads (server 端)都等待在proc->wait上。因为对于服务端来说,用哪个thread来响应远程调用请求都是一样的。然而所有的ref thread(client端)的返回等待都发生在调用thread的wait 队列,因为,当某个binder thread 完成服务请求后,他必须唤醒特定的等待返回的线程。但是有一个例外,在双向调用的情况下,某个Server端的thread将会挂在thread->wait上等待,而不是proc->wait. 举个例子,假设两个进程P1 和 P2,各自运行了一个Service, S1,S2, P1 在 thread T1 里调用S2提供的服务,然后在T1->wait里等待返回。S2的服务在P2的binder thread(T2)里执行,执行过程中,S2又调到S1里的某个接口,按理S1 将在P1的binder thread T3里执行, 如果P1接下来又调到了P2,那又会产生新的进程 T4, 如果这个反复调用栈很深,需要耗费大量的线程,显然这是非常不高效的设计。所以,binder driver 里做了特殊的处理。当T2 调用 S1的接口函数时,binder driver 会遍历T2的transaction_stack, 如果发现这是一个双向调用(binder_transaction->from->proc 等于P1), 便会唤醒正在等待reply的T1,T1 完成这个请求后,继续等待S2的回复。这样,只需要最多两个Thread就可以完成多层的双向调用。

  binder_thread里的transaction_stack 是用链表实现的堆栈, 调用线程和服务线程的transaction有着不同的堆栈。下图是上面这个例子的堆栈情形:

 

6. binder_ref_death

  binder_ref 记录了从client进程到server进程某个service的引用,binder_ref_death 是binder_ref的一个成员变量,它的不为空说明了client进程想得到这个service的死亡通知(严格意义上讲,是service所在进程的死亡通知,因为一个进程一个/dev/binder的fd, 只有进程死亡了,driver才会知晓,通过 file_operations->release 接口)。

struct binder_ref_death {
    struct binder_work work;
    binder_ptr __user cookie;
};

一张时序图来了解binder death notifycation 的全过程:

 

7. binder_work

  从应用程序角度来看,所有的binder调用都是同步的。但在binder driver 内部,两个进程间的交互都是异步的,一个进程产生的请求会变成一个binder_work, 并送入目标进程或线程的todo 队列里,然后唤醒目标进程和线程来完成这个请求,并阻塞等待结果。binder_work的定义如下:

struct binder_work {
    struct list_head entry;
    enum {
        BINDER_WORK_TRANSACTION = 1,
        BINDER_WORK_TRANSACTION_COMPLETE,
        BINDER_WORK_NODE,
        BINDER_WORK_DEAD_BINDER,
        BINDER_WORK_DEAD_BINDER_AND_CLEAR,
        BINDER_WORK_CLEAR_DEATH_NOTIFICATION,
    } type;
};

 

8. binder_buffer

  进程间通信除了命令,还有参数和返回值的交换,要将数据从一个进程的地址空间,传到另外一个进程的地址空间,通常需要两次拷贝,进程A -> 内核 -> 进程B。binder_buffer 就是内核里存放交换数据的空间(这些数据是以Parcel的形式存在)。为了提高效率,Android 的 binder 只需要一次拷贝,因为binder 进程通过mmap将内核空间地址映射到用户空间,从而可以直接访问binder_buffer的内容而无需一次额外拷贝。binder_buffer由内核在每次发起的binder调用创建,并赋给binder_transaction->buffer. binder driver 根据binder_transaction 生产 transaction_data(包含buffer的指针而非内容), 并将其复制到用户空间。

 

9. flat_binder_obj

前面我们说过,<proc, handle> 可以标识一个BpBinder 对象,而<proc, ptr> 可以标识一个BBinder对象。Binder Driver 会收到来自与BpBinder 和 BBinder的系统调用,它是如何判别它们的身份呢?答案就在flat_binder_obj里,先看看它的定义,

struct flat_binder_object {
 unsigned long type;  //见下面定义
 unsigned long flags;
 union {
 void *binder;            //BBinder,通过它driver可以找到对应的node
 signed long handle; //BpBinder,根据它driver可以找到对应的ref
 };
 void *cookie;
};

enum {
 BINDER_TYPE_BINDER = B_PACK_CHARS('s', 'b', '*', B_TYPE_LARGE),
 BINDER_TYPE_WEAK_BINDER = B_PACK_CHARS('w', 'b', '*', B_TYPE_LARGE),
 BINDER_TYPE_HANDLE = B_PACK_CHARS('s', 'h', '*', B_TYPE_LARGE),
 BINDER_TYPE_WEAK_HANDLE = B_PACK_CHARS('w', 'h', '*', B_TYPE_LARGE),
 BINDER_TYPE_FD = B_PACK_CHARS('f', 'd', '*', B_TYPE_LARGE),
};

union表明了在Server端和Client端它有着不同的解读。type则表明了它的身份。binder driver 根据它可以找到BpBinder 和 BBinder 在内核中相对应的对象 (ref 或 node). flat_binder_obj 封装在parcel里,详见Parcel.cpp.

 

流程总结:

1.  当一个service向binder driver 注册时(通过flat_binder_object), driver 会创建一个binder_node, 并挂载到该service所在进程的nodes红黑树。

2.  这个service的binder线程在proc->wait 队列上进入睡眠等待。等待一个binder_work的到来。

3.  客户端的BpBinder 创建的时候,它在driver内部也产生了一个binder_ref对象,并指向某个binder_node, 在driver内部,将client和server关联起来。如果它需要获知Service的死亡状态,则会生成相应的binfer_ref_death

4.  客户端通过transact() (对应内核命令BC_TRANSACTION)请求远端服务,driver通过ref->node的映射,找到service所在进程,生产一个binder_buffer, binder_transactionbinder_work 并插入proc->todo队列,接着唤醒某个睡在proc->wait队列上的Binder_thread. 与此同时,该客户端线程在其线程的wait队列上进入睡眠,等待返回值。

5.  这个binder thread 从proc->todo 队列中读出一个binder_transaction, 封装成transaction_data (命令为 BR_TRANSACTION) 并送到用户空间。Binder用户线程唤醒并最终执行对应的on_transact() 函数。

6.  Binder用户线程通过transact() 向内核发送 BC_REPLY命令,driver收到后从其thread->transaction_stack中找到对应的binder_transaction, 从而知道是哪个客户端线程正在等待这个返回。

7.  Driver 生产新的binder_transaction (命令 BR_REPLY), binder_buffer, binder_work, 将其插入应用线程的todo对立,并将该线程唤醒。

8.  客户端的用户线程收到回复数据,该Transaction完成。

9.  当service所在进程发生异常退出,driver 的 release函数被调到,在某位内核work_queue 线程里完成该service在内核态的清理工作(thread,buffer,node,work...), 并找到所有引用它的binder_ref, 如果某个binder_ref 有不为空的binder_ref_death, 生成新的binder_work, 送人其线程的todo 对立,唤醒它来执行剩余工作,用户端的DeathRecipient 会最终被调用来完成client端的清理工作。

下面这张时序图描述了上述一个transaction完成的过程。不同的颜色代表不同的线程。注意的是,虽然Kernel和User space 线程的颜色是不一样的,但所有的系统调用都发生在用户进程的上下文里(所谓上下文,就是Kernel能通过某种方式找到关联的进程(通过Kernel的current 宏),并完成进程相关的操作,比如说唤醒某个睡眠的线程,或跟用户空间交换数据,copy_from, copy_to, 与之相对应的是中断上下文,其完全异步触发,因此无法做任何与进程相关的操作,比如说睡眠,锁等)。

 

 

 -end-

posted on 2019-01-27 15:43  sheldon_blogs  阅读(1454)  评论(0编辑  收藏  举报

导航