美文网首页
Android进程间通信(三)——系统服务管理进程service

Android进程间通信(三)——系统服务管理进程service

作者: Boahui | 来源:发表于2021-07-11 08:15 被阅读0次

    Android进程间通信——Binder通信相关系统进程(二)这篇中我们知道,手机系统服务需要先注册到servicemanager中才能被服务端找到和使用,而一个普通应用的运行过程中需要频繁用到系统服务,比如AMS,WMS等,这些系统服务都要在系统启动时就注册到servicemanager进程中,之后才能被其他进程调用。所以进程间通信中最底层和最核心的进程就是servicemanager,没有它可以说进程间通信是无法正常工作的,所以我们就先从servicemanager这个进程开始,通过分析源码了解一步一步了解进Android进程间通信的原理。源码github地址

    servicemanager的源码

    servicemanager是一个c语言编写的程序,其源码位于系统源码/frameworks/native/cmds/servicemanager/service_manager.c
    首先我们看下main方法做了什么,这里我省略了一些与核心逻辑无关代码

    int main(int argc, char** argv)
    {
        char *driver;
        if (argc > 1) {
            driver = argv[1];
        } else {
            driver = "/dev/binder";
        }
        bs = binder_open(driver, 128*1024);
        if (binder_become_context_manager(bs)) {
            ALOGE("cannot become context manager (%s)\n", strerror(errno));
            return -1;
        }
        binder_loop(bs, svcmgr_handler);
        return 0;
    }
    

    binder_open

    我们看到,在main方法中,调用了binder_open方法,同时传入得数据是一个driver字符传表示binder驱动得路径,另外一个是128*1026也就是128k。
    binder_open的源码位于service_manager.c同目录下的binder.c中,注意这个不是真正的binder驱动代码。

    struct binder_state    //binder_open方法的返回数据结构体
    {
        int fd;
        void *mapped;
        size_t mapsize;
    };
    //注意其中忽略了核心逻辑无关代码
    struct binder_state *binder_open(size_t mapsize)
    {
        struct binder_state *bs;
        bs = malloc(sizeof(*bs));
        bs->fd = open("/dev/binder", O_RDWR);
        bs->mapsize = mapsize;
        bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
        return bs;
    }
    

    1.首先为创建了一个binder_state结构体变量bs并分配了内存
    2.通过open("/dev/binder",O_RDWR)方法打开binder驱动,并返回fd文件句柄保存到bs中
    open方法会调用驱动kernel/drivers/staging/android/binder.c
    binder是一种虚拟的字符设备,注册在/dev/binder中,字符(char)设备是个能够像字节流(类似文件)一样被访问的设备,由字符设备驱动程序来实现这种特性。字符设备驱动程序通常至少要实现open,close,read和write系统调用。大多数字符设备是一个个只能顺序访问的数据通道

    // 每个使用Binder的进程都会在使用前打开binder驱动,驱动在open的时候会创建结构体binder_proc,并将它的指针记录到文件结构体private_data中,以后的驱动操作就能通过文件结构体来获取该进程的信息。
      //  驱动里都是以binder_proc为单元来分配和记录资源的,驱动会给每个使用binder的进程创建一个/proc/binder/proc/pid文件,用来展示进程相关的binder信息。
    //proc/binder/stats和/proc/binder/proc/pid能查看到相关信息
    static int binder_open(struct inode *nodp, struct file *filp)
    {
        struct binder_proc *proc;//进程信息
        struct binder_device *binder_dev;//
        proc = kzalloc(sizeof(*proc), GFP_KERNEL);
        get_task_struct(current);
        proc->tsk = current;
        INIT_LIST_HEAD(&proc->todo);//注意初始化todo链表,表示要执行的任务
        init_waitqueue_head(&proc->wait);//初始化等待队列,表示挂起任务
        proc->default_priority = task_nice(current);
        binder_dev = container_of(filp->private_data, struct binder_device,
                      miscdev);/、
        proc->context = &binder_dev->context;//context上下文信息,保存有servicemanager 进程相关信息
        binder_stats_created(BINDER_STAT_PROC);
        hlist_add_head(&proc->proc_node, &binder_procs);
        proc->pid = current->group_leader->pid;
        INIT_LIST_HEAD(&proc->delivered_death);
        filp->private_data = proc;
    }
    

    3.通过mmap方法给打开的文件句柄fd映射一块内存,对这个映射的内存读写就可以实现对文件的读写
    这里是一个重点,也就是通常说binder只需要一次拷贝来实现进程间通信。
    这里需要我们补充一下Linux的背景知识,知道为啥需要mmap,可以参考这篇博客Android Binder通信一次拷贝你真的理解了吗?

    我们需要了解以下背景
    1、为啥要有跨进程通信机制?
    因为Linux的内存模型是进程间内存隔离的,2个进程不能直接通信,也就是进程之间不能直接访问对方的内存, 这肯定是为了安全
    2、Android为啥不用Linux原生自带的进程间通信机制可参考Android Bander设计与实现 - 设计篇
    为了性能和安全等因素
    3、了解什么是内核空间和什么是用户空间
    普通应用运行在内核空间,系统应用运行在内核空间,普通应用不能直接访问内核空间,可以通过系统调用访问内核空间


    4、传统跨进程通信方式,或不用mmap的跨进程通信方式

    也就是需要进程A先把数据拷贝到内核空间,然后再从内核空间拷贝到进程B 这样才能实现跨进程通信,但是2次拷贝的开销很大
    5、Linux的虚拟内存——用户空间程序使用的内存地址都是系统分配的虚拟内存地址,虚拟内存是对物理内存的映射

    mmap

    这里调用mmap最终会调用到binder驱动中的binder_mmap方法具体流程可参考Android binder中的mmap到binder_mmap调用流程 在理解binder_mmap函数前,我们要了解一些基本的结构体的作用。

    struct file

    代表一个打开的文件描述符

    vm_area_struct *vma

    表示一个虚拟内存,因为实际物理内存并不是连续的,所以程序的虚拟内存是一个链表
    vm_area_struct结构体中几个重要的变量

    struct vm_area_struct {
        unsigned long vm_start;//虚拟内存起始位置
        unsigned long vm_end;//虚拟内存结束位置
        struct vm_area_struct* vm_next, * vm_prev;//虚拟内存的前后节点指针
        unsigned long vm_flags;//主要保存VMA标志位
        const struct vm_operations_struct* vm_ops;//VMA操作函数合集,常用于文件映射
        struct file* vm_file;//描述一个被映射的文件
        void* vm_private_data;
    };
    

    binder_proc

    binder_proc是一个非常重要的结构体,其中保存了进程的很多信息

    struct binder_proc {
        // 进程打开设备文件/dev/binder时,Binder驱动会为它创建一个binder_proc结构体,并将它
        // 保存在全局hash列表中,proc_node是该hash列表的节点。
        struct hlist_node proc_node;
        //每个使用了Binder机制的进程都有一个Binder线程池,用来处理进程间通信请求。threads以
        // 线程ID作为key来组织进程的Binder线程池。进程可以调用ioctl将线程注册到Binder驱动中
        // 当没有足够的空闲线程处理进程间通信请求时,驱动可以要求进程注册更多的线程到Binder线程
        // 池中
        struct rb_root threads;
        struct rb_root nodes;//组织Binder实体对象,它以成员ptr作为key
        struct rb_root refs_by_desc;//进程中的rb_root红黑树的根,用于查找handle对应的node  组织Binder引用对象,它以成员desc作为key
        struct rb_root refs_by_node;//组织Binder引用对象,它以成员node作为key
        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;// vma和buffer之间的差值
        // buffer指向一块大的内核缓冲区,驱动程序为方便管理,将它划分成若干小块,这些小块的内核缓
        // 冲区用binder_buffer描述保存在列表中,按地址从小到大排列。buffers指向该列表的头部。
        struct list_head buffers;
        struct rb_root free_buffers// buffers中的小块有的正在使用,被保存在此红黑树
        struct rb_root allocated_buffers;;// buffers中的空闲小块被保存在此红黑树
        size_t free_async_space;
        struct page **pages;// buffer和vma都是虚拟地址,它们对应的物理页面保存在page中,这是一个数组,每个元素指向一个物理页面
        size_t buffer_size;
        uint32_t buffer_free;
        struct list_head todo;//// 当进程接收到一个进程间通信请求时,Binder驱动就将该请求封
                                // 装成一个工作项,并且加入到进程的待处理工作向队列中,该队列
                                // 使用成员变量todo来描述。
        wait_queue_head_t wait;// 线程池中空闲Binder线程会睡眠在由该成员所描述的等待队列中
                                // 当宿主进程的待处理工作项队列增加新工作项后,驱动会唤醒这
                                // 些线程,以便处理新的工作项
        struct binder_stats stats;
        struct list_head delivered_death;
        int max_threads;// 驱动程序最多可以主动请求进程注册的线程数
        int requested_threads;
        int requested_threads_started;
        int ready_threads;// 进程当前的空闲Binder线程数
        long default_priority;
        struct dentry *debugfs_entry;
        struct binder_context *context;//保存binder的上下文信息
    };
    

    binder_buffer

    该结构体用来描述一个内核缓冲区,该缓冲区用于在进程间传输数据。

    struct binder_buffer {
        // 每一个使用Binder机制的进程在Binder驱动中都有一个内核缓冲区列表,用来保存Binder驱动
        // 程序为它分配的内核缓冲区,entry是该列表的一个节点
        struct list_head entry; /* free and allocated entries by address */
         // 进程使用两个红黑树分别保存使用中以及空闲的内核缓冲区。如果空闲,free=1,
        //rb_node就是空闲内核缓冲区红黑树中的节点,否则是使用中内核缓冲区红黑树中的节点
        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;
        // 保存通信数据,分两种类型:普通数据、Binder对象。驱动程序不关心普通数据,但必须知道里面
        // 的Binder对象,因为要根据它们来维护内核中Binder实体对象和Binder引用对象的生命周期。
        uint8_t data[0];
    };
    

    下面开始看下binder_mmap的流程

    static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
    {
        int ret;
        struct vm_struct *area;
        struct binder_proc *proc = filp->private_data;//在binder_open驱动打开时,filp的private_data指向binder_proc的地址filp->private_data = proc;具体可参考kernel/drivers/staging/android/binder.c
        const char *failure_string;
        //内核进入这个函数时,就已经预先为此次映射分配好了调用进程在用户空间的虚拟地址范围
      //(vma->vm_start,vma->vm_end)
        struct binder_buffer *buffer;//
    //  // 有效性检查:映射的内存不能大于4M
        if ((vma->vm_end - vma->vm_start) > SZ_4M)
            vma->vm_end = vma->vm_start + SZ_4M;
        vma->vm_flags = (vma->vm_flags | VM_DONTCOPY) & ~VM_MAYWRITE;
      //为进程所在的内核空间申请与用户空间同样长度的虚拟地址空间,这段空间用于内核来访问和管理binder内存区域
        area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP);
        //将内核空间地址赋值给proc->buffer,即保存到进程上下文中,对应内核虚拟地址的开始即为binder内存的开始地址
        proc->buffer = area->addr;
          // 计算 "内核空间地址" 和 "进程虚拟地址" 的偏移
        proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer;
         // 为proc->pages分配内存,用于存放内核分配的物理页的页描述指针:
        //struct page,每个物理页对应这样一个struct page结构
        proc->pages = kzalloc(sizeof(proc->pages[0]) * ((vma->vm_end - vma->vm_start) / PAGE_SIZE), GFP_KERNEL);
            // 内核空间的内存大小 = 进程虚拟地址区域(用户空间)的内存大小
        proc->buffer_size = vma->vm_end - vma->vm_start;
        vma->vm_ops = &binder_vm_ops;
          // 将 proc(进程上下文信息) 赋值给vma私有数据
        vma->vm_private_data = proc;
    // 通过调用binder_update_page_range()来分配物理页面。即,将物理内存映射到内核空间 以及 用户空间
        buffer = proc->buffer;
        INIT_LIST_HEAD(&proc->buffers);
            // 将物理内存添加到proc->buffers链表中进行管理。
        list_add(&buffer->entry, &proc->buffers);
        buffer->free = 1;
          // 把分配好内存插入到对应的表中(空闲内存表)
        binder_insert_free_buffer(proc, buffer);
        //异步传输大小只有一半
        proc->free_async_space = proc->buffer_size / 2;
        barrier();
        proc->files = get_files_struct(current);
          // 将用户空间地址信息保存到proc中
        proc->vma = vma;
        proc->vma_vm_mm = vma->vm_mm;
        /*pr_info("binder_mmap: %d %lx-%lx maps %p\n",
             proc->pid, vma->vm_start, vma->vm_end, proc->buffer);*/
        return 0;
    }
    

    binder_update_page_range

    ......
        for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) {
            int ret;
    
            page = &proc->pages[(page_addr - proc->buffer) / PAGE_SIZE];
    
            BUG_ON(*page);
            *page = alloc_page(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO);
            if (*page == NULL) {
                pr_err("%d: binder_alloc_buf failed for page at %p\n",
                    proc->pid, page_addr);
                goto err_alloc_page_failed;
            }
            //内核空间的虚拟内存映射到物理内存
            ret = map_kernel_range_noflush((unsigned long)page_addr,
                        PAGE_SIZE, PAGE_KERNEL, page);
            flush_cache_vmap((unsigned long)page_addr,
                    (unsigned long)page_addr + PAGE_SIZE);
            if (ret != 1) {
                pr_err("%d: binder_alloc_buf failed to map page at %p in kernel\n",
                       proc->pid, page_addr);
                goto err_map_kernel_failed;
            }
            user_page_addr =
                (uintptr_t)page_addr + proc->user_buffer_offset;
                //用户空间的虚拟内存映射到物理内存
            ret = vm_insert_page(vma, user_page_addr, page[0]);
            if (ret) {
                pr_err("%d: binder_alloc_buf failed to map page at %lx in userspace\n",
                       proc->pid, user_page_addr);
                goto err_vm_insert_page_failed;
            }
            /* vm_insert_page does not seem to increment the refcount */
        }
    ....
    

    binder_mmap中关键的代码
    1.area = get_vm_area(size) 这个是分配了一块内存空间
    2.proc->buffer = area->addr proc->buffer是内核空间的数据缓冲区,也就是把刚刚分配的内存空间地址给了内核空间的buffer

    1. proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer;计算 "内核空间地址" 和 "用户空间地址" 的偏移

    所以我们可以得出,如果用户空间想要访问刚刚分配的内核空间内存buffer,那么只需要通过proc的buffer指针+user_buffer_offset就可以访问到vma->vm_start=proc->user_buffer_offset+proc_buffer。


    总结
    binder_mmap给当前进程分配了一块物理内存,并将这块物理内存的应用保存到proc进程的上下文中。

    到目前为止,终于走完了main方法中的binder_open方法。servicermanager进程打开了binder驱动,并在进程中创建了一块内存。

    binder_become_context_manager

    在open_binder方法执行完成后,开始将该进程servicemanager注册为上下文管理器,也就是在binder驱动中,记录servicemanager的进程信息,之后这个进程就会管理系统服务,其他系统服务都要在这个进程中注册后才能使用

    int binder_become_context_manager(struct binder_state *bs)
    {
        return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
    }
    

    看起来代码很简单,只调用了一个ioctl,传入fd,BINDER_SET_CONTEXT_MGR命令,和一个0

    ioctl 是设备驱动程序中设备控制接口函数,一个字符设备驱动通常会实现设备打开、关闭、读、写等功能,在一些需要细分的情境下,如果需要扩展新的功能,通常以增设 ioctl() 命令的方式实现。
    接收参数
    fd 文件描述符
    cmd 交互协议,设备驱动将根据 cmd 执行对应操作
    … 可变参数 arg,依赖 cmd 指定长度以及类型

    这个ioctl最终会调用到binder驱动中的binder_ioctl

    static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
    {
        int ret;
        struct binder_proc *proc = filp->private_data;//获取进程信息
        struct binder_thread *thread;//进程的线程
        unsigned int size = _IOC_SIZE(cmd);
        void __user *ubuf = (void __user *)arg;//用户空间的数据指针
        thread = binder_get_thread(proc);//从proc信息中获取线程
        switch (cmd) {
        case BINDER_WRITE_READ://binder读写数据  arg是数据指针  对于smgr binder_write_read  是 arg  cmd为      filp为驱动的句柄,thread为进程中获取的线程
            ret = binder_ioctl_write_read(filp, cmd, arg, thread);
            if (ret)
                goto err;
            break;
        case BINDER_SET_MAX_THREADS:
            if (copy_from_user(&proc->max_threads, ubuf, sizeof(proc->max_threads))) {
                ret = -EINVAL;
                goto err;
            }
            break;
        case BINDER_SET_CONTEXT_MGR://用户空间ioctl传过来命令,BINDER_SET_CONTEXT_MGR
            ret = binder_ioctl_set_ctx_mgr(filp);
            if (ret)
                goto err;
            break;
        case BINDER_THREAD_EXIT:
            binder_debug(BINDER_DEBUG_THREADS, "%d:%d exit\n",
                     proc->pid, thread->pid);
            binder_free_thread(proc, thread);
            thread = NULL;
            break;
        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项后,驱动调用了binder_ioctl_set_ctx_mgr方法

    static int binder_ioctl_set_ctx_mgr(struct file *filp)
    {
        int ret = 0;
        struct binder_proc *proc = filp->private_data;//从filp中读取进程信息
        struct binder_context *context = proc->context;// binder_context 保存binder上下文管理者的信息。通过binder context可以找到service manager对应的bind node。
        kuid_t curr_euid = current_euid();
        if (context->binder_context_mgr_node) {//已经注册了MGR
            pr_err("BINDER_SET_CONTEXT_MGR already set\n");
            ret = -EBUSY;
            goto out;
        }
        ret = security_binder_set_context_mgr(proc->tsk);
        if (ret < 0)
            goto out;
        if (uid_valid(context->binder_context_mgr_uid)) {
            if (!uid_eq(context->binder_context_mgr_uid, curr_euid)) {
                pr_err("BINDER_SET_CONTEXT_MGR bad uid %d != %d\n",
                       from_kuid(&init_user_ns, curr_euid),
                       from_kuid(&init_user_ns,
                         context->binder_context_mgr_uid));
                ret = -EPERM;
                goto out;
            }
        } else {
            context->binder_context_mgr_uid = curr_euid;
        }
        //创建一个binder_node
        context->binder_context_mgr_node = binder_new_node(proc, 0, 0);
        context->binder_context_mgr_node->local_weak_refs++;
        context->binder_context_mgr_node->local_strong_refs++;
        context->binder_context_mgr_node->has_strong_ref = 1;
        context->binder_context_mgr_node->has_weak_ref = 1;
    out:
        return ret;
    }
    

    我们看下binder_context结构体

    //binder_context是全局唯一的,其中包含了一个binder_context_mgr_node也就是节点信息,可以通过这个节点找到对应的进程
    struct binder_context {
        struct binder_node *binder_context_mgr_node;
        kuid_t binder_context_mgr_uid;
        const char *name;
    };
    

    到目前为止servicemanager已经在binder驱动中注册为一个MGR节点,也就是成为了系统的服务管家进程。其他进程都可以通过proc->context->binder_node来获取到servicemanager来将自己注册。

    binder_loop

    binder_loop(bs, svcmgr_handler)
    

    从字面意思上我们看到,servicemanager进程开启了一个循环,成为了一个守护进程,开始了作为系统服务管家的工作。我们看到接收的参数为一个binder_state结构,和一个svcmgr_handler,这个是一个回调函数,用于处理其他进程发送给servicemanager进程的命令

    int svcmgr_handler(struct binder_state *bs,
                       struct binder_transaction_data *txn,
                       struct binder_io *msg,
                       struct binder_io *reply)
    {
        switch(txn->code) {
        case SVC_MGR_GET_SERVICE:
        case SVC_MGR_CHECK_SERVICE;
        case SVC_MGR_ADD_SERVICE://注意,这个是注册服务命令,也就是AMS通过跨进程调用最终调用到servicemanager的方法,也是我们经过大量的源码分析后最ServerManager.addService的终点
            s = bio_get_string16(msg, &len);
            if (s == NULL) {
                return -1;
            }
            handle = bio_get_ref(msg);
            allow_isolated = bio_get_uint32(msg) ? 1 : 0;
            if (do_add_service(bs, s, len, handle, txn->sender_euid,
                allow_isolated, txn->sender_pid))
                return -1;
            break;
        case SVC_MGR_LIST_SERVICES: {
        return 0;
    }
    

    binder_loop命令调用了驱动的binder_loop命令,先看下相关的结构体binder_write_read 这个结构体的作用其实就是对binder通信数据的封装

    struct binder_write_read {
        binder_size_t       write_size; /* bytes to write */写数据大小
        binder_size_t       write_consumed; /* bytes consumed by driver */已经读取的写入数据
        binder_uintptr_t    write_buffer;//写入数据的buffer地址
        binder_size_t       read_size;  /* bytes to read *///读数据大小
        binder_size_t       read_consumed;  /* bytes consumed by driver *已经读取了多少数据
        binder_uintptr_t    read_buffer;//读取数据的buffer
    };
    

    调用binder_looper

    void binder_loop(struct binder_state *bs, binder_handler func)
    {
        int res;
        struct binder_write_read bwr;//内核中创建一个binder_write_read结构体指针
        uint32_t readbuf[32];//分配读取buffer内存
    
    、////设置写入数据为0,代表没有写入数据
        bwr.write_size = 0;
        bwr.write_consumed = 0;
        bwr.write_buffer = 0;
    
        readbuf[0] = BC_ENTER_LOOPER;
        binder_write(bs, readbuf, sizeof(uint32_t));//设置servicemanager进程中的thread标志位为BINDER_LOOPER_STATE_ENTERED
    
        for (;;) {//无限循环
            bwr.read_size = sizeof(readbuf);
            bwr.read_consumed = 0;
            bwr.read_buffer = (uintptr_t) readbuf;
             //挂起,等待唤醒
            res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);//用户空间发送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);
            if (res == 0) {
                ALOGE("binder_loop: unexpected reply?!\n");
                break;
            }
            if (res < 0) {
                ALOGE("binder_loop: io error %d %s\n", res, strerror(errno));
                break;
            }
        }
    }
    

    关键的是进入无限循环后,bwr的read_buffer内容是 readbuf ,read_size不等于0,通过通过ioctl发送BINDER_WRITE_REA命令给驱动层,其实驱动层接收到这个命令后,会根据bwr中的write_size和read_size来判断是读数据还是写数据,write_size不等0表示进程写入数据到binder驱动中,read_siez不等于0表示驱动读取数据到进程下面我们看ioctl的驱动层实现binder_ioctrl做了什么

    static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
    {
        int ret;
        struct binder_proc *proc = filp->private_data;//获取进程信息
        struct binder_thread *thread;//进程的线程
        unsigned int size = _IOC_SIZE(cmd);
        void __user *ubuf = (void __user *)arg;//用户空间的数据指针
        thread = binder_get_thread(proc);//从proc信息中根据pid查找线程
    
        switch (cmd) {
        case BINDER_WRITE_READ://binder读写数据  arg是数据指针,filp为驱动的句柄,thread为进程中获取的线程
            ret = binder_ioctl_write_read(filp, cmd, arg, thread);
            if (ret)
                goto err;
            break;
    }
    

    binder_ioctl_write_read

    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))) {//从用户空间拷贝数据到内核空间,第一次拷贝数据
            ret = -EFAULT;
            goto out;
        }
    

    这里我们发现了一次拷贝,我们知道binder_write_read就是一个对读写数据进程封装的结构体,


    这样用户空间的数据封装结构体内容被拷贝到了内核空间,继续看代码

    if (bwr.write_size > 0) {//bwr是从用户空间拷贝过来的数据封装,如果发现数据封装中写入数据大于0,则进入binder_thread_write方法,发送数据
            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;
            }
        }
        if (bwr.read_size > 0) {//如果发现,读取数据大于0,则进入binder_thread_read来接收数据
            ret = binder_thread_read(proc, thread, bwr.read_buffer,
                         bwr.read_size,
                         &bwr.read_consumed,
                         filp->f_flags & O_NONBLOCK);
            trace_binder_read_done(ret);
            if (!list_empty(&proc->todo))
                wake_up_interruptible(&proc->wait);
            if (ret < 0) {
                if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
                    ret = -EFAULT;
                goto out;
            }
        }
    

    由于我们知道,bwr结构体中封装的数据read_size是大于0,write_size是小于0的,所以进入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;//读取数据buffer指针
        void __user *ptr = buffer + *consumed;//未读取数据位置 
        void __user *end = buffer + size;//读取数据的结束位置
        int wait_for_proc_work;//进程是否挂起标志
        wait_for_proc_work = thread->transaction_stack == NULL &&list_empty(&thread->todo);//wait_for_proc_work目前为1,表示没有要处理的任务
        if (wait_for_proc_work)//ready_threads为1,进程多了一个空闲线程
            proc->ready_threads++;
        if (wait_for_proc_work) {
            binder_set_nice(proc->default_priority);;//把当前线程的优先级设置为它所属进程的优先级
            if (non_block) {//非阻塞要立刻返回处理结果
                if (!binder_has_proc_work(proc, thread))
                    ret = -EAGAIN;
            } else
                ret = wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread));
        } else {
            if (non_block) {//非阻塞要立刻返回处理结果
                if (!binder_has_thread_work(thread))
                    ret = -EAGAIN;
            } else
                ret = wait_event_freezable(thread->wait, binder_has_thread_work(thread));////睡眠等待直到线程有新的未处理项为止 然后发送端进程就进入了休眠。目的是为了等待目标进程处理完请求后给予它响应。
        }
    //线程被唤醒了
    if (wait_for_proc_work)
            proc->ready_threads--;
        thread->looper &= ~BINDER_LOOPER_STATE_WAITING;
    
        if (ret)
            return ret;
    
        while (1) {
            uint32_t cmd;
            struct binder_transaction_data tr;
            struct binder_work *w;
            struct binder_transaction *t = NULL;
    .......
    

    从代码可以看到servicemanager进程启动时 thread->transaction_stack 肯定未空并且thread->todo也为空,所以wait_for_proc_work为真proc->ready_threads增加1,之后根据线程同步的还是异步的来确定时直接返回还是挂起,binder通信默认时同步的,所以最终会执行到wait_event_freezable_exclusive挂起线程,等待接收唤醒。至此,servicemanager进程终于启动完成了,并且进入了休眠,等待后面其他进程来唤醒了

    相关文章

      网友评论

          本文标题:Android进程间通信(三)——系统服务管理进程service

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