Binder驱动逻辑

Binder驱动:

所有的用户控件方法都会经过系统调用对应到binder驱动方法,如: open-> __open() -> binder_open()

 binder_init

static int __init binder_init(void)
{
    int ret;
    //创建名为binder的工作队列
    binder_deferred_workqueue = create_singlethread_workqueue("binder");
    ...

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

     // 注册misc设备
    ret = misc_register(&binder_miscdev);
    if (binder_debugfs_dir_entry_root) {
        ... //在debugfs文件系统中创建一系列的文件
    }
    return ret;
}

主要工作是为了注册misc设备

binder_open

static int binder_open(struct inode *nodp, struct file *filp)
{
    struct binder_proc *proc; // binder进程 【见附录3.1】

    proc = kzalloc(sizeof(*proc), GFP_KERNEL); // 为binder_proc结构体在分配kernel内存空间
    if (proc == NULL)
        return -ENOMEM;
    get_task_struct(current);
    proc->tsk = current;   //将当前线程的task保存到binder进程的tsk
    INIT_LIST_HEAD(&proc->todo); //初始化todo列表
    init_waitqueue_head(&proc->wait); //初始化wait队列
    proc->default_priority = task_nice(current);  //将当前进程的nice值转换为进程优先级

    binder_lock(__func__);   //同步锁,因为binder支持多线程访问
    binder_stats_created(BINDER_STAT_PROC); //BINDER_PROC对象创建数加1
    hlist_add_head(&proc->proc_node, &binder_procs); //将proc_node节点添加到binder_procs为表头的队列
    proc->pid = current->group_leader->pid;
    INIT_LIST_HEAD(&proc->delivered_death); //初始化已分发的死亡通知列表
    filp->private_data = proc;       //file文件指针的private_data变量指向binder_proc数据
    binder_unlock(__func__); //释放同步锁

    return 0;
}

打开binder驱动设备,创建binder_proc对象,并把当前进程等信息保存到binder_proc对象,该对象管理IPC所需的各种信息并拥有其他结构体的根结构体;再把binder_proc对象保存到文件指针filp,以及把binder_proc加入到全局链表binder_procs

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;
    const char *failure_string;
    struct binder_buffer *buffer;  //【见附录3.9】

    if (proc->tsk != current)
        return -EINVAL;

    if ((vma->vm_end - vma->vm_start) > SZ_4M)
        vma->vm_end = vma->vm_start + SZ_4M;  //保证映射内存大小不超过4M

    mutex_lock(&binder_mmap_lock);  //同步锁
    //采用IOREMAP方式,分配一个连续的内核虚拟空间,与进程虚拟空间大小一致
    area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP);
    if (area == NULL) {
        ret = -ENOMEM;
        failure_string = "get_vm_area";
        goto err_get_vm_area_failed;
    }
    proc->buffer = area->addr; //指向内核虚拟空间的地址
    //地址偏移量 = 用户虚拟地址空间 - 内核虚拟地址空间
    proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer;
    mutex_unlock(&binder_mmap_lock); //释放锁

    ...
    //分配物理页的指针数组,数组大小为vma的等效page个数;
    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;

    //分配物理页面,同时映射到内核空间和进程空间,先分配1个物理页 【见小节2.3.1】
    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; //binder_buffer对象 指向proc的buffer地址
    INIT_LIST_HEAD(&proc->buffers); //创建进程的buffers链表头
    list_add(&buffer->entry, &proc->buffers); //将binder_buffer地址 加入到所属进程的buffers队列
    buffer->free = 1;
    //将空闲buffer放入proc->free_buffers中
    binder_insert_free_buffer(proc, buffer);
    //异步可用空间大小为buffer总大小的一半。
    proc->free_async_space = proc->buffer_size / 2;
    barrier();
    proc->files = get_files_struct(current);
    proc->vma = vma;
    proc->vma_vm_mm = vma->vm_mm;
    return 0;

    ...// 错误flags跳转处,free释放内存之类的操作
    return ret;
}

