binder(五) client获取服务

2021-06-10  本文已影响0人  曾大稳丶

应用层主要逻辑

//1. 打开驱动,mmap映射
binder_open
//2. 构造binder_write_read通过ioctl和驱动交互通过name获取到server的handle
 ioctl(bs->fd, BINDER_WRITE_READ, &bwr)

//3. 拿到数据
handle =bio_get_ref(&reply);

  1. 打开驱动,mmap映射,binder_open,和servicemanager启动一样流程
  1. 构造binder_write_read通过ioctl和驱动交互通过servicemanager获取对应服务的handle,svcmgr_lookup , binder_call
uint32_t svcmgr_lookup(struct binder_state *bs, uint32_t target, const char *name)
{
    
    uint32_t handle;
    unsigned iodata[512/4];
    struct binder_io msg, reply;
    //构造bio_init
    bio_init(&msg, iodata, sizeof(iodata), 4);
    //header
    bio_put_uint32(&msg, 0);  // strict mode header
    // 设置参数
    bio_put_string16_x(&msg, SVC_MGR_NAME);
    bio_put_string16_x(&msg, name);

    if (binder_call(bs, &msg, &reply, target, SVC_MGR_CHECK_SERVICE))
        return 0;
    //获取到了handle
    handle = bio_get_ref(&reply);

    if (handle)
        //发送acquire
        binder_acquire(bs, handle);
    //结束
    binder_done(bs, &msg, &reply);

    return handle;
}

binder_call流程和server注册基本一致,会走到server_manager的用户空间处理函数svcmgr_handler

int svcmgr_handler(struct binder_state *bs,
                   struct binder_transaction_data *txn,
                   struct binder_io *msg,
                   struct binder_io *reply)
{
        //...
     struct svcinfo *si;
     uint32_t handle;
     switch(txn->code) {
        case SVC_MGR_GET_SERVICE:
        case SVC_MGR_CHECK_SERVICE:
            s = bio_get_string16(msg, &len);
            if (s == NULL) {
                return -1;
            }
            handle = do_find_service(bs, s, len, txn->sender_euid, txn->sender_pid);
            if (!handle)
                break;
            //把handle put到reply里面-->2.1
            bio_put_ref(reply, handle);
            return 0;
         
         }
        //...

}

//找到service的handle
uint32_t do_find_service(struct binder_state *bs, const uint16_t *s, size_t len, uid_t uid, pid_t spid)
{
    struct svcinfo *si;
    si = find_svc(s, len);
    //ALOGI("check_service('%s') handle = %x\n", str8(s, len), si ? si->handle : 0);
    if (si && si->handle) {
       //...
        return si->handle;
    } else {
        return 0;
    }
}


//找到name找到svcinfo
struct svcinfo *find_svc(const uint16_t *s16, size_t len)
{
    struct svcinfo *si;

    for (si = svclist; si; si = si->next) {
        if ((len == si->len) &&
            !memcmp(s16, si->name, len * sizeof(uint16_t))) {
            return si;
        }
    }
    return NULL;
}


2.1 bio_put_ref主要作用将对应namehandle写入reply

void bio_put_ref(struct binder_io *bio, uint32_t handle)
{
    struct flat_binder_object *obj;

    if (handle)
        obj = bio_alloc_obj(bio);
    else
        obj = bio_alloc(bio, sizeof(*obj));

    if (!obj)
        return;

    obj->flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
    //写入类型
    obj->type = BINDER_TYPE_HANDLE;
    //写入handle
    obj->handle = handle;
    obj->cookie = 0;
}

经过service_managerlooper parser处理之后,会调用binder_send_reply
回执数据


