您的位置:首页 > 移动开发 > Android开发

binder驱动框架注册服务

2017-12-12 13:57 218 查看
binder驱动框架misc驱动,Binder驱动:

ret = misc_register(&binder_miscdev);杂项驱动

      static struct miscdevice binder_miscdev = {

           .minor = MISC_DYNAMIC_MINOR,

           .name = "binder",

           .fops = &binder_fops/////////////////binder驱动向misc注册

      };

                                                                  misc_class = class_create(THIS_MODULE, "misc");//创建类

                                                                  register_chrdev(MISC_MAJOR,"misc",&misc_fops)

                                                                  它同样是做注册字符设备驱动名字"misc", 文件操作&misc_fops

                                                                  static const struct file_operations misc_fops = {

                                                                                 .owner        = THIS_MODULE,

                                                                                 .open        = misc_open,

                                                                                 .llseek        = noop_llseek,

                                                                   };misc向内核注册

      在open函数里做了

        list_for_each_entry(c, &misc_list, list) {

            if (c->minor == minor) {

                new_fops = fops_get(c->fops);

                break;

            }

        } //////////根据次设备号找到那个文件操作的结构体以后只要操作文件就直接调用binder的open函数

服务注册过程:

       SMmain函数打开驱动输出版本信息  

       然后告诉内核binder_become_context_manager(bs)自己是Handle为0 的SM

      在binder_loop中 binder_write(bs, readbuf, sizeof(uint32_t));把readbuf[0] = BC_ENTER_LOOPER;写入将会嗲用binder_thread_write函数。这个函数的参数线程是怎么来的thread = binder_get_thread(proc);,,用来取在线程树下与当前线程PID相等的线程。

             case BC_ENTER_LOOPER:

            binder_debug(BINDER_DEBUG_THREADS,

                     "binder: %d:%d BC_ENTER_LOOPER\n",

                     proc->pid, thread->pid);

            if (thread->looper & BINDER_LOOPER_STATE_REGISTERED) {

                thread->looper |= BINDER_LOOPER_STATE_INVALID;

                binder_user_error("binder: %d:%d ERROR:"

                    " BC_ENTER_LOOPER called after "

                    "BC_REGISTER_LOOPER\n",

                    proc->pid, thread->pid);

            }

            thread->looper |= BINDER_LOOPER_STATE_ENTERED;

            break;

thread = rb_entry(parent, struct binder_thread, rb_node);

#define    rb_entry(ptr, type, member) container_of(ptr, type, member)

在Binder驱动层,会跟用户空间的线程关联一个struct binder_thread实例,这个结构记录了内核空间的在该线程上执行的一些IPC状态信息

struct binder_thread {  
    struct binder_proc *proc;  //线程所属的进程  
    struct rb_node rb_node;  //红黑树的结点,进程通过该结点将线程加入到红黑树中  
    int pid; //线程的pid  
    int looper;  //线程所处的状态  
    struct binder_transaction *transaction_stack;//transaction session list on this thread  
    struct list_head todo; //在该线程上的Task列表  
    uint32_t return_error; /* Write failed, return error code in read buf */  
    uint32_t return_error2; /* Write failed, return error code in read */  
        /* buffer. Used when sending a reply to a dead process that */  
        /* we are also waiting on */  
    wait_queue_head_t wait; //该线程的等待队列  
    struct binder_stats stats; //统计该线程上的命令数量  
};

1> Container_of在Linux内核中是一个常用的宏,用于从包含在某个结构中的指针获得结构本身的指针,通俗地讲就是通过结构体变量中某个成员的首地址进而获得整个结构体变量的首地址。

2>接口:

container_of(ptr, type, member) 

 ptr:表示结构体中member的地址

 type:表示结构体类型

 member:表示结构体中的成员

通过ptr的地址可以返回结构体的首地址

        就是输出一些打印信息

        bwr.read_size = sizeof(readbuf);

        bwr.read_consumed = 0;

        bwr.read_buffer = (uintptr_t) readbuf;

        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);///////////读操作将会调用binder_thread_read函数