主要功能:首先在内核虚拟地址空间,申请一块与用户虚拟内存相同大小的内存;然后再申请1个page大小的物理内存,再将同一块物理内存分别映射到内核虚拟地址空间和用户虚拟内存空间,从而实现了用户空间的Buffer和内核空间的Buffer同步操作的功能。binder_mmap通过加锁,保证一次只有一个进程分配内存,保证多进程间的并发访问。其中user_buffer_offset是虚拟进程地址与虚拟内核地址的差值(该值为负数)。也就是说同一物理地址,当内核地址为kernel_addr,则进程地址为proc_addr = kernel_addr + user_buffer_offset。

 

虚拟进程地址空间(vm_area_struct)和虚拟内核地址空间(vm_struct)都映射到同一块物理内存空间。当Client端与Server端发送数据时,Client(作为数据发送端)先从自己的进程空间把IPC通信数据copy_from_user拷贝到内核空间,而Server端(作为数据接收端)与内核共享数据,不再需要拷贝数据,而是通过内存地址空间的偏移量,即可获悉内存地址,整个过程只发生一次内存拷贝。一般地做法,需要Client端进程空间拷贝到内核空间,再由内核空间拷贝到Server进程空间,会发生两次拷贝。

对于进程和内核虚拟地址映射到同一个物理内存的操作是发生在数据接收端,而数据发送端还是需要将用户态的数据复制到内核态。到此,可能有读者会好奇,为何不直接让发送端和接收端直接映射到同一个物理空间,那样就连一次复制的操作都不需要了,0次复制操作那就与Linux标准内核的共享内存的IPC机制没有区别了,对于共享内存虽然效率高,但是对于多进程的同步问题比较复杂,而管道/消息队列等IPC需要复制2两次,效率较低。

ioctl(文件描述符,ioctl命令,数据类型)

ioctl命令数据类型操作
BINDER_WRITE_READstruct binder_write_read收发Binder IPC数据
BINDER_SET_MAX_THREADS__u32设置Binder线程最大个数
BINDER_SET_CONTEXT_MGR__s32设置Service Manager节点
BINDER_THREAD_EXIT__s32释放Binder线程
BINDER_VERSIONstruct binder_version获取Binder版本信息
BINDER_SET_IDLE_TIMEOUT__s64没有使用
BINDER_SET_IDLE_PRIORITY__s32没有使用

这些命令中BINDER_WRITE_READ命令使用率最为频繁,也是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;  // binder线程
    unsigned int size = _IOC_SIZE(cmd);
    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;

    binder_lock(__func__);
    //获取binder_thread【见2.4.1】
    thread = binder_get_thread(proc);
    if (thread == NULL) {
        ret = -ENOMEM;
        goto err;
    }

    switch (cmd) {
    case BINDER_WRITE_READ:  //进行binder的读写操作
        ret = binder_ioctl_write_read(filp, cmd, arg, thread); //【见2.4.2】
        if (ret)
            goto err;
        break;
    case BINDER_SET_MAX_THREADS: //设置binder最大支持的线程数
        if (copy_from_user(&proc->max_threads, ubuf, sizeof(proc->max_threads))) {
            ret = -EINVAL;
            goto err;
        }
        break;
    case BINDER_SET_CONTEXT_MGR: //成为binder的上下文管理者,也就是ServiceManager成为守护进程
        ret = binder_ioctl_set_ctx_mgr(filp);
        if (ret)
            goto err;
        break;
    case BINDER_THREAD_EXIT:   //当binder线程退出,释放binder线程
        binder_free_thread(proc, thread);
        thread = NULL;
        break;
    case BINDER_VERSION: {  //获取binder的版本号
        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;
    }
    default:
        ret = -EINVAL;
        goto err;
    }
    ret = 0;