nt binder_parse(struct binder_state *bs, struct binder_io *bio,
                 uintptr_t ptr, size_t size, binder_handler func)
{
    //...
    switch(cmd) {
        //...
        case BR_TRANSACTION: {
            struct binder_transaction_data *txn = (struct binder_transaction_data *) ptr;
            if ((end - ptr) < sizeof(*txn)) {
                ALOGE("parse: txn too small!\n");
                return -1;
            }
            binder_dump_txn(txn);
            if (func) {
                unsigned rdata[256/4];
                struct binder_io msg;
                struct binder_io reply;
                int res;

                bio_init(&reply, rdata, sizeof(rdata), 4);
                bio_init_from_txn(&msg, txn);
                res = func(bs, txn, &msg, &reply);
                //数据回传 见2.2
                binder_send_reply(bs, &reply, txn->data.ptr.buffer, res);
            }
            ptr += sizeof(*txn);
            break;
        }
    }

}

2.2 binder_send_reply

void binder_send_reply(struct binder_state *bs,
                       struct binder_io *reply,
                       binder_uintptr_t buffer_to_free,
                       int status)
{
    struct {
        uint32_t cmd_free;
        binder_uintptr_t buffer;
        uint32_t cmd_reply;
        struct binder_transaction_data txn;
    } __attribute__((packed)) data;
    
    //free命令
    data.cmd_free = BC_FREE_BUFFER;
    data.buffer = buffer_to_free;
    //reply命令
    data.cmd_reply = BC_REPLY;
    data.txn.target.ptr = 0;
    data.txn.cookie = 0;
    data.txn.code = 0;
    //status是0表示没有错误
    if (status) {
        data.txn.flags = TF_STATUS_CODE;
        data.txn.data_size = sizeof(int);
        data.txn.offsets_size = 0;
        data.txn.data.ptr.buffer = (uintptr_t)&status;
        data.txn.data.ptr.offsets = 0;
    } else {
        //走这里
        //设置相关数据,handle在reply->data0里面
        data.txn.flags = 0;
        data.txn.data_size = reply->data - reply->data0;
        data.txn.offsets_size = ((char*) reply->offs) - ((char*) reply->offs0);
        data.txn.data.ptr.buffer = (uintptr_t)reply->data0;
        data.txn.data.ptr.offsets = (uintptr_t)reply->offs0;
    }
    //binder交互
    binder_write(bs, &data, sizeof(data));
}


int binder_write(struct binder_state *bs, void *data, size_t len)
{
    struct binder_write_read bwr;
    int res;

    bwr.write_size = len;
    bwr.write_consumed = 0;
    bwr.write_buffer = (uintptr_t) data;
    bwr.read_size = 0;
    bwr.read_consumed = 0;
    bwr.read_buffer = 0;
    //交互
    res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
    if (res < 0) {
        fprintf(stderr,"binder_write: ioctl failed (%s)\n",
                strerror(errno));
    }
    return res;
}


最终组装数据进入驱动binder_thread_write

static int binder_thread_write(struct binder_proc *proc,
            struct binder_thread *thread,
            binder_uintptr_t binder_buffer, size_t size,
            binder_size_t *consumed)
{
    uint32_t cmd;               
    void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
    void __user *ptr = buffer + *consumed;
    void __user *end = buffer + size;           
    while (ptr < end && thread->return_error == BR_OK) {
        if (get_user(cmd, (uint32_t __user *)ptr))
            return -EFAULT;
        ptr += sizeof(uint32_t);    
        
        //...
        
        switch (cmd) {
            //...
            case BC_FREE_BUFFER: {
                //..这里主要回事binder层开辟的buff,不看了
            }
            
            case BC_REPLY: {
                struct binder_transaction_data tr;
                if (copy_from_user(&tr, ptr, sizeof(tr)))
                    return -EFAULT;
                ptr += sizeof(tr);
                //见2.3 
                binder_transaction(proc, thread, &tr, cmd == BC_REPLY);
                break
            }
            //...
        }
    }           
}


2.3 binder_transaction


