Binder之一:Binder Driver概览:《android框架揭秘读书笔记》

1.第一部分 Binder之一:Binder Driver概览:《android框架揭秘读书笔记》

Binder Driver的分析

1.从进程的角度看服务的使用

客户端使用服务端要经历的三个阶段

  • 服务注册(服务端与Context Manager之间的IPC)
  • 服务检索(客户度与Context Manager之间的IPC)
  • 服务使用(客户端与服务端之间的IPC)

1.1 服务注册

服务注册:把服务注册到Context Manager的服务目录中

Binder之一:Binder Driver概览:《android框架揭秘读书笔记》_第1张图片
服务注册.png

(1)-(3)servicemanager调用open函数,打开binder driver,然后调用mmap函数,在内核空间开辟一块用于接收ipc数据的buffer,再调用ioctl函数进入待机状态。
(4)-(5)为了注册服务,服务端先打开binder driver,而后调用mmap函数,确定一块buffer,用于接收ipc应答数据。
(6)服务端生成ipc数据,ipc数据包含rpc数据(保存要注册的服务名称),prc代码(servicemanager的注册函数ADD_SERVICE),Handle(servicemanager的Handle值为0)三个部分
(7)服务端调用ioctl函数向binder driver传递IPC数据,Binder Driver将数据再传递给Service Manager
(8)-(9)Service Manager分析IPC数据中的RPC代码,并根据RPC代码调用相应的服务注册函数,而后使用RPC数据中的服务名称,将制定的服务注册到目录中。
(10)服务注册完成后,Service Manager会生成应答数据,并传递给服务端,告知服务已经注册。

1.2 服务检索

服务检索:客户端使用服务端的服务时,需要向ServiceManager请求服务的编号,这个过程叫服务检索。
在该阶段,客户端会调用ServiceManager的服务检索函数,客户端首先生成调用ServiceManager的IPC数据,然后传递给Binder Driver,然后Binder Driver将IPC数据传递给ServiceManager,ServiceManager接收到IPC数据中的RPC代码调用相应的函数。

Binder之一:Binder Driver概览:《android框架揭秘读书笔记》_第2张图片
服务检索.png

1:ServiceManager处于待机状态,等待接收IPC数据
2-4:为了向ServiceManager传递IPC数据,客户端打开BinderDriver,而后调用mmap函数准备一块buffer,用于接收IPC应答数据,客户端生成IPC数据,IPC数据包括RPC数据(保存要使用的服务名称),RPC代码(ServiceManger的服务检索函数GET_SERVICE),Handle(ServiceManager的handle为0)三部分。
5:服务端调用ioctl函数向BinderDriver传递数据,BinderDriver根据IPC数据中的Handle,将IPC数据传递给ServiceManager
6:-8:ServiceManager分析IPC数据,根据RPC代码调用服务检索函数,根据RPC数据中保存的服务名称在服务目录中检索服务,查找到服务BInder节点编号,ServiceManager将查找到的Binder节点编号插入到IPC应答数据中。
9:客户端接收IPC应答数据,获得指定服务的Binder节点编号。

1.3 服务使用

Binder之一:Binder Driver概览:《android框架揭秘读书笔记》_第3张图片
服务使用.png

1.服务端进入待机状态,等待接收IPC数据,并且当处理完接收的IPC数据之后,它将再次进入待机状态,等待接收新的数据
2-3:为了使用服务,客户端首先生成IPC数据,IPC数据有三个部分组成,包括RPC代码(指定服务函数),RPC数据(服务函数的参数),Handle(指定哪个服务);然后调用ioctl函数,将IPC数据传递给BinderDriver,最后BinderDriver根据IPC数据中的Handle把IPC数据传递给服务端
4-5:服务端分析IPC数据中的RPC代码,根据RPC代码调用服务函数,IPC数据中的RPC数据是函数参数,在函数的内部使用,当指定的函数执行完毕之后,服务端会生成IPC应答数据,其中包括Binder节点编号,而后将IPC应答数据发送给客户端,告知指定的服务函数已经执行完成。
6:客户端接收IPC数据,并进行处理

2.从BinderDriver角度看服务的使用:BinderDriver的运行过程分析

2.1服务注册