err:
    if (thread)
        thread->looper &= ~BINDER_LOOPER_STATE_NEED_RETURN;
    binder_unlock(__func__);
    wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);

err_unlocked:
    trace_binder_ioctl_done(ret);
    return ret;
}
static struct binder_thread *binder_get_thread(struct binder_proc *proc)
{
    struct binder_thread *thread = NULL;
    struct rb_node *parent = NULL;
    struct rb_node **p = &proc->threads.rb_node;
    while (*p) {  //根据当前进程的pid,从binder_proc中查找相应的binder_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
            break;
    }
    if (*p == NULL) {
        thread = kzalloc(sizeof(*thread), GFP_KERNEL); //新建binder_thread结构体
        if (thread == NULL)
            return NULL;
        binder_stats_created(BINDER_STAT_THREAD);
        thread->proc = proc;
        thread->pid = current->pid;  //保存当前进程(线程)的pid
        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 |= BINDER_LOOPER_STATE_NEED_RETURN;
        thread->return_error = BR_OK;
        thread->return_error2 = BR_OK;
    }
    return thread;
}

从binder_proc中查找binder_thread,如果当前线程已经加入到proc的线程队列则直接返回,如果不存在则创建binder_thread,并将当前线程添加到当前的proc。

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 (size != sizeof(struct binder_write_read)) {
        ret = -EINVAL;
        goto out;
    }
    if (copy_from_user(&bwr, ubuf, sizeof(bwr))) { //把用户空间数据ubuf拷贝到bwr
        ret = -EFAULT;
        goto out;
    }

    if (bwr.write_size > 0) {
        //当写缓存中有数据,则执行binder写操作
        ret = binder_thread_write(proc, thread,
                      bwr.write_buffer, bwr.write_size, &bwr.write_consumed);
        trace_binder_write_done(ret);
        if (ret < 0) { //当写失败,再将bwr数据写回用户空间,并返回
            bwr.read_consumed = 0;
            if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
                ret = -EFAULT;
            goto out;
        }
    }
    if (bwr.read_size > 0) {
        //当读缓存中有数据,则执行binder读操作
        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) { //当读失败,再将bwr数据写回用户空间,并返回
            if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
                ret = -EFAULT;
            goto out;
        }
    }

    if (copy_to_user(ubuf, &bwr, sizeof(bwr))) { //将内核数据bwr拷贝到用户空间ubuf
        ret = -EFAULT;
        goto out;
    }
out:
    return ret;
}

对于ioctl()方法中,传递进来的命令是cmd = BINDER_WRITE_READ时执行该方法,arg是一个binder_write_read结构体。

流程:

  • 首先,把用户空间数据ubuf拷贝到内核空间bwr;
  • 当bwr写缓存有数据,则执行binder_thread_write;当写失败则将bwr数据写回用户空间并退出;
  • 当bwr读缓存有数据,则执行binder_thread_read;当读失败则再将bwr数据写回用户空间并退出;
  • 最后,把内核数据bwr拷贝到用户空间ubuf。

这里涉及两个核心方法binder_thread_write()binder_thread_read()方法,

序号结构体名称解释
1binder_procbinder进程每个进程调用open()打开binder驱动都会创建该结构体,用于管理IPC所需的各种信息
2binder_threadbinder线程对应于上层的binder线程
3binder_nodebinder实体对应于BBinder对象,记录BBinder的进程、指针、引用计数等
4binder_refbinder引用对应于BpBinder对象,记录BpBinder的引用计数、死亡通知、BBinder指针等
5binder_ref_deathbinder死亡引用记录binder死亡的引用信息
6binder_write_readbinder读写记录buffer中读和写的数据信息
7binder_transaction_databinder事务数据记录传输数据内容,比如发送方pid/uid,RPC数据
8flat_binder_objectbinder扁平对象Binder对象在两个进程间传递的扁平结构
9binder_bufferbinder内存调用mmap()创建用于Binder传输数据的缓存区
10binder_transactionbinder事务记录传输事务的发送方和接收方线程、进程等
11binder_workbinder工作记录binder工作类型
12binder_statebinder状态 
struct binder_transaction_data {
    union {
        __u32    handle;       //binder_ref(即handle)
        binder_uintptr_t ptr;     //Binder_node的内存地址
    } target;  //RPC目标
    binder_uintptr_t    cookie;    //BBinder指针
    __u32        code;        //RPC代码,代表Client与Server双方约定的命令码