static void binder_transaction(struct binder_proc *proc,
                   struct binder_thread *thread,
                   struct binder_transaction_data *tr, int reply)
{
    struct binder_transaction *t;
    //..
    
    
    if (reply) {
        //..
        //根据transaction_stack找到需要回传的binder_proc
        in_reply_to = thread->transaction_stack;
        //...
        //更新当前线程和目标线程的transaction_stack信息
        thread->transaction_stack = in_reply_to->to_parent;
        target_thread = in_reply_to->from;
        //...
        target_proc = target_thread->proc;
    }
    //...
    
    //找到todo队列
    if (target_thread) {
        e->to_thread = target_thread->pid;
        target_list = &target_thread->todo;
        target_wait = &target_thread->wait;
    }
    
    // 目标进程分配buffer,copy数据
    t = kzalloc(sizeof(*t), GFP_KERNEL);
    //..
    t->to_proc = target_proc;
    t->to_thread = target_thread;
    t->code = tr->code;
    t->flags = tr->flags;
    t->buffer = binder_alloc_buf(target_proc, tr->data_size,
        tr->offsets_size, !reply && (t->flags & TF_ONE_WAY));
    //..
    t->buffer->transaction = t;
    t->buffer->target_node = target_node;
    //..
    //copy data.ptr.buffer数据到目标进程
    if (copy_from_user(t->buffer->data, (const void __user *)(uintptr_t)
               tr->data.ptr.buffer, tr->data_size)) {
        //...
    }
    //copy 偏移量
    f (copy_from_user(offp, (const void __user *)(uintptr_t)
               tr->data.ptr.offsets, tr->offsets_size)) {
        //...
    }
    //..
    off_end = (void *)offp + tr->offsets_size;
    
    for (; offp < off_end; offp++) {
        //组装flat_binder_object
        struct flat_binder_object *fp;
        //..
        fp = (struct flat_binder_object *)(t->buffer->data + *offp);
        //...
        switch (fp->type) {
            //...
            //这里是handle类型
            case BINDER_TYPE_HANDLE:
            case BINDER_TYPE_WEAK_HANDLE: {
                //根据handle获取具体的binder_ref
                struct binder_ref *ref = binder_get_ref(proc, fp->handle);
                //在根据binder_ref创建或者获取binder_node
                
                //判断这个binder_ref的bind_proc是不是目标进程,目标进程是client,这里binder_ref的bind_proc是server,不是一个,走else
                if (ref->node->proc == target_proc) {
                
                }else{
                    //走的这里
                    struct binder_ref *new_ref;
                    //在目标进程,也就是client进程根据这个服务的binder_node创建新的binder_ref并把proc指向client进程,这个属于client进程的binder_ref
                    new_ref = binder_get_ref_for_node(target_proc, ref->node);
                    fp->handle = new_ref->desc;
                    binder_inc_ref(new_ref, fp->type == BINDER_TYPE_HANDLE, NULL);
                    //...
                }
            }
        }
    }
    if (reply) {
        //出栈
        binder_pop_transaction(target_thread, in_reply_to);
    }
    
    t->work.type = BINDER_WORK_TRANSACTION;
    list_add_tail(&t->work.entry, target_list);
    //...
    if (target_wait)
        //唤醒客户端
        wake_up_interruptible(target_wait);
}

这一步主要的操作处理BC_REPLY命令,根据server_manager进程的目标handle找到对应的binder_ref,即可以找到对应的binder_node,然后在目标进程,也就是client进程创建自己的binder_ref,然后构建flat_binder_object,组装到binder_transaction,唤醒目标client进程。

client进程在调用ioctl进行查找server的时候,会在驱动层进行等待的操作,service_manager进程唤醒之后会进行读取数据的操作binder_thread_read