tr.data_size = t->buffer->data_size;

        tr.offsets_size = t->buffer->offsets_size;

        tr.data.ptr.buffer = (void *)t->buffer->data +

                    proc->user_buffer_offset;/////////////////////////////////////////数据

        tr.data.ptr.offsets = tr.data.ptr.buffer +

                    ALIGN(t->buffer->data_size,

                        sizeof(void *));

 
4000
       if (put_user(cmd, (uint32_t __user *)ptr))

            return -EFAULT;

        ptr += sizeof(uint32_t);

        if (copy_to_user(ptr, &tr, sizeof(tr)))

SM ioctl里面会创建一个线程也有一个todo链表

接下来进入for循环是一个读操作调用bidner_thread_read函数

if (put_user(BR_NOOP, (uint32_t __user *)ptr))

            return -EFAULT;

但是他先写入BR_NOOP这个命令,,对于所有的读操作数据头部都是BR_NOOP。

read_buffer里放着的是BR_NOOP,cmd 数据,cmd 数据,,,

当然没有数据休眠ret = wait_event_interruptible_exclusive(pr。

现在轮到test_server了

int svcmgr_publish(struct binder_state *bs, uint32_t target, const char *name, void *ptr)

{

    int status;

    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);

    bio_put_obj(&msg, ptr);



对于这个iodata buffer里的数据格式存放前16 字节空开,然后用一个字节放0,然后放    bio_put_string16_x(&msg, SVC_MGR_NAME);

这个函数用前一个字节放长度bio_put_uint32(bio, len);

用两个字节放一个字符    uint16_t *ptr;  

while (*str)

        *ptr++ = *str++;

    *ptr++ = 0;

////////////////////////////////////////////////

    bio_put_obj(&msg, ptr);

               struct flat_binder_object *obj;

server给驱动传入一个obj,然后内核根据它创捷一个binder_node结构体

offs的作用就是指出这个结构体的位置以便内核知道他在哪里

static struct flat_binder_object *bio_alloc_obj(struct binder_io *bio)

{

    struct flat_binder_object *obj;

    obj = bio_alloc(bio, sizeof(*obj));

    if (obj && bio->offs_avail) {

        bio->offs_avail--;

        *bio->offs++ = ((char*) obj) - ((char*) bio->data0);///bio->offs是首地址。       ((char*) obj) - ((char*) bio->data0)表示i相对于空出16字节的地址的偏移。所以前16 字节的作用就是记录这个内存快的位置

        return obj;

    }

    bio->flags |= BIO_F_OVERFLOW;

    return NULL;

}

接着把构造的数据发送,调用binder_call实质就是调用binder_ioctl函数当然先构造一个驱动认识的binder_write_read结构体,cmd表明消息的类型,数据放在binder_transaction_data这

    writebuf.cmd = BC_TRANSACTION;

    writebuf.txn.target.handle = target;

    writebuf.txn.code = code;

    writebuf.txn.flags = 0;

    writebuf.txn.data_size = msg->data - msg->data0;

    writebuf.txn.offsets_size = ((char*) msg->offs) - ((char*) msg->offs0);

    writebuf.txn.data.ptr.buffer = (uintptr_t)msg->data0;

    writebuf.txn.data.ptr.offsets = (uintptr_t)msg->offs0;

构造数据,转为binder_write_read结构体,然后把数据发送用ioctl()系统调用进入驱动,调用binder_ioctl(的作用就是把数据放入目标进程的todo链表唤醒进程

if (tr->target.handle) {///////////////如果不是SM的handle就取出它的ref结构体,前边说过这个ref结构体记录了handle值和对应的binder_ndoe结构体
struct binder_ref *ref;
ref = binder_get_ref(proc, tr->target.handle);
else {
target_node = binder_context_mgr_node;////否则直接让他等于一个特殊的目标节点
这个在什么时候被创建
target_proc = target_node->proc;////////找到目标进程
copy_from_user(t->buffer->data, tr->data.ptr.buffer, tr->data_size)/////开始真正的复制数据
for (; offp < off_end; offp++) {
        struct flat_binder_object *fp;
        if (*offp > t->buffer->data_size - sizeof(*fp) ||
            *offp < off_min ||
            t->buffer->data_size < sizeof(*fp) ||
            !IS_ALIGNED(*offp, sizeof(void *))) {///////////处理数据开头的offso
t->work.type = BINDER_WORK_TRANSACTION;
    list_add_tail(&t->work.entry, target_list);
    tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
    list_add_tail(&tcomplete->entry, &thread->todo);
    if (target_wait)
        wake_up_interruptible(target_wait);///最后把t放入目的的链表里,然后唤醒这个链表是todo链表target_list = &target_thread->todo;
详细分析一下驱动对数据offso的处理,,,就是flag_binder_object结构体
给test_server构造binder_node结构体,,,,构造binder_ref给SM。增加引用计数。。。
看看这个结构体:
struct flat_binder_object {
    /* 8 bytes for large_flat_header. */
    unsigned long        type;////表示当前传输的这个结构体是实体还是引用,只有server提供者可以传实体,对于SM和client程序只能传引用
    unsigned long        flags;

    /* 8 bytes of data. */
    union {
        void        *binder;    /* local object */   。/////当传实体的时候联合体对应binder,当传引用的时候代表handle比如test_server中    obj->binder = (uintptr_t)ptr;即使将处理函数放在了binder中
        signed long    handle;        /* remote object */
    };

    /* extra data associated with local object */
    void            *cookie;
};
copy_from_user(offp, tr->data.ptr.offsets, tr->offsets_size);
这个结构体从用户空间复制到内核空间,,,数据在
tr->data.ptr.offsets


这个地方是个数组相当于放四个flag_binder_object结构体然后逐个处理switch (fp->type) {        case BINDER_TYPE_BINDER:        case BINDER_TYPE_WEAK_BINDER: {/////如果是binder就是这个node = binder_new_node(proc, fp->binder, fp->cookie);创建node节点给teat_server进程ref
= binder_get_ref_for_node(target_proc, node);//创建目的进程也就是SM创建按引用binder_proc结构体里的红黑节点node用来记录进程创建的服务    struct rb_root refs_by_desc;/////////根据handle号快速找到引用    struct rb_root refs_by_node;///////////根据node快速找到ref            if (fp->type == BINDER_TYPE_BINDER)  
             fp->type = BINDER_TYPE_HANDLE;并且修改type现在在内核空间binder_inc_ref(ref, fp->type == BINDER_TYPE_HANDLE,                       &thread->todo);//////会返回一些信息给当前线程,test_server就知道有人引用。t->work.type = BINDER_WORK_TRANSACTION;  
 list_add_tail(&t->work.entry, target_list);    tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;    list_add_tail(&tcomplete->entry, &thread->todo);    if (target_wait)        wake_up_interruptible(target_wait);///////////数据写入后休眠if
(t->buffer->target_node) {            struct binder_node *target_node = t->buffer->target_node;            tr.target.ptr = target_node->ptr;            tr.cookie =  target_node->cookie;            t->saved_priority = task_nice(current);            if (t->priority
< target_node->min_priority &&                !(t->flags & TF_ONE_WAY))                binder_set_nice(t->priority);            else if (!(t->flags & TF_ONE_WAY) ||                 t->saved_priority > target_node->min_priority)                binder_set_nice(target_node->min_priority);  
         cmd = BR_TRANSACTION;SM这边收到数据后开始处理res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);        if (res < 0) {            ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));            break;        }        res
= binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);////////返回到这里进行解析  在前边int binder_become_context_manager(struct binder_state *bs){    return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);}////SM函数里就已经ioctl这个,binder_context_mgr_node
= binder_new_node(proc, NULL, NULL);然后创建了这个结构体
BINDER_SET_CONTEXT_MGR






)把数据放入SMbinder_proc结构体的todo链表里,












                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Android Binder系统
相关文章推荐