    __u32            flags; //标志位,比如TF_ONE_WAY代表异步,即不等待Server端回复
    pid_t        sender_pid;  //发送端进程的pid
    uid_t        sender_euid; //发送端进程的uid
    binder_size_t    data_size;    //data数据的总大小
    binder_size_t    offsets_size; //IPC对象的大小

    union {
        struct {
            binder_uintptr_t    buffer; //数据区起始地址
            binder_uintptr_t    offsets; //数据区IPC对象偏移量
        } ptr;
        __u8    buf[8];
    } data;   //RPC数据
};
  • target: 对于BpBinder则使用handle,对于BBinder则使用ptr,故使用union数据类型来表示;
  • code: 比如注册服务过程code为ADD_SERVICE_TRANSACTION,又比如获取服务code为CHECK_SERVICE_TRANSACTION
  • data:代表整个数据区,其中data.ptr指向的是传递给Binder驱动的数据区的起始地址,data.offsets指的是数据区中IPC数据地址的偏移量。
  • cookie: 记录着BBinder指针。
  • data_size:代表本次传输的parcel数据的大小;
  • offsets_size: 代表传递的IPC对象的大小;根据这个可以推测出传递了多少个binder对象。
    • 对于64位IPC,一个IPC对象大小等于8;
    • 对于32位IPC,一个IPC对象大小等于4;

flat_binder_object

类型成员变量解释
__u32type类型
__u32flags记录优先级、文件描述符许可
binder_uintptr_tbinder(union)当传递的是binder_node时使用,指向binder_node在应用程序的地址
__u32handle(union)当传递的是binder_ref时使用,存放Binder在进程中的引用号
binder_uintptr_tcookie只对binder_node有效,存放binder_node的额外数据

此处的类型type的可能取值来自于enum,成员如下:

成员变量解释
BINDER_TYPE_BINDERbinder_node的强引用
BINDER_TYPE_WEAK_BINDERbinder_node的弱引用
BINDER_TYPE_HANDLEbinder_ref强引用
BINDER_TYPE_WEAK_HANDLEbinder_ref弱引用
BINDER_TYPE_FDbinder文件描述符

说明:

  • 当type等于BINDER_TYPE_BINDER或BINDER_TYPE_WEAK_BINDER类型时, 代表Server进程向ServiceManager进程注册服务,则创建binder_node对象;
  • 当type等于BINDER_TYPE_HANDLE或BINDER_TYPE_WEAK_HEANDLE类型时, 代表Client进程向Server进程请求代理,则创建binder_ref对象;
  • 当type等于BINDER_TYPE_FD类型时, 代表进程向另一个进程发送文件描述符,只打开文件,则无需创建任何对象。

Client进程通过RPC(Remote Procedure Call Protocol)与Server通信,可以简单地划分为三层,驱动层、IPC层、业务层。handle、RPC数据、代码、协议这4项组成了IPC层的数据,通过IPC层进行数据传输;而真正在Client和Server两端建立通信的基础设施便是Binder Driver。

 

例如,当名为BatteryStatsService的Client向ServiceManager注册服务的过程中,IPC层的数据组成为:Handle=0,RPC代码为ADD_SERVICE_TRANSACTION,RPC数据为BatteryStatsService,Binder协议为BC_TRANSACTION

binder请求码,是用enum binder_driver_command_protocol来定义的,是用于应用程序向binder驱动设备发送请求消息,应用程序包含Client端和Server端,以BC_开头,总17条;(-代表目前不支持的请求码)