static int binder_thread_read(struct binder_proc *proc,
                  struct binder_thread *thread,
                  binder_uintptr_t binder_buffer, size_t size,
                  binder_size_t *consumed, int non_block)
{
    void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
    void __user *ptr = buffer + *consumed;
    void __user *end = buffer + size;
    //...
    
    while (1) {
        uint32_t cmd;
        struct binder_transaction_data tr;
        struct binder_work *w;
        struct binder_transaction *t = NULL;
        //从todo 拿出数据
        if (!list_empty(&thread->todo)) {
            w = list_first_entry(&thread->todo, struct binder_work,
                         entry);
        } else if (!list_empty(&proc->todo) && wait_for_proc_work) {
            w = list_first_entry(&proc->todo, struct binder_work,
                         entry);
        }
        ///...
        switch (w->type) {
            case BINDER_WORK_TRANSACTION: {
                t = container_of(w, struct binder_transaction, work);
            } break;
            //...
        }
        
        if (t->buffer->target_node) {
            //...
        
        }else {
           tr.target.ptr = 0;
           tr.cookie = 0;
           /BC_REPLY变成了BR_REPLY
           cmd = BR_REPLY;
        }
        tr.code = t->code;
        //...
        //数据读取
        tr.data_size = t->buffer->data_size;
        tr.offsets_size = t->buffer->offsets_size;
        tr.data.ptr.buffer = (binder_uintptr_t)(
                    (uintptr_t)t->buffer->data +
                    proc->user_buffer_offset);
        tr.data.ptr.offsets = tr.data.ptr.buffer +
                    ALIGN(t->buffer->data_size,
                        sizeof(void *));
                        
        if (put_user(cmd, (uint32_t __user *)ptr)){
            
        }
        ptr += sizeof(uint32_t);
        //数据copy到用户空间
        if (copy_to_user(ptr, &tr, sizeof(tr))){
            
        }
        
    }
    
    //..
    
}

客户端在内核把数据copy到用户空间后,用户空间继续执行代码,binder_parse函数


int binder_parse(struct binder_state *bs, struct binder_io *bio,
                 uintptr_t ptr, size_t size, binder_handler func)
    
    //...
    
    switch(cmd) {
        case BR_REPLY: {
            struct binder_transaction_data *txn = (struct binder_transaction_data *) ptr;
            if ((end - ptr) < sizeof(*txn)) {
                ALOGE("parse: reply too small!\n");
                return -1;
            }
            binder_dump_txn(txn);
            if (bio) {
                //将数据转换到binder_io里面,binder_io就是reply
                bio_init_from_txn(bio, txn);
                bio = 0;
            } else {
                /* todo FREE BUFFER */
            }
            ptr += sizeof(*txn);
            r = 0;
            break;
        }
    }

uint32_t svcmgr_lookup(struct binder_state *bs, uint32_t target, const char *name)
{
    uint32_t handle;
    unsigned iodata[512/4];
    struct binder_io msg, reply;

    bio_init(&msg, iodata, sizeof(iodata), 4);
    bio_put_uint32(&msg, 0);  // strict mode header
    bio_put_string16_x(&msg, SVC_MGR_NAME);
    bio_put_string16_x(&msg, name);
    //调用驱动,get_service
    if (binder_call(bs, &msg, &reply, target, SVC_MGR_CHECK_SERVICE))
        return 0;
    //从reply获取handle
    handle = bio_get_ref(&reply);

    if (handle)
        binder_acquire(bs, handle);

    binder_done(bs, &msg, &reply);

    return handle;
}

    

总结:
客户端通过name获取服务。

  1. 首先组装binder_transation_data和驱动层交互,驱动把相关的数据copyserver_manager进程加入todo队列
  2. server_mamnager唤醒后解析todo消息,把指令copy到用户控件,用户空间根据name得到相关的数据,得到handle
  3. handle传递到server_manager驱动,驱动根据handler找到binder_ref,也就找到了binder_node,然后驱动根据栈管理找到需要回执的进程,也就是客户端进程,根据客户端进程 binder_proc创建需要服务的binder_ref,并且把新的handle组装成新的数据加入到客户端进程的todo队列,并唤醒
  4. 客户端唤醒后读取数据,把这个数据写入copy到用户空间,用户空间执行数据解析得到这个handle数据。
上一篇下一篇

猜你喜欢

热点阅读