美文网首页
binder(五) client获取服务

binder(五) client获取服务

作者: 曾大稳丶 | 来源:发表于2021-06-10 11:35 被阅读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数据。

    相关文章

      网友评论

          本文标题:binder(五) client获取服务

          本文链接:https://www.haomeiwen.com/subject/rghbsltx.html