请求码参数类型作用
BC_TRANSACTIONbinder_transaction_dataClient向Binder驱动发送请求数据
BC_REPLYbinder_transaction_dataServer向Binder驱动发送请求数据
BC_FREE_BUFFERbinder_uintptr_t(指针)释放内存
BC_INCREFS__u32(descriptor)binder_ref弱引用加1操作
BC_DECREFS__u32(descriptor)binder_ref弱引用减1操作
BC_ACQUIRE__u32(descriptor)binder_ref强引用加1操作
BC_RELEASE__u32(descriptor)binder_ref强引用减1操作
BC_ACQUIRE_DONEbinder_ptr_cookiebinder_node强引用减1操作
BC_INCREFS_DONEbinder_ptr_cookiebinder_node弱引用减1操作
BC_REGISTER_LOOPER无参数创建新的looper线程
BC_ENTER_LOOPER无参数应用线程进入looper
BC_EXIT_LOOPER无参数应用线程退出looper
BC_REQUEST_DEATH_NOTIFICATIONbinder_handle_cookie注册死亡通知
BC_CLEAR_DEATH_NOTIFICATIONbinder_handle_cookie取消注册的死亡通知
BC_DEAD_BINDER_DONEbinder_uintptr_t(指针)已完成binder的死亡通知
BC_ACQUIRE_RESULT--
BC_ATTEMPT_ACQUIRE--

binder响应码,是用enum binder_driver_return_protocol来定义的,是binder设备向应用程序回复的消息,,应用程序包含Client端和Server端,以BR_开头,总18条;

响应码参数类型作用
BR_ERROR__s32操作发生错误
BR_OK无参数操作完成
BR_NOOP无参数不做任何事
BR_SPAWN_LOOPER无参数创建新的Looper线程
BR_TRANSACTIONbinder_transaction_dataBinder驱动向Server端发送请求数据
BR_REPLYbinder_transaction_dataBinder驱动向Client端发送回复数据
BR_TRANSACTION_COMPLETE无参数对请求发送的成功反馈
BR_DEAD_REPLY无参数回复失败,往往是线程或节点为空
BR_FAILED_REPLY无参数回复失败,往往是transaction出错导致
BR_INCREFSbinder_ptr_cookiebinder_ref弱引用加1操作(Server端)
BR_DECREFSbinder_ptr_cookiebinder_ref弱引用减1操作(Server端)
BR_ACQUIREbinder_ptr_cookiebinder_ref强引用加1操作(Server端)
BR_RELEASEbinder_ptr_cookiebinder_ref强引用减1操作(Server端)
BR_DEAD_BINDERbinder_uintptr_t(指针)Binder驱动向client端发送死亡通知
BR_CLEAR_DEATH_NOTIFICATION_DONEbinder_uintptr_t(指针)BC_CLEAR_DEATH_NOTIFICATION命令对应的响应码
BR_ACQUIRE_RESULT--
BR_ATTEMPT_ACQUIRE--
BR_FINISHED--

先列举一次完整的Binder通信过程:

Binder协议包含在IPC数据中,分为两类:

  1. BINDER_COMMAND_PROTOCOL:binder请求码,以”BC_“开头,简称BC码,用于从IPC层传递到Binder Driver层;
  2. BINDER_RETURN_PROTOCOL :binder响应码,以”BR_“开头,简称BR码,用于从Binder Driver层传递到IPC层;

Binder IPC通信至少是两个进程的交互:

  • client进程执行binder_thread_write,根据BC_XXX命令,生成相应的binder_work;
  • server进程执行binder_thread_read,根据binder_work.type类型,生成BR_XXX,发送到用户空间处理。

 

其中binder_work.type共有6种类型:

 

