美文网首页彩笔看binderandroid菜鸟笔记
Binder源码解读 02(service_manager的bi

Binder源码解读 02(service_manager的bi

作者: 李发糕 | 来源:发表于2020-10-03 22:12 被阅读0次

    上一篇我们从用户空间分析了service_manager如何开启第一个binder的,这一篇我们就开始从内核空间的角度来分析。

    这一节的代码不在framework中,现在在android源码中应该也没有kernel代码了,需要去另外的地址单独下载:https://android.googlesource.com/kernel/

    binder.c

    kernel/drivers/android/binder.c

    首先我们先看看binder驱动模块加载时的初始化

    binder_init

    static int __init binder_init(void)
    {
        int ret;
        char *device_name, *device_tmp;
        struct binder_device *device;
        struct hlist_node *tmp;
        char *device_names = NULL;
    
        ret = binder_alloc_shrinker_init(); //初始化用于binder内存分配的shrinker
        if (ret)
            return ret;
    
        atomic_set(&binder_transaction_log.cur, ~0U);
        atomic_set(&binder_transaction_log_failed.cur, ~0U);
        //在内核文件系统中建立一系列文件夹和文件存储信息
        binder_debugfs_dir_entry_root = debugfs_create_dir("binder", NULL);
        if (binder_debugfs_dir_entry_root)
            binder_debugfs_dir_entry_proc = debugfs_create_dir("proc",
                             binder_debugfs_dir_entry_root);
    
        if (binder_debugfs_dir_entry_root) {
            debugfs_create_file("state",
                        0444,
                        binder_debugfs_dir_entry_root,
                        NULL,
                        &binder_state_fops);
            debugfs_create_file("stats",
                        0444,
                        binder_debugfs_dir_entry_root,
                        NULL,
                        &binder_stats_fops);
            debugfs_create_file("transactions",
                        0444,
                        binder_debugfs_dir_entry_root,
                        NULL,
                        &binder_transactions_fops);
            debugfs_create_file("transaction_log",
                        0444,
                        binder_debugfs_dir_entry_root,
                        &binder_transaction_log,
                        &binder_transaction_log_fops);
            debugfs_create_file("failed_transaction_log",
                        0444,
                        binder_debugfs_dir_entry_root,
                        &binder_transaction_log_failed,
                        &binder_transaction_log_fops);
        }
    
        if (!IS_ENABLED(CONFIG_ANDROID_BINDERFS) &&
            strcmp(binder_devices_param, "") != 0) {//不支持bidnerfs系统
            device_names = kstrdup(binder_devices_param, GFP_KERNEL);//binder_devices_param拷贝
            if (!device_names) {
                ret = -ENOMEM;
                goto err_alloc_device_names_failed;
            }
    
            device_tmp = device_names;
            while ((device_name = strsep(&device_tmp, ","))) { //按照“,”分割binder_devices_param拷贝并遍历
                ret = init_binder_device(device_name); //依次初始化binder_device
                if (ret)
                    goto err_init_binder_device_failed;
            }
        }
        ret = init_binderfs();
        ...
        return ret;
        ...
    }
    

    1.初始化用于binder内存分配的shrinker

    2.在内核文件系统中建立一系列文件夹和文件存储信息

    3.依次初始化binder_device

    4.初始化binderfs

    init_binder_device

    static int __init init_binder_device(const char *name)
    {
        int ret;
        struct binder_device *binder_device;
    
        binder_device = kzalloc(sizeof(*binder_device), GFP_KERNEL);//创建binder_device结构体
        ...
        binder_device->miscdev.fops = &binder_fops; //设置miscdev
        binder_device->miscdev.minor = MISC_DYNAMIC_MINOR;
        binder_device->miscdev.name = name;
    
        refcount_set(&binder_device->ref, 1);//binder_device引用+1
        binder_device->context.binder_context_mgr_uid = INVALID_UID;
        binder_device->context.name = name; //初始化的device的context
        mutex_init(&binder_device->context.context_mgr_node_lock);//初始化互斥锁
    
        ret = misc_register(&binder_device->miscdev);//通过misc_register注册binder设备
        if (ret < 0) {
            kfree(binder_device);
            return ret;
        }
    
        hlist_add_head(&binder_device->hlist, &binder_devices);//将此设备加入binder_devices列表
        return ret;
    }
    

    在设置初始化binder_device的过程中有个属性:binder_device->miscdev.fops = &binder_fops

    const struct file_operations binder_fops = {
        .owner = THIS_MODULE,
        .poll = binder_poll,
        .unlocked_ioctl = binder_ioctl,
        .compat_ioctl = binder_ioctl,
        .mmap = binder_mmap,
        .open = binder_open,
        .flush = binder_flush,
        .release = binder_release,
    };
    

    通过注册binder设备的这些方法,用户空间使用系统调用最终会调用到这些方法。

    binderfs :: init_binderfs

    int __init init_binderfs(void)
    {
        int ret;
        const char *name;
        size_t len;
    
        /* Verify that the default binderfs device names are valid. */
        name = binder_devices_param; //在android-base.config中设置为:CONFIG_ANDROID_BINDER_DEVICES=binder,hwbinder,vndbinder
        for (len = strcspn(name, ","); len > 0; len = strcspn(name, ",")) { //验证设置是否合法
            if (len > BINDERFS_MAX_NAME)
                return -E2BIG;
            name += len;
            if (*name == ',')
                name++;
        }
    
        /* Allocate new major number for binderfs. */
        ret = alloc_chrdev_region(&binderfs_dev, 0, BINDERFS_MAX_MINOR,
                      "binder"); //申请分配一个驱动设备号
        if (ret)
            return ret; 
    
        ret = register_filesystem(&binder_fs_type); //注册binder文件系统
        if (ret) { //注册失败
            unregister_chrdev_region(binderfs_dev, BINDERFS_MAX_MINOR); //取消注册binder设备号
            return ret;
        }
    
        return ret;
    }
    
    

    到这里,binder驱动初始化完毕。

    下面我们根据上一篇文章提到的顺序逐个分析binder驱动的方法。

    open

    static int binder_open(struct inode *nodp, struct file *filp)
    {
        struct binder_proc *proc, *itr; 
        struct binder_device *binder_dev;
        struct binderfs_info *info;
        struct dentry *binder_binderfs_dir_entry_proc = NULL;
        bool existing_pid = false;
        ...
        proc = kzalloc(sizeof(*proc), GFP_KERNEL);//申请一块代表binder的binder_proc
        if (proc == NULL)
            return -ENOMEM;
        ...
        get_task_struct(current->group_leader);//对当前进程组的引用+1
        proc->tsk = current->group_leader;//binder的tsk指向当前进程组
        INIT_LIST_HEAD(&proc->todo);//初始化binder的todo list
        if (binder_supported_policy(current->policy)) { //根据是否支持当前进程的调度策略决定此binder的调度策略
            proc->default_priority.sched_policy = current->policy;
            proc->default_priority.prio = current->normal_prio;
        } else {
            proc->default_priority.sched_policy = SCHED_NORMAL;
            proc->default_priority.prio = NICE_TO_PRIO(0);
        }
    
        /* binderfs stashes devices in i_private */
        if (is_binderfs_device(nodp)) {  //是否是挂载在binderfs下的?
            binder_dev = nodp->i_private;
            info = nodp->i_sb->s_fs_info;
            binder_binderfs_dir_entry_proc = info->proc_log_dir;
        } else {
            binder_dev = container_of(filp->private_data,
                          struct binder_device, miscdev);
        } 
        refcount_inc(&binder_dev->ref); //对binder设备引用+1
        proc->context = &binder_dev->context; //获取context
        binder_alloc_init(&proc->alloc); //初始化binder用户分配记录内存的队列
        binder_stats_created(BINDER_STAT_PROC); //记录BINDER_STAT_PROC create+1
        proc->pid = current->group_leader->pid; //binder的进程id为当前进程组id
        INIT_LIST_HEAD(&proc->delivered_death);
        INIT_LIST_HEAD(&proc->waiting_threads); //初始化binder的两个list
        filp->private_data = proc; //将binder地址设置给filp的private_date用于传递
    
        mutex_lock(&binder_procs_lock);
        hlist_for_each_entry(itr, &binder_procs, proc_node) {
            if (itr->pid == proc->pid) {
                existing_pid = true;
                break;
            }
        }//遍历binder队列的proc_node,判断是否已经存在了和当前binder相同进程id的binder
        hlist_add_head(&proc->proc_node, &binder_procs);//将此binder插入binder队列头
        mutex_unlock(&binder_procs_lock);
    
        if (binder_debugfs_dir_entry_proc && !existing_pid) { //如果没有相同pid的binder存在
            char strbuf[11];
    
            snprintf(strbuf, sizeof(strbuf), "%u", proc->pid);
            //创建对应pid的输出文件
            proc->debugfs_entry = debugfs_create_file(strbuf, 0444,
                binder_debugfs_dir_entry_proc,
                (void *)(unsigned long)proc->pid,
                &proc_fops);
        }
    
        if (binder_binderfs_dir_entry_proc && !existing_pid) {//如果是binder文件系统下
            char strbuf[11];
            struct dentry *binderfs_entry;
    
            snprintf(strbuf, sizeof(strbuf), "%u", proc->pid);
            /*
             * Similar to debugfs, the process specific log file is shared
             * between contexts. Only create for the first PID.
             * This is ok since same as debugfs, the log file will contain
             * information on all contexts of a given PID.
             */
            binderfs_entry = binderfs_create_file(binder_binderfs_dir_entry_proc,
                strbuf, &proc_fops, (void *)(unsigned long)proc->pid);
            if (!IS_ERR(binderfs_entry)) {
                proc->binderfs_entry = binderfs_entry;
            } else {
                int error;
    
                error = PTR_ERR(binderfs_entry);
                pr_warn("Unable to create file %s in binderfs (error %d)\n",
                    strbuf, error);
            }
        }
    
        return 0;
    }
    
    binder_alloc_init

    open之后会调用ioctl方法判断版本号,不过这段逻辑比较简单,而ioctl的逻辑比较多,所以我们暂时不看,先看下一步,mmap。

    mmap

    mmap方法最终会调用binder的binder_mmap,在这之前,会做什么呢?我百度了一下,找到了一个比较清晰的解3释:

    mmap内存映射的实现过程,总的来说可以分为三个阶段:

    (一)进程启动映射过程,并在虚拟地址空间中为映射创建虚拟映射区域

    1、进程在用户空间调用库函数mmap,原型:
    void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);

    2、在当前进程的虚拟地址空间中,寻找一段空闲的满足要求的连续的虚拟地址

    3、为此虚拟区分配一个vm_area_struct结构,接着对这个结构的各个域进行了初始化

    4、将新建的虚拟区结构(vm_area_struct)插入进程的虚拟地址区域链表或树中

    (二)调用内核空间的系统调用函数mmap(不同于用户空间函数),实现文件物理地址和进程虚拟地址的一一映射关系

    5、为映射分配了新的虚拟地址区域后,通过待映射的文件指针,在文件描述符表中找到对应的文件描述符,通过文件描述符,链接到内核“已打开文件集”中该文件的文件结构体(struct file),每个文件结构体维护着和这个已打开文件相关各项信息。

    6、通过该文件的文件结构体,链接到file_operations模块,调用内核函数mmap,其原型为:int mmap(struct file *filp, struct vm_area_struct *vma),不同于用户空间库函数。

    7、内核mmap函数通过虚拟文件系统inode模块定位到文件磁盘物理地址。

    8、通过remap_pfn_range函数建立页表,即实现了文件地址和虚拟地址区域的映射关系。此时,这片虚拟地址并没有任何数据关联到主存中。

    (三)进程发起对这片映射空间的访问,引发缺页异常,实现文件内容到物理内存(主存)的拷贝

    :前两个阶段仅在于创建虚拟区间并完成地址映射,但是并没有将任何文件数据的拷贝至主存。真正的文件读取是当进程发起读或写操作时。

    9、进程的读或写操作访问虚拟地址空间这一段映射地址,通过查询页表,发现这一段地址并不在物理页面上。因为目前只建立了地址映射,真正的硬盘数据还没有拷贝到内存中,因此引发缺页异常。

    10、缺页异常进行一系列判断,确定无非法操作后,内核发起请求调页过程。

    11、调页过程先在交换缓存空间(swap cache)中寻找需要访问的内存页,如果没有则调用nopage函数把所缺的页从磁盘装入到主存中。

    12、之后进程即可对这片主存进行读或者写的操作,如果写操作改变了其内容,一定时间后系统会自动回写脏页面到对应磁盘地址,也即完成了写入到文件的过程。

    :修改过的脏页面并不会立即更新回文件中,而是有一段时间的延迟,可以调用msync()来强制同步, 这样所写的内容就能立即保存到文件里了。

    在看这个方法之前,我们先看一下几个结构体。

    struct
    binder_alloc
    struct binder_alloc {
        struct mutex mutex;
        struct vm_area_struct *vma; //用户空间分配的虚拟内存结构
        struct mm_struct *vma_vm_mm;//vma的拷贝,mmap之后就不会变了
        void __user *buffer; //虚拟内存空间的首地址
        struct list_head buffers;//所有分配的内核空间
        struct rb_root free_buffers;//未被分配(可用)的buffer
        struct rb_root allocated_buffers;//以被分配过的buffer
        size_t free_async_space;//可用于异步事务的内核缓冲空间
        struct binder_lru_page *pages;//分配的所有页
        size_t buffer_size//通过mmap指定的地址空间的大小
        uint32_t buffer_free;//可用空间
        int pid;//对应binder的pid
        size_t pages_high;//
    };
    
    binder_buffer
    struct binder_buffer {
        struct list_head entry; /* free and allocated entries by address */
        struct rb_node rb_node; /* free entry by size or allocated entry */
                    /* by address */
        unsigned free:1;
        unsigned allow_user_free:1;
        unsigned async_transaction:1;
        unsigned debug_id:29;
    
        struct binder_transaction *transaction;
    
        struct binder_node *target_node;
        size_t data_size;
        size_t offsets_size;
        size_t extra_buffers_size;
        void __user *user_data;
    };
    

    之后在来看看内核空间最终被调用的binder_mmap

    binder_mmap
    static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
    {
        int ret;
        struct binder_proc *proc = filp->private_data;//从文件描述中取出我们在open时存入的binder
        const char *failure_string;
        ... //这里进行各种
        vma->vm_flags |= VM_DONTCOPY | VM_MIXEDMAP; //给用户空间分配的虚拟内存设置flag
        vma->vm_flags &= ~VM_MAYWRITE;
    
        vma->vm_ops = &binder_vm_ops;//给这块虚拟内存设置操作方法
        vma->vm_private_data = proc;//把binder设置给vm_private_data
    
        ret = binder_alloc_mmap_handler(&proc->alloc, vma);//映射一块物理地址给虚拟内存并记录在binder->alloc上
        if (ret)
            return ret;
        return 0;
    
    err_bad_arg:
        pr_err("%s: %d %lx-%lx %s failed %d\n", __func__,
               proc->pid, vma->vm_start, vma->vm_end, failure_string, ret);
        return ret;
    }
    
    binder_alloc_mmap_handler
    int binder_alloc_mmap_handler(struct binder_alloc *alloc,
                      struct vm_area_struct *vma)
    {
        int ret;
        const char *failure_string;
        struct binder_buffer *buffer;
        ...
        alloc->buffer_size = min_t(unsigned long, vma->vm_end - vma->vm_start,
                       SZ_4M);//计算出最大4M的空间
        mutex_unlock(&binder_alloc_mmap_lock);
    
        alloc->buffer = (void __user *)vma->vm_start;//给alloc设置对应的虚拟空间的起始地址
    
        alloc->pages = kcalloc(alloc->buffer_size / PAGE_SIZE,
                       sizeof(alloc->pages[0]),
                       GFP_KERNEL);//计算并申请页的空间
        ...
        buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); //申请binder_buffer.
        ...
        buffer->user_data = alloc->buffer; //设置user_data为虚拟空间起始地址
        list_add(&buffer->entry, &alloc->buffers);//将此binder_buffer插入binder->alloc中
        buffer->free = 1;
        binder_insert_free_buffer(alloc, buffer);//将此buffer插入可用队列
        alloc->free_async_space = alloc->buffer_size / 2;//设定用于异步事务的最大空间为一半
        binder_alloc_set_vma(alloc, vma);//设置vma给binder_alloc
        mmgrab(alloc->vma_vm_mm);
        return 0;
        ...
    }
    
    binder_insert_free_buffer
    static void binder_insert_free_buffer(struct binder_alloc *alloc,
                          struct binder_buffer *new_buffer)
    {
        struct rb_node **p = &alloc->free_buffers.rb_node;
        struct rb_node *parent = NULL;
        struct binder_buffer *buffer;
        size_t buffer_size;
        size_t new_buffer_size;
        ...
        new_buffer_size = binder_alloc_buffer_size(alloc, new_buffer);//计算此buffer要分配的空间大小
        ...
        while (*p) {//遍历可用buffer树,根据buffer大小将此buffer插入可用buffer红黑树中
            parent = *p;
            buffer = rb_entry(parent, struct binder_buffer, rb_node);
            BUG_ON(!buffer->free);
    
            buffer_size = binder_alloc_buffer_size(alloc, buffer);
    
            if (new_buffer_size < buffer_size)
                p = &parent->rb_left;
            else
                p = &parent->rb_right;
        }
        rb_link_node(&new_buffer->rb_node, parent, p);
        rb_insert_color(&new_buffer->rb_node, &alloc->free_buffers);
    }
    

    到此为止,我们发现mmap方法并没有在内核空间真的分配用户空间请求的对应内存,而只是先创建了一系列结构体用于记录分配的虚拟内存空间的各信息。

    接下来,根据上篇文章所说,用户空间进入了loop当中,首先向内核空间写入BC_ENTER_LOOPER字段通知,之后则不断的从内核空间中读取数据,通过binder_parse进行解析来决定接下来的操作。

    那么,这一系列的方法,如read/write以及前面我们略过的判定binder版本信息等方法,都是通过一个系统调用方法和不同的对应参数实现的,就是ioctl。

    ioctl

    binder_ioctl

    这个方法比较长,我们分段看

    static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
    {
        int ret;
        struct binder_proc *proc = filp->private_data; //拿到内核空间的binder
        struct binder_thread *thread;
        unsigned int size = _IOC_SIZE(cmd); //从cmd中读取到本次数据传输的size。在cmd这个值的中不同的位代表了不同的含义,其中就有几位表示size,这个宏定义在ioctl.h中。
        void __user *ubuf = (void __user *)arg;//用户空间传过来的参数的虚拟空间地址
        ...
        ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2); //判断当前错误数决定是继续进行还是等待。
        if (ret) //错误数多,被唤醒,跳转报错处理
            goto err_unlocked;
        //正常情况继续执行
        thread = binder_get_thread(proc); //获取此次调用的binder_thread
        ...
    }
    

    这里我们停下看看,binder_thread是如何获取的

    binder_get_thread
    static struct binder_thread *binder_get_thread(struct binder_proc *proc)
    {
        struct binder_thread *thread;
        struct binder_thread *new_thread;
    
        binder_inner_proc_lock(proc);
        thread = binder_get_thread_ilocked(proc, NULL);//查找此binder进程下的thread
        binder_inner_proc_unlock(proc);
        if (!thread) { //没找到
            new_thread = kzalloc(sizeof(*thread), GFP_KERNEL);
            if (new_thread == NULL)
                return NULL;
            binder_inner_proc_lock(proc);
            thread = binder_get_thread_ilocked(proc, new_thread);//创建一个新的binder_thread并插入binder的thread树
            binder_inner_proc_unlock(proc);
            if (thread != new_thread) //出错了
                kfree(new_thread);//释放刚才创建的thread
        }
        return thread;
    }
    
    binder_get_thread_ilocked
    static struct binder_thread *binder_get_thread_ilocked(
            struct binder_proc *proc, struct binder_thread *new_thread)
    {
        struct binder_thread *thread = NULL;
        struct rb_node *parent = NULL;
        struct rb_node **p = &proc->threads.rb_node;
    
        while (*p) { //遍历此binder的所有binder_thread,查找和binder_proc相同进程号的thread
            parent = *p;
            thread = rb_entry(parent, struct binder_thread, rb_node);
    
            if (current->pid < thread->pid)
                p = &(*p)->rb_left;
            else if (current->pid > thread->pid)
                p = &(*p)->rb_right;
            else
                return thread; //找到了,直接返回
        }
        if (!new_thread) //如果没有指定新的thread,比如上一个方法的第一次调用,此时仅相当与查找,直接返回null
            return NULL;
        thread = new_thread;//有传入新的thread
        binder_stats_created(BINDER_STAT_THREAD); //记录创建binder线程的状态
        //给新的binder_thread绑定或初始化binder,pid,进程,引用计数,以及各种任务队列等属性
        thread->proc = proc; 
        thread->pid = current->pid;
        get_task_struct(current);
        thread->task = current;
        atomic_set(&thread->tmp_ref, 0);
        init_waitqueue_head(&thread->wait);
        INIT_LIST_HEAD(&thread->todo);
        rb_link_node(&thread->rb_node, parent, p);
        rb_insert_color(&thread->rb_node, &proc->threads);
        thread->looper_need_return = true;
        thread->return_error.work.type = BINDER_WORK_RETURN_ERROR;
        thread->return_error.cmd = BR_OK;
        thread->reply_error.work.type = BINDER_WORK_RETURN_ERROR;
        thread->reply_error.cmd = BR_OK;
        INIT_LIST_HEAD(&new_thread->waiting_thread_node);
        return thread;
    }
    

    回过头继续看ioctl

    ...
    switch (cmd) {
        ... //根据不同的cmd执行不同的策略
        }
        ret = 0;
    err:
        if (thread)
            thread->looper_need_return = false;
        wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
        if (ret && ret != -ERESTARTSYS)
            pr_info("%d:%d ioctl %x %lx returned %d\n", proc->pid, current->pid, cmd, arg, ret);
    err_unlocked:
        trace_binder_ioctl_done(ret);
        return ret;
    

    上面我们了解到cmd的各个位有不同的含义,在ioctl.h中定义了cmd取值的各种方法,这里就不细说了。

    下面挑选我们只挑选两个之前见到过的cmd说一下,剩下的等遇到了再说~。

    BINDER_VERSION

    获取binderversion,这个之前见到过,很简单。

    case BINDER_VERSION: {
            struct binder_version __user *ver = ubuf;//
    
            if (size != sizeof(struct binder_version)) {
                ret = -EINVAL;
                goto err;
            }
            if (put_user(BINDER_CURRENT_PROTOCOL_VERSION,
                     &ver->protocol_version)) { //直接把版本写入用户空间提供的虚拟内存地址
                ret = -EINVAL;
                goto err;
            }
            break;
        }
    
    BINDER_SET_CONTEXT_MGR

    这个case在service_manager中open之后会被调用,用来将service_manager生成的binder设置为contextmanager。

    case BINDER_SET_CONTEXT_MGR:
       ret = binder_ioctl_set_ctx_mgr(filp);
       if (ret)
          goto err;
       break;
    
    static int binder_ioctl_set_ctx_mgr(struct file *filp)
    {
       int ret = 0;
       struct binder_proc *proc = filp->private_data;
       struct binder_context *context = proc->context;
       struct binder_node *new_node;
       kuid_t curr_euid = current_euid();
       ...
       new_node = binder_new_node(proc, NULL);//生成一个binder节点并插入当前binder_proc的note树中
       if (!new_node) {
          ret = -ENOMEM;
          goto out;
       }
       binder_node_lock(new_node);
       new_node->local_weak_refs++;
       new_node->local_strong_refs++;
       new_node->has_strong_ref = 1;
       new_node->has_weak_ref = 1;
       context->binder_context_mgr_node = new_node;//设置给binder_context_mgr_node
       binder_node_unlock(new_node);
       binder_put_node(new_node);
    out:
       mutex_unlock(&context->context_mgr_node_lock);
       return ret;
    }
    
    BINDER_WRITE_READ

    这个情况就比较复杂了,在前文的binder_loop以及其他情况中肯定是多次调用,我们详细分析一下。

    case BINDER_WRITE_READ:
            ret = binder_ioctl_write_read(filp, cmd, arg, thread);
            if (ret)
                goto err;
            break;
    
    static int binder_ioctl_write_read(struct file *filp,
                    unsigned int cmd, unsigned long arg,
                    struct binder_thread *thread)
    {
        int ret = 0;
        struct binder_proc *proc = filp->private_data;
        unsigned int size = _IOC_SIZE(cmd);
        void __user *ubuf = (void __user *)arg;
        struct binder_write_read bwr;
        ...
        if (copy_from_user(&bwr, ubuf, sizeof(bwr))) { //从用户空间中拷贝传输的binder_write_read结构参数到内核空间。
            ret = -EFAULT;
            goto out;
        }
        ...
        if (bwr.write_size > 0) { //需要写入数据到内核空间
            ret = binder_thread_write(proc, thread,
                          bwr.write_buffer,
                          bwr.write_size,
                          &bwr.write_consumed); //写入数据
            trace_binder_write_done(ret);
            if (ret < 0) { //写入失败了
                bwr.read_consumed = 0;//
                if (copy_to_user(ubuf, &bwr, sizeof(bwr)))//把结果拷贝给用户空间
                    ret = -EFAULT;
                goto out;//跳到out
            }
        }
        if (bwr.read_size > 0) { //需要从内核空间读取数据
            ret = binder_thread_read(proc, thread, bwr.read_buffer,
                         bwr.read_size,
                         &bwr.read_consumed,
                         filp->f_flags & O_NONBLOCK);//读取数据到bwr
            trace_binder_read_done(ret);
            binder_inner_proc_lock(proc);
            if (!binder_worklist_empty_ilocked(&proc->todo))//如果此binder的todolist不为空
                binder_wakeup_proc_ilocked(proc);//唤醒此binder
            binder_inner_proc_unlock(proc);
            if (ret < 0) {
                if (copy_to_user(ubuf, &bwr, sizeof(bwr)))//把结果拷贝给用户空间
                    ret = -EFAULT;
                goto out;
            }
        }
        ...
        if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {//把结果拷贝给用户空间
            ret = -EFAULT;
            goto out;
        }
    out:
        return ret;
    }
    

    总的看来,就是先从用户空间拷贝数据,然后进行操作,最后吧结果拷贝回用户空间。

    那么我们分别看一下两个方法。

    首先是写入数据。binder_thread_write

    这个方法也很长,不过很重要,我们一点一点分析。

    首先是方法参数。

    static int binder_thread_write(struct binder_proc *proc, //内核空间的binder
                struct binder_thread *thread,//当前thread
                binder_uintptr_t binder_buffer, //需要写入的数据的起始位置(用户空间的虚拟内存地址)
          size_t size,//要写入的数据量
                binder_size_t *consumed)//已写入的大小
    

    然后是方法体

    {
        uint32_t cmd;
        struct binder_context *context = proc->context;
        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.cmd == BR_OK) {
            int ret;
    
            if (get_user(cmd, (uint32_t __user *)ptr))//把用户空间传输的第一个unit32数据拷贝到内核控件,作为cmd
                return -EFAULT;
            ptr += sizeof(uint32_t); // 用户空间传输的数据指针后移到下一个数据
            trace_binder_command(cmd);
            if (_IOC_NR(cmd) < ARRAY_SIZE(binder_stats.bc)) {
                atomic_inc(&binder_stats.bc[_IOC_NR(cmd)]);
                atomic_inc(&proc->stats.bc[_IOC_NR(cmd)]);
                atomic_inc(&thread->stats.bc[_IOC_NR(cmd)]);
            }
            switch (cmd) {
            ...
            }
        return 0;
    }
    

    在switch中,对于用户空间写入的第一个参数做了大量的case判断,比如在上一篇中,用户空间servicemanager在进入循环之前通知内核驱动,写入了BC_ENTER_LOOPER字段

    case BC_ENTER_LOOPER:
        ...
        if (thread->looper & BINDER_LOOPER_STATE_REGISTERED) {
            thread->looper |= BINDER_LOOPER_STATE_INVALID;
            binder_user_error("%d:%d ERROR: BC_ENTER_LOOPER called after BC_REGISTER_LOOPER\n",
                proc->pid, thread->pid);
        }
        thread->looper |= BINDER_LOOPER_STATE_ENTERED;
        break;
    

    判断当前thread的looper标记位是否已注册,设置进入循环的标记位。

    剩下的情况比较复杂,我们这里暂不分析,后面遇到了再说。

    在通知进入looper之后,serviccemanager就正式开始循环读取内核数据了,我们正好分析下读取数据的方法。

    binder_thread_read

    和刚才一样,首先是方法参数:

    static int binder_thread_read(struct binder_proc *proc,//内核binder
                      struct binder_thread *thread,//binder线程
                      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;
    
        int ret = 0;
        int wait_for_proc_work;
    
        if (*consumed == 0) { //还未读取
            if (put_user(BR_NOOP, (uint32_t __user *)ptr)) //向用户空间写入 BR_NOOP, 根据上篇文章,用户空间读取到此标记会继续读取解析。
                return -EFAULT;
            ptr += sizeof(uint32_t); //写入指针后移
        }
    
    retry:
        binder_inner_proc_lock(proc);
        wait_for_proc_work = binder_available_for_proc_work_ilocked(thread); //判断此binder线程是否需要等待数据
        binder_inner_proc_unlock(proc);
    
        thread->looper |= BINDER_LOOPER_STATE_WAITING;//此线程进入WAITING状态
    
        trace_binder_wait_for_work(wait_for_proc_work,
                       !!thread->transaction_stack,
                       !binder_worklist_empty(proc, &thread->todo));
        if (wait_for_proc_work) {//可等待工作
            if (!(thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
                        BINDER_LOOPER_STATE_ENTERED))) { //当前线程状态不对,报错
                ...
            }
            ...
        }
    
        if (non_block) {//如果此次读取不允许阻塞
            if (!binder_has_work(thread, wait_for_proc_work)) //当前binder没有数据可读
                ret = -EAGAIN;//报错
        } else {
            ret = binder_wait_for_work(thread, wait_for_proc_work); //等待数据
        }
    
        thread->looper &= ~BINDER_LOOPER_STATE_WAITING; //等待完毕,线程退出等待数据状态
        ... //省略的这一部分才是重点,但是我们本篇暂时不分析,为啥呢?因为根据service_manager的启动流程,目前还没有事务要执行,我们还在等待的状态呢~所以,我们先看看等待的方法。
        return 0;
    }
    

    binder_wait_for_work

    static int binder_wait_for_work(struct binder_thread *thread,
                    bool do_proc_work)
    {
        DEFINE_WAIT(wait);
        struct binder_proc *proc = thread->proc;
        int ret = 0;
    
        freezer_do_not_count();
        binder_inner_proc_lock(proc);
        for (;;) {
            prepare_to_wait(&thread->wait, &wait, TASK_INTERRUPTIBLE);
            if (binder_has_work_ilocked(thread, do_proc_work))
                break;
            if (do_proc_work)
                list_add(&thread->waiting_thread_node,
                     &proc->waiting_threads); //将此线程加入此binder的等待线程队列中
            binder_inner_proc_unlock(proc);
            schedule();//让给其他进程
            binder_inner_proc_lock(proc);
            list_del_init(&thread->waiting_thread_node);
            if (signal_pending(current)) {
                ret = -ERESTARTSYS;
                break;
            }
        }
        finish_wait(&thread->wait, &wait);
        binder_inner_proc_unlock(proc);
        freezer_count();
    
        return ret;
    }
    

    到这里为止,service_manager的binder的启动流程就结束啦。

    相关文章

      网友评论

        本文标题:Binder源码解读 02(service_manager的bi

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