ServiceManager先于其他所有服务运行,它最先使用BinderDriver,进入待机状态,等待服务的注册和客户端对服务的检索请求。

Binder之一:Binder Driver概览:《android框架揭秘读书笔记》_第4张图片
注册服务时BinderDriver的行为.png

1-3:描述的是ServiceManager进入待机状态前使用BinderDriver的过程;binder_proc是一个结构体,这个结构体里包含了一系列指针,以便访问其他结构体,每个进程都有一个binder_proc结构体,通过这个结构体,可以查找到另一个进程,也可以查找到进程接收IPC数据的buffer
1:ServiceManager通过调用open函数调用binder_open函数,BinderDriver使用binder_open函数诶ServiceManager生成并初始化binder_proc
2.ServiceManager在内核开辟一块buffer,用于接收IPC数据:ServiceManger通过调用mmap函数调用BinderDriver的binder_mmap函数,binder_mmap函数在内核空间中分配一块用于接收IPC数据的buffer,并保存到binder_buffer结构体,而binder_buffer结构体会被注册到binder_proc结构体里
3.ServiceManger通过调用ioctl函数调用BinderDriver的binder_ioctl函数,在binder_ioctl中,ServiceManger将处于待机状态,知道另一个进程向其发送IPC数据。
4.服务端通过open调用BinderDriver的binder_open函数,BinderDriver为服务生成binder_proc结构体,并将其初始化。
5.服务端通过mmap函数调用BinderDriver的binder_mmap函数开辟一块空间用于接收IPC应答数据的buffer,并将其保存到binder_buffer结构体,而后服务端生成IPC数据,用来调用ServiceManager的服务注册函数。
6.为了注册服务,服务端将IPC数据传递给BinderDriver;BinderDriver通过IPC数据中的Handle查找到ServiceManager的binde_node和binder_proc
7.为注册的服务生成binder_node结构体,BinderDriver将binder_node分别注册到服务的binder_proc和ServiceManager的binder_proc中。
8.BinderDriver将在第六步中查找到的ServiceManager的binder_proc结构体中查找注册在binder_buffer中的buffer,并传递IPC数据。
9.BinderDriver会记下IPC数据发送进程也就是服务进程的binder_proc结构体,以便在ServiceManager向服务进程发送应答数据时查找服务的binder_proc。
10.BinderDriver让服务处于待机状态,并将ServiceManager从待机状态唤醒,传递IPC数据,ServiceManger从BinderDriver中接收来自服务的IPC数据,ServiceManger根据接收到的数据注册指定的服务,而后生成IPC应答数据,并将其传递给BinderDriver。
11.BinderDriver会在服务的binder_proc结构体中查找binder_buffer里的buffer,并传递IPC应答数据,唤醒服务端,服务端被唤醒后接收IPC数据,并根据IPC数据做相应处理。

2.2 服务检索

Binder之一:Binder Driver概览:《android框架揭秘读书笔记》_第5张图片
服务检索中BinderDriver的行为.png

1.ServiceManager进入待机状态,等待接收IPC数据
2-3客户端生成并初始化binder_proc结构体,然后开辟一块buffer用于接收IPC数据
4.客户端通过ioctl函数调用BinderDriver的binder_ioctl函数,BinderDriver会查找Servicemanager的binder_proc结构体,这里不生成binder_node结构体,只是将IPC数据拷贝到ServiceManager的接收buffer中,并记下客户端的binder_proc结构体,以便查找IPC应答数据的接收端。
5.BinderDriver让客户端处于待机状态,并唤醒处于等待状态的ServiceManger,而后接收IPC数据,ServiceManager在服务目录中查找请求的服务,把服务的编号插入IPC数据中,并将IPC数据传递给BinderDriver。
6.BinderDriver根据接收到的服务编号查找相应的binder_node结构体,而后将查找到的binder_node结构体注册到客户端的binder_proc中,binder_node结构体用于在服务使用阶段查找服务端的binder_proc结构体。
7.BinderDriver将在第六步中注册的binder_node结构体的编号插入到IPC应答数据中,传递给客户端,唤醒客户端,在使用服务时,客户端会将编号作为Handle使用。

2.3服务使用