BINDER_WORK_TRANSACTION //最常见类型
BINDER_WORK_TRANSACTION_COMPLETE
BINDER_WORK_NODE
BINDER_WORK_DEAD_BINDER
BINDER_WORK_DEAD_BINDER_AND_CLEAR
BINDER_WORK_CLEAR_DEATH_NOTIFICATION

请求处理过程是通过binder_thread_write()方法,该方法用于处理Binder协议中的请求码。当binder_buffer存在数据,binder线程的写操作循环执行。

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) {
        get_user(cmd, (uint32_t __user *)ptr);//获取IPC数据中的Binder协议(BC码)
        switch (cmd) {
            case BC_INCREFS: ...
            case BC_ACQUIRE: ...
            case BC_RELEASE: ...
            case BC_DECREFS: ...
            case BC_INCREFS_DONE: ...
            case BC_ACQUIRE_DONE: ...
            case BC_FREE_BUFFER: ... break;
            
            case BC_TRANSACTION:
            case BC_REPLY: {
                struct binder_transaction_data tr;
                copy_from_user(&tr, ptr, sizeof(tr)); //拷贝用户空间tr到内核
                // 【见小节2.2.1】
                binder_transaction(proc, thread, &tr, cmd == BC_REPLY);
                break;

            case BC_REGISTER_LOOPER: ...
            case BC_ENTER_LOOPER: ...
            case BC_EXIT_LOOPER: ...
            case BC_REQUEST_DEATH_NOTIFICATION: ...
            case BC_CLEAR_DEATH_NOTIFICATION:  ...
            case BC_DEAD_BINDER_DONE: ...
            }
        }
    }
}

对于请求码为BC_TRANSACTIONBC_REPLY时,会执行binder_transaction()方法,这是最为频繁的操作。 对于其他命令则不同。

static void binder_transaction(struct binder_proc *proc,
               struct binder_thread *thread,
               struct binder_transaction_data *tr, int reply){
    //根据各种判定,获取以下信息:
    struct binder_thread *target_thread; //目标线程
    struct binder_proc *target_proc;    //目标进程
    struct binder_node *target_node;    //目标binder节点
    struct list_head *target_list;      //目标TODO队列
    wait_queue_head_t *target_wait;     //目标等待队列
    ...
    
    //分配两个结构体内存
    struct binder_transaction *t = kzalloc(sizeof(*t), GFP_KERNEL);
    struct binder_work *tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL);
    //从target_proc分配一块buffer【见小节3.2】
    t->buffer = binder_alloc_buf(target_proc, tr->data_size,
        tr->offsets_size, !reply && (t->flags & TF_ONE_WAY));

    for (; offp < off_end; offp++) {
         struct flat_binder_object *fp;
        fp = (struct flat_binder_object *)(t->buffer->data + *offp);
        off_min = *offp + sizeof(struct flat_binder_object);
        switch (fp->type) {
        case BINDER_TYPE_BINDER: ...
        case BINDER_TYPE_WEAK_BINDER: ...
        case BINDER_TYPE_HANDLE: ...
        case BINDER_TYPE_WEAK_HANDLE: ...
        case BINDER_TYPE_FD: ...
        }
    }
    //向目标进程的target_list添加BINDER_WORK_TRANSACTION事务
    t->work.type = BINDER_WORK_TRANSACTION;
    list_add_tail(&t->work.entry, target_list);
    //向当前线程的todo队列添加BINDER_WORK_TRANSACTION_COMPLETE事务
    tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
    list_add_tail(&tcomplete->entry, &thread->todo);
    if (target_wait)
        wake_up_interruptible(target_wait);
    return;
}

路由过程:handle -> ref -> target_node -> target_proc

  • reply的过程会找到target_thread;
  • 非reply则一般找到target_proc;
  • 对特殊的嵌套binder call会根据transaction_stack来决定是插入事务到目标线程还是目标进程。

binder_transaction

 

类型成员变量解释
intdebug_id用于调试
struct binder_workworkbinder工作类型
struct binder_thread *from发送端线程
struct binder_transaction *from_parent上一个事务
struct binder_proc *to_proc接收端进程
struct binder_thread *to_thread接收端线程
struct binder_transaction *to_parent下一个事务
unsignedneed_reply是否需要回复
struct binder_buffer *buffer数据buffer
unsigned intcode通信方法,比如startService
unsigned intflags标志,比如是否oneway
longpriority优先级
longsaved_priority保存的优先级
kuid_tsender_euid发送端uid

 

执行binder_transaction()过程创建的结构体

  • debug_id:是一个全局静态变量,每当创建一个binder_transactionbinder_nodebinder_ref对象,则++debug_id
  • from与to_thread是一对,分别是发送端线程和接收端线程;
  • from_parent与to_parent是一对,分别是上一个和下一个binder_transaction,组成一个链表。
    • 执行binder_transaction()方法过程,当非oneway的BC_TRANSACTION时,则设置当前事务t->from_parent等于当前线程的transaction_stack;
    • 执行binder_thread_read()方法过程,当非oneway的BR_TRANSACTION时,则设置当前事务t->to_parent等于当前线程的transaction_stack;

binder_work

struct binder_work {
    struct list_head entry;
    enum {
        BINDER_WORK_TRANSACTION = 1, 
        BINDER_WORK_TRANSACTION_COMPLETE,
        BINDER_WORK_NODE, 
        BINDER_WORK_DEAD_BINDER, 
        BINDER_WORK_DEAD_BINDER_AND_CLEAR, 
        BINDER_WORK_CLEAR_DEATH_NOTIFICATION,
    } type;
};

binder_work.type设置时机:

  • binder_transaction()
  • binder_thread_write()
  • binder_new_node()
binder_thread_read(){
    //当已使用字节数为0时,将BR_NOOP响应码放入指针ptr
    if (*consumed == 0) {
            if (put_user(BR_NOOP, (uint32_t __user *)ptr))
                return -EFAULT;
            ptr += sizeof(uint32_t);
        }

retry:
    //binder_transaction()已设置transaction_stack不为空,则wait_for_proc_work为false.
    wait_for_proc_work = thread->transaction_stack == NULL &&
            list_empty(&thread->todo);

    thread->looper |= BINDER_LOOPER_STATE_WAITING;
    if (wait_for_proc_work)
      proc->ready_threads++; //进程中空闲binder线程加1

    //只有当前线程todo队列为空,并且transaction_stack也为空,才会开始处于当前进程的事务
    if (wait_for_proc_work) {
        if (non_block) {
            ...
        } else
            //当进程todo队列没有数据,则进入休眠等待状态
            ret = wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread));
    } else {
        if (non_block) {
            ...
        } else
            //当线程todo队列有数据则执行往下执行;当线程todo队列没有数据,则进入休眠等待状态
            ret = wait_event_freezable(thread->wait, binder_has_thread_work(thread));
    }

    if (wait_for_proc_work)
      proc->ready_threads--; //退出等待状态, 则进程中空闲binder线程减1
    thread->looper &= ~BINDER_LOOPER_STATE_WAITING;
    ...

    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);
        // 线程todo队列没有数据, 则从进程todo对获取事务数据
        } else if (!list_empty(&proc->todo) && wait_for_proc_work) {
            w = list_first_entry(&proc->todo, struct binder_work, entry);
        } else {
            //没有数据,则返回retry
            if (ptr - buffer == 4 &&
                !(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN))
                goto retry;
            break;
        }

        switch (w->type) {
            case BINDER_WORK_TRANSACTION:
                //获取transaction数据
                t = container_of(w, struct binder_transaction, work);
                break;

            case BINDER_WORK_TRANSACTION_COMPLETE:
                cmd = BR_TRANSACTION_COMPLETE;
                //将BR_TRANSACTION_COMPLETE写入*ptr,并跳出循环。
                put_user(cmd, (uint32_t __user *)ptr);
                list_del(&w->entry);
                kfree(w);
                break;

            case BINDER_WORK_NODE: ...    break;
            case BINDER_WORK_DEAD_BINDER:
            case BINDER_WORK_DEAD_BINDER_AND_CLEAR:
            case BINDER_WORK_CLEAR_DEATH_NOTIFICATION: ...   break;
        }

        //只有BINDER_WORK_TRANSACTION命令才能继续往下执行
        if (!t)
            continue;

        if (t->buffer->target_node) {
            //获取目标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);
            ...
            cmd = BR_TRANSACTION;  //设置命令为BR_TRANSACTION
        } else {
            tr.target.ptr = NULL;
            tr.cookie = NULL;
            cmd = BR_REPLY; //设置命令为BR_REPLY
        }
        tr.code = t->code;
        tr.flags = t->flags;
        tr.sender_euid = t->sender_euid;

        if (t->from) {
            struct task_struct *sender = t->from->proc->tsk;
            //当非oneway的情况下,将调用者进程的pid保存到sender_pid
            tr.sender_pid = task_tgid_nr_ns(sender,
                            current->nsproxy->pid_ns);
        } else {
            //当oneway的的情况下,则该值为0
            tr.sender_pid = 0;
        }

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

        //将cmd和数据写回用户空间
        if (put_user(cmd, (uint32_t __user *)ptr))
            return -EFAULT;
        ptr += sizeof(uint32_t);
        if (copy_to_user(ptr, &tr, sizeof(tr)))
            return -EFAULT;
        ptr += sizeof(tr);

        list_del(&t->work.entry);
        t->buffer->allow_user_free = 1;
        if (cmd == BR_TRANSACTION && !(t->flags & TF_ONE_WAY)) {
            t->to_parent = thread->transaction_stack;
            t->to_thread = thread;
            thread->transaction_stack = t;
        } else {
            t->buffer->transaction = NULL;
            kfree(t); //通信完成,则运行释放
        }
        break;
    }