Binder之一:Binder Driver概览:《android框架揭秘读书笔记》_第6张图片
服务使用时BinderDriver的行为.png

1.服务端处于待机状态,用于接收IPC数据
2.客户端把从服务检索阶段获取到的Binder节点编号保存到IPC数据的Handle中,生成IPC数据后传递给BinderDriver,BinderDriver根据IPC数据中的Handle查找相应的binder_node结构体,并根据此查找到注册有binder_node结构体的服务进程的binder_proc结构体,然后通过注册在binder_proc结构体中的binder_buffer结构体,将IPC数据拷贝的buffer中
3.BinderDriver让客户端进入待机状态,唤醒处于待机状态的服务端,服务端接收IPC数据,并通过IPC数据中的RPC代码RPC数据调用相应的服务函数
4.服务函数执行完毕后,服务端会生成IPC应答数据,并将其传递给BinderDriver,然后BinderDriver将数据传递给客户端

3.BinderDriver的函数分析

3.1 binder_open函数

binder_open函数为打开BinderDriver的进程生成并初始化结构体binder_proc;并且初始化等待队列(用来将进程切换到待机状态下)和todo队列(用来在进程被唤醒后执行其中的任务)

Binder之一:Binder Driver概览:《android框架揭秘读书笔记》_第7张图片
binder_open函数的功能.png

3.1.1 binder_proc结构体

binder_proc结构体用来管理BinderIPC所需要的各种信息,包括打开BinderDriver的进程信息,接收IPC数据的buffer信息等,此外,binder_proc拥有其他结构体的指针,为访问其他结构体提供了方便,被称为BinderDriver的根结构体

Binder之一:Binder Driver概览:《android框架揭秘读书笔记》_第8张图片
binder_proc结构体.png

3.1.2 binder_open源码分析


static HLIST_HEAD(binder_procs);

static int binder_open(struct inode *nodp, struct file *filp)
{
    struct binder_proc *proc;

    binder_debug(BINDER_DEBUG_OPEN_CLOSE, "binder_open: %d:%d\n",
             current->group_leader->pid, current->pid);
         //为binder_proc开辟内存空间,也就是初始化binder_proc
    proc = kzalloc(sizeof(*proc), GFP_KERNEL);
    if (proc == NULL)
        return -ENOMEM;
    get_task_struct(current);
    proc->tsk = current;//1
    INIT_LIST_HEAD(&proc->todo);//2
    init_waitqueue_head(&proc->wait);//2
    proc->default_priority = task_nice(current);
    mutex_lock(&binder_lock);
    binder_stats_created(BINDER_STAT_PROC);
        //把当前生成的proc加入到binder_procs中
    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
    mutex_unlock(&binder_lock);

    if (binder_debugfs_dir_entry_proc) {//4
        char strbuf[11];
        snprintf(strbuf, sizeof(strbuf), "%u", proc->pid);
        proc->debugfs_entry = debugfs_create_file(strbuf, S_IRUGO,
            binder_debugfs_dir_entry_proc, proc, &binder_proc_fops);
    }

    return 0;
}

1.task_struct结构体中包含打开BinderDriver的进程的信息,proc->tsk = current将task_struct结构体变量current注册到binder_proc结构体变量的tsk成员中,该变量用来输出当前正在进行BinderIPC的进程。
2.BinderDriver接收到IPC数据后,将要执行的任务保存到proc->todo列表中,第二条语句初始化待机队列,以便将打开的BinderDriver进程切换到待机状态。
3.将proc放到flip中,以便在后面使用,后面使用时只需要用proc=flip->private_data就可以获取到binder_proc
4.在proc/binder/proc目录下生成文件,以便初始化binder_proc结构体或显示与BInderIPC相关的信息,在Android启动后,查看该目录和目录下的文件,可以查看到BinderIPC的进程信息。

3.2 binder_mmap函数