done:
    *consumed = ptr - buffer;
    //当满足请求线程加已准备线程数等于0,已启动线程数小于最大线程数(15),
    //且looper状态为已注册或已进入时创建新的线程。
    if (proc->requested_threads + proc->ready_threads == 0 &&
        proc->requested_threads_started < proc->max_threads &&
        (thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
         BINDER_LOOPER_STATE_ENTERED))) {
        proc->requested_threads++;
        // 生成BR_SPAWN_LOOPER命令,用于创建新的线程
        put_user(BR_SPAWN_LOOPER, (uint32_t __user *)buffer);
    }
    return 0;
}

该方法功能说明:

此处wait_for_proc_work是指当前线程todo队列为空,并且transaction_stack也为空,该值为true.

  1. 当wait_for_proc_work = false, 则进入线程的等待队列thread->wait, 直到thread->todo队列有事务才往下执行;
    • 获取并处理thread->todo队列中的事务;将相应的cmd和数据写回用户空间.
  2. 当wait_for_proc_work = true, 则进入进程的等待队列proc->wait, 直到proc->todo队列有事务才往下执行;
    • 获取并处理proc->todo队列中的事务;将相应的cmd和数据写回用户空间.

在Binder驱动层,只有在进入binder_thread_read()方法时,同时满足以下条件, 才会生成BR_SPAWN_LOOPER命令,当用户态进程收到该命令则会创建新线程:

  1. binder_proc的requested_threads线程数为0;
  2. binder_proc的ready_threads线程数为0;
  3. binder_proc的requested_threads_started个数小于15(即最大线程个数);
  4. binder_thread的looper状态为BINDER_LOOPER_STATE_REGISTERED或BINDER_LOOPER_STATE_ENTERED。

至此binder驱动的事情就做完了。

原文地址:

Binder系列1—Binder Driver初探

Binder系列2—Binder Driver再探

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值