Binder之一:Binder Driver概览:《android框架揭秘读书笔记》_第9张图片
映射.png
Binder之一:Binder Driver概览:《android框架揭秘读书笔记》_第10张图片
binder_mmap函数功能.png
static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
{
    int ret;
    struct vm_struct *area;
        //1:
    struct binder_proc *proc = filp->private_data;
    const char *failure_string;
    struct binder_buffer *buffer;

    if ((vma->vm_end - vma->vm_start) > SZ_4M)
        vma->vm_end = vma->vm_start + SZ_4M;

    binder_debug(BINDER_DEBUG_OPEN_CLOSE,
             "binder_mmap: %d %lx-%lx (%ld K) vma %lx pagep %lx\n",
             proc->pid, vma->vm_start, vma->vm_end,
             (vma->vm_end - vma->vm_start) / SZ_1K, vma->vm_flags,
             (unsigned long)pgprot_val(vma->vm_page_prot));

    if (vma->vm_flags & FORBIDDEN_MMAP_FLAGS) {
        ret = -EPERM;
        failure_string = "bad vm_flags";
        goto err_bad_arg;
    }
    vma->vm_flags = (vma->vm_flags | VM_DONTCOPY) & ~VM_MAYWRITE;

    if (proc->buffer) {
        ret = -EBUSY;
        failure_string = "already mapped";
        goto err_already_mapped;
    }

    area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP);//2
    if (area == NULL) {
        ret = -ENOMEM;
        failure_string = "get_vm_area";
        goto err_get_vm_area_failed;
    }
    proc->buffer = area->addr; //3
    proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer;//4

#ifdef CONFIG_CPU_CACHE_VIPT
    if (cache_is_vipt_aliasing()) {
        while (CACHE_COLOUR((vma->vm_start ^ (uint32_t)proc->buffer))) {
            printk(KERN_INFO "binder_mmap: %d %lx-%lx maps %p bad alignment\n", proc->pid, vma->vm_start, vma->vm_end, proc->buffer);
            vma->vm_start += PAGE_SIZE;
        }
    }
#endif
    proc->pages = kzalloc(sizeof(proc->pages[0]) * ((vma->vm_end - vma->vm_start) / PAGE_SIZE), GFP_KERNEL);
    if (proc->pages == NULL) {
        ret = -ENOMEM;
        failure_string = "alloc page array";
        goto err_alloc_pages_failed;
    }
    proc->buffer_size = vma->vm_end - vma->vm_start;

    vma->vm_ops = &binder_vm_ops;
    vma->vm_private_data = proc;
        //5
    if (binder_update_page_range(proc, 1, proc->buffer, proc->buffer + PAGE_SIZE, vma)) {
        ret = -ENOMEM;
        failure_string = "alloc small buf";
        goto err_alloc_small_buf_failed;
    }
    buffer = proc->buffer;//6
    INIT_LIST_HEAD(&proc->buffers);
    list_add(&buffer->entry, &proc->buffers);
    buffer->free = 1;
    binder_insert_free_buffer(proc, buffer);//7
    proc->free_async_space = proc->buffer_size / 2;
    barrier();
    proc->files = get_files_struct(current);
    proc->vma = vma;

    /*printk(KERN_INFO "binder_mmap: %d %lx-%lx maps %p\n",
         proc->pid, vma->vm_start, vma->vm_end, proc->buffer);*/
    return 0;

err_alloc_small_buf_failed:
    kfree(proc->pages);
    proc->pages = NULL;
err_alloc_pages_failed:
    vfree(proc->buffer);
    proc->buffer = NULL;
err_get_vm_area_failed:
err_already_mapped:
err_bad_arg:
    printk(KERN_ERR "binder_mmap: %d %lx-%lx %s failed %d\n",
           proc->pid, vma->vm_start, vma->vm_end, failure_string, ret);
    return ret;
}

1.binder_mmap函数的第一个参数是file结构体指针,它指向BinderDriver的文件描述符,file结构体指针的private_data成员变量是当前进程的binder_proc结构体,在binder_open中已经被赋值;binder_mmap第二个参数是用户空间的buffer信息,由mmap函数与内核空间映射在一起。
2.get_vm_area是一个内核函数,通过该函数可以向系统申请一块可用的虚拟内存空间,也就是在内核中申请并保留一块连续的内核虚拟内存区域,若内核的VMALLOC区域有一块符合指定尺寸的空间,则生成一个vm_struct结构体,在把空间的起始地址付给vm_struct结构体的addr变量后返回
3.调用get_vm_area函数将返回vm_struct结构体,并将其赋值给area变量,此时area->addr中存放着内核空间的起始地址,使用proc->buffer=area->addr后,proc->buffer其实存放了内核空间中接收IPC数据的Buffer的起始地址。
4.这行代码用来计算用户空间buffer地址与内核空间buffer地址的偏移。proc->user_buffer_offset是int类型变量,是一个负值,偏移信息用来告诉进程用户空间的buffer地址,在BinderDriver接收IPC数据后,用户空间的Buffer与保存IPC数据的内核空间映射在一起。
5.调用binder_update_page_range函数,分配物理页,并将存在于物理内存与虚拟内存的内核空间的接收Buffer与用户空间的接收Buffer映射起来
6.该语句用来将IPC数据保存到binder_buffer中
7.binder_insert_free_buffer函数,将bindeer_buffer结构体注册到当前进程的binder_proc结构体的free_buffers中,free_buffers变量是指接收IPC数据的buffer,根据IPC数据的大小,分配不同的free_buffers,以便IPC接收IPC数据

3.3 binder_ioctl函数

Binder之一:Binder Driver概览:《android框架揭秘读书笔记》_第11张图片
binder_ioctl所支持的命令.png
Binder之一:Binder Driver概览:《android框架揭秘读书笔记》_第12张图片
binder_ioctl收发数据的流程.png

3.3.1 binder_write_read结构体

struct binder_write_read {
       //用于发送数据
    signed long write_size; /* bytes to write */
    signed long write_consumed; /* bytes consumed by driver */
    unsigned long   write_buffer;
        //用于接收数据
    signed long read_size;  /* bytes to read */
    signed long read_consumed;  /* bytes consumed by driver */
    unsigned long   read_buffer;
};

3.3.2 binder_transaction_data结构体(Handle,RPC代码,RPC数据保存在该结构体中)

Binder之一:Binder Driver概览:《android框架揭秘读书笔记》_第13张图片
binder_transaction_data与IPC数据的关系.png
Binder之一:Binder Driver概览:《android框架揭秘读书笔记》_第14张图片
binder_write_read与binder_transaction_data的关系.png

以发送IPC数据进程的buffer来分析
binder_write_read结构体中包含着用户空间生成的IPC数据,BinderDriver也拥有一个相同的结构体,用户空间设置完binder_write_read结构体数据后,调用ioctl函数传递给BInderDrier,BinderDrier调用copy_from_user函数将用户空间中的数据拷贝到自身的binder_write_read结构体中,相反,这IPC应答阶段,BinderDrier将调用copy_to_user函数,将自身的binder_write_read结构体中的数据拷贝到用户空间。

struct binder_transaction_data {
    /* The first two are only used for bcTRANSACTION and brTRANSACTION,
     * identifying the target and contents of the transaction.
     */
    union {
        size_t  handle; /* target descriptor of command transaction */
        void    *ptr;   /* target descriptor of return transaction */
    } target;
    void        *cookie;    /* target object cookie */
    unsigned int    code;       /* transaction command */

    /* General information about the transaction. */
    unsigned int    flags;
    pid_t       sender_pid;
    uid_t       sender_euid;
    size_t      data_size;  /* number of bytes of data */
    size_t      offsets_size;   /* number of bytes of offsets */

    /* If this transaction is inline, the data immediately
     * follows here; otherwise, it ends with a pointer to
     * the data buffer.
     */
    union {
        struct {
            /* transaction data */
            const void  *buffer;
            /* offsets from buffer to flat_binder_object structs */
            const void  *offsets;
        } ptr;
        uint8_t buf[8];
    } data;
};

3.3.3 概览

Binder之一:Binder Driver概览:《android框架揭秘读书笔记》_第15张图片
binder_ioctl收发数据的流程.png

Binder架构

http://gityuan.com/images/binder/java_binder/java_binder.jpg

Binder进程和线程

http://gityuan.com/images/binder/summary/binder_proc_relation.png


参考
[Binder系列总结](http://gityuan.com/2015/11/28/binder-summary/)
《android框架揭秘》

你可能感兴趣的:(Binder之一:Binder Driver概览:《android框架揭秘读书笔记》)