芙蓉王红色包装多少钱:binder驱动-交互时的传输实现(二)

来源:百度文库 编辑:中财网 时间:2024/04/28 06:52:39

三、 binder通讯实现

3.1 场景概念

某个进程在调用了binder_open()之后将会在驱动中各有一个binder_proc结构体与之对应,而每一个线程(包括主进程)却不一定在驱动中有一个binder_thread结构体与之对应(除非有调用ioctl进行过读写),如果有binder_thread存在,那么这些binder_thread结构体均以域rb_node挂在对应进程的binder_proc.threads这颗红黑树上。

      

3.2 Transaction request

       主进程或者他的各个线程均可以使用binder设备文件描述符来调用ioctl函数来发送请求或者接受请求来处理。这一小节讨论发送请求的过程。

       在transaction过程中,上层应用程序用ioctl命令字BINDER_WRITE_READ带上struct binder_write_read作为参数传进内核空间继续执行,universus的文章中有详细阐述命令参数格式,如下图:

      

       对于发送者进程,上图中提到的数据结构均是在发送者进程用户空间分配的。

      

static long binder_ioctl(struct file *filp,unsigned int cmd,unsigned long arg)

{

       int ret;

       struct binder_proc *proc = filp->private_data;// 当前进程对应的binder_proc

       struct binder_thread *thread;       // 进程的每个线程在binder驱动中的表示

       unsigned int size = _IOC_SIZE(cmd);

       void __user *ubuf = (void __user *)arg;

       …

       mutex_lock(&binder_lock);

thread = binder_get_thread(proc); /* note3.2-1, 查找当前task对应的bind

er_thread结构体,如果没找到就新建一个binder_thread,同时将其加入binder_proc的threads的红黑树中。*/

       …

       switch (cmd) {

       case BINDER_WRITE_READ: {

              struct binder_write_read bwr;

              …

              if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {

                     …

}

              if (bwr.write_size > 0) { // > 0, 表示本次ioctl有待发送的数据

                     ret = binder_thread_write(proc, thread, (void __user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed);// note3.2-2,binder驱动发送传输函数

                     if (ret < 0) { // 成功返回0,出错小于0

                            bwr.read_consumed = 0;

                            if (copy_to_user(ubuf, &bwr, sizeof(bwr)))

                                   ret = -EFAULT;

                            goto err;

                     }

              }

              if (bwr.read_size > 0) { // > 0, 表示本次ioctl想接收数据

                     ret = binder_thread_read(proc, thread, (void __user *)bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK);

                     // note3.2-3,binder驱动接收读数据函数

                     if (!list_empty(&proc->todo))

wake_up_interruptible(&proc->wait);    /* 读返回的时候如果发现todo任务队列中有待处理的任务,那么将会唤醒binder_proc.wait中下一个等待着的空闲线程。*/

                     if (ret < 0) { // 成功返回0,出错小于0

                            if (copy_to_user(ubuf, &bwr, sizeof(bwr))) // 将错误状态返回

                                   ret = -EFAULT;

                            goto err;

                     }

              }

              …

              if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {  // 返回binder_write_read

                     …

              }

              break;

       }

       case BINDER_SET_MAX_THREADS:

       …

       case BINDER_SET_CONTEXT_MGR:

       …

}

       ret = 0;

err:

       …

       return ret;

}

/**********************  note3.2-1 ***********************/

thread = binder_get_thread(proc);

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;

      

       // 搜索红黑树binder_proc.threads

       while (*p) {  // *p是一个rb_node的指针

              parent = *p;

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

              // 通过结构体成员的指针地址,得到这个结构体的地址

              if (current->pid < thread->pid) // 这颗红黑树是以task的pid为索引值

                     p = &(*p)->rb_left;

              else if (current->pid > thread->pid)// 按照大小排列,有点类似二分法

                     p = &(*p)->rb_right;

              else//如果找到这个binder_thread结构体,立即退出查找,否则*p最终为NULL

                     break;

       }

       /* 值得一提的是,如果当前的task是主进程而非线程,那么在调用ioctl的时候也会使用该函数来创建或者查找对应的binder_thread。 */

       if (*p == NULL) { // 如果没找到,也就是当前task第一次调用ioctl的时候,会新

建binder_thread数据结构。

              thread = kzalloc(sizeof(*thread), GFP_KERNEL);// 分配内存空间

              if (thread == NULL)

                     return NULL;

              binder_stats_created(BINDER_STAT_THREAD);// 在binder_stats全局统计数据中,为新建的binder_thread对应的统计项加1。

              thread->proc = proc;    // 记录下binder驱动中对主进程描述的结构体

              thread->pid = current->pid; // 当前task的PID

              init_waitqueue_head(&thread->wait); // 初始化每一个task的私有等待队列

              INIT_LIST_HEAD(&thread->todo);    // 初始化每一个task的私有任务队列

              rb_link_node(&thread->rb_node, parent, p);

              rb_insert_color(&thread->rb_node, &proc->threads); // 将新建的binder_t

hread结构体加入当前进程的红黑树binder_proc.threads中

thread->looper |= BINDER_LOOPER_STATE_NEED_RETURN; // binder_thread.loo

per用来表示当前线程的状态

              thread->return_error = BR_OK;

              thread->return_error2 = BR_OK; // 初始化错误返回值

       }

       return thread;  // 返回查找到的或者新建的binder_thread结构体指针

}

/**********************  note3.2-1 ***********************/

 

/**********************  note3.2-2 ***********************/

binder_thread_write(proc, thread, (void __user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed);

调用该函数传递下去的参数有:当前task所在进程在binder驱动中对应的binder_proc结构体、和当前task在binder驱动中对应的binder_thread结构体;本次ioctl的参数binder_write_read结构体的write_buffer、write_size、write_consumed域。

 

int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread,

                     void __user *buffer, int size, signed long *consumed)

{

       uint32_t cmd;

       void __user *ptr = buffer + *consumed;

       void __user *end = buffer + size;

      

       /* ioctl传进来的只是结构体binder_write_read,而对于其中write和read的

       buffer却还是存在于用户空间,下面用get_user来获取用户空间buffer中的值 */

       while (ptr < end && thread->return_error == BR_OK) {// 一次ioctl的发送过程,可以在bwr.write_buffer中按照格式:命令+参数,组织多个命令包发送给接收方,所以这里使用了循环的方式来一个一个地处理这些命令包。

              if (get_user(cmd, (uint32_t __user *)ptr))

// 取得用户空间buffer中每个命令包的命令字

                     return -EFAULT;

              ptr += sizeof(uint32_t);       // ptr 指针前移,命令字是一个字的长度

              if (_IOC_NR(cmd) < ARRAY_SIZE(binder_stats.bc)) {

                     // 提取出cmd中的命令序号,对全局统计数据结构中bc对应命令使用域加1

                     binder_stats.bc[_IOC_NR(cmd)]++;//binder驱动全局统计数据

                     proc->stats.bc[_IOC_NR(cmd)]++;      //  binder_proc统计数据

                     thread->stats.bc[_IOC_NR(cmd)]++;//  binder_thread统计数据

              }

              switch (cmd) {

              case BC_INCREFS:      // BC_INCREFS = _IOW('c', 4, int),

              case BC_ACQUIRE:     // cmd | desc

              case BC_RELEASE:

              case BC_DECREFS:

              …

              case BC_TRANSACTION:

// BC_TRANSACTION = _IOW('c', 0, struct binder_transaction_data),

              case BC_REPLY: {      // cmd | binder_transaction_data

                     struct binder_transaction_data tr;

                     /* 理解binder驱动的关键之一在于认清下面两个结构体的区别和联系:

struct  binder_transaction_data 和struct binder_transaction ,前者是用于在用户空间中表示传输的数据,而后者是binder驱动在内核空间中来表示传输的数据,接下来所做的工作很大部分就是完成前者向后者转换,而对于binder读取函数binder_thread_read()则主要是完成从后者向前者转换。*/

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

// 先将传输数据结构从用户空间拷贝到内核空间

                            return -EFAULT;

                     ptr += sizeof(tr);   // 指针向前移动

                     binder_transaction(proc, thread, &tr, cmd == BC_REPLY);

              /* note3.2-2_1,完成传输数据的转换、如果有binder在传输数据包中,需

要为检查这些binder是否已经符合将来通讯的要求,最后就是往目标任务队列中添加任务,唤醒目标task */

                     break;

              }

              …

              }  // switch

}     // while

return 0;

}

/**********************  note3.2-2_1 ***********************/

binder_transaction(proc, thread, &tr, cmd == BC_REPLY);

调用该函数传递下去的参数有:当前task所在进程在binder驱动中对应的binder_proc结构体、和当前task在binder驱动中对应的binder_thread结构体;struct binder_transaction_data结构体指针,最后一个参数表示当前发送的是请求还是回复。

       这个函数将发送请求和发送回复放在了一个函数,而且包含了数据包中有binder在传输的处理情况,所以显得很复杂,不太容易看懂,大概有400多行吧!

       static void binder_transaction(struct binder_proc *proc,

                            struct binder_thread *thread,

                            struct binder_transaction_data *tr, int reply)

{

       struct binder_transaction *t; // 发送请求时使用的内核传输数据结构

       struct binder_work *tcomplete;   // binder的工作任务项

       size_t *offp, *off_end;               // 处理数据包中的binder结构体用

       struct binder_proc *target_proc;  // 目标进程对应的binder_proc

       struct binder_thread *target_thread = NULL; // 目标线程对应的binder_thread

       struct binder_node *target_node = NULL; // binder实体在内核中的节点结构体

       struct list_head *target_list;

       wait_queue_head_t *target_wait;        

       struct binder_transaction *in_reply_to = NULL;

       struct binder_transaction_log_entry *e;

       uint32_t return_error;

       …

       if (reply) {

       … // 如果ioctl写入的是回复数据,后面讨论

} else { // ioctl写入的是请求数据

       if (tr->target.handle) { // > 0,目标进程不是SMgr管理类进程

              struct binder_ref *ref;

              ref = binder_get_ref(proc, tr->target.handle);

              /*根据引用号得到当前进程的binder_proc.refs_by_desc红黑树中保

存的对应binder_ref指针,以引用号为索引。该函数容易看懂。*/

              if (ref == NULL) { // 错误处理, 无效的handle号

                     …

              }

              target_node = ref->node; /* 通过binder_ref.node域得到目标binde

r实体的binder_node节点指针 */

       } else { // == 0, 目标进程是SMgr管理类进程

              target_node = binder_context_mgr_node;/* 这个是已经存在的binde

r节点,系统启动时首先会让一个进程变成SMgr进程,才可能有后续的binder通讯。*/

// 如果ioctl想和SMgr的binder实体建立联系,需要使tr->target.handle = 0

if (target_node == NULL) { // 错误处理,如果管理进程的binder内

核节点还没建立好

                     return_error = BR_DEAD_REPLY;

                     goto err_no_context_mgr_node;

              }

       }

       target_proc = target_node->proc; /* 得到binder_node所对应的binder_proc

结构体,也就得到了binder实体所在进程的相关信息 */

       if (target_proc == NULL) { // 出错检查

                     return_error = BR_DEAD_REPLY;

                     goto err_dead_binder;

       }

       /* 下面深绿色的部分就是universus在他文中提到的关于传输中对目标task的一点小小的优化:当进程P1的线程T1向进程P2发送请求时,驱动会先查看一下线程T1是否也正在处理来自P2某个线程请求但尚未完成(没有发送回复)。这种情况通常发生在两个进程都有Binder实体并互相对发时请求时。假如驱动在进程P2中发现了这样的线程,比如说T2,就会要求T2来处理T1的这次请求。因为T2既然向T1发送了请求尚未得到返回包,说明T2肯定(或将会)阻塞在读取返回包的状态。 这时候可以让T2顺便做点事情,总比等在那里闲着好。而且如果T2不是线程池中的线程还可以为线程池分担部分工作,减少线程池使用率。

现在我们暂时跳过这个优化的部分,假设当前的task事先并没有处理来自别的进程的请求,也就是他是当前进程从空闲队列中唤醒的空闲线程。后面单独讨论他,因为想了解它需要知道binder_thread.transaction_stack、binder_transaction.from、binder_transaction.from_parent之间的关系和组织结构,现在还不能轻易看出来。

*/

       if (!(tr->flags & TF_ONE_WAY) && thread->transaction_stack) {

              struct binder_transaction *tmp;

              tmp = thread->transaction_stack;

              if (tmp->to_thread != thread) {  // line 2711

              // 这种优化的时候,判断发送者就是前面某次通讯的接收者。

              …

                     return_error = BR_FAILED_REPLY;

                     goto err_bad_call_stack;

              }

              while (tmp) {

                     if (tmp->from && tmp->from->proc == target_proc)

                            target_thread = tmp->from;

                     tmp = tmp->from_parent;

              }

       }

       if (target_thread) {// 如果这个优化有结果,有找到对应的线程作为目标线程,

              …                         // 目标任务队列和线程等待队列使用特定线程的。

              target_list = &target_thread->todo;

              target_wait = &target_thread->wait;

       } else {// 优化没有结果,则使用目标进程的任务队列和等待队列

              target_list = &target_proc->todo;

              target_wait = &target_proc->wait;             // 同上

       }

       /* 分配本次单边传输需要使用的binder_transaction结构体内存,这个结构体在

一次单边传输中存在一个,供发送和接收侧使用,一般在发送侧创建,然后接收侧通过work域反向得到binder_transaction的指针,最后在发送侧接收到接收侧的回复后将其内存空间释放。 */

       t = kzalloc(sizeof(*t), GFP_KERNEL);

       …

       binder_stats_created(BINDER_STAT_TRANSACTION); // 全局统计计数

       // 分配本次单边传输需要使用的binder_work结构体内存

       tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL);

       …

       binder_stats_created(BINDER_STAT_TRANSACTION_COMPLETE); // 全局统计计数

       t->debug_id = ++binder_last_id;

       …

       if (!reply && !(tr->flags & TF_ONE_WAY))

              t->from = thread;/* 如果是同步传输的发送边,这里将当前的binder_thre

ad记录在binder_transaction.from中,以供在同步传输的回复边时,驱动可以根据这个找到回复的目的task。*/

       else

              t->from = NULL; /* 如果是BC_REPLY或者是异步传输,这里不需要记录和返

回信息相关的信息。*/

       t->sender_euid = proc->tsk->cred->euid;    // 记录用户有效ID

       t->to_proc = target_proc;

       t->to_thread = target_thread;                   // 可以为NULL

       t->code = tr->code;             // 这个保持不变,驱动不会关心它

       t->flags = tr->flags;     // 同上

       t->priority = task_nice(current);//保存当前处于发送状态的task的nice值

       …

       t->buffer = binder_alloc_buf(target_proc, tr->data_size,

                            tr->offsets_size, !reply && (t->flags & TF_ONE_WAY));

       /*从目标进程的接收缓冲区中分配data大小为tr->data_size+tr->offsets_size的binder_buffer。关于这部分详细的讨论将会放在文档:binder驱动-接收缓存区管理*/

       if (t->buffer == NULL) {   // 分配buffer出错

              return_error = BR_FAILED_REPLY;

              goto err_binder_alloc_buf_failed;

       }

       t->buffer->allow_user_free = 0;

// 先设置成禁止用户空间释放,后面将会在接收完成之后设置成1

       t->buffer->debug_id = t->debug_id;

       t->buffer->transaction = t;   // 指向所属的binder_ transaction结构体

       t->buffer->target_node = target_node;       // 可以等于NULL

       if (target_node)

              binder_inc_node(target_node, 1, 0, NULL); // 增加binder_node的计数

      

       offp = (size_t *)(t->buffer->data + ALIGN(tr->data_size, sizeof(void *))); // 计算出存放flat_binder_object结构体偏移数组的起始地址,4字节对齐。

       /* struct flat_binder_object是binder在进程之间传输的表示方式 */

       /* 这里就是完成binder通讯单边时候在用户进程同内核buffer之间的一次拷贝动作 */

       if (copy_from_user(t->buffer->data, tr->data.ptr.buffer, tr->data_size)

{ // 出错处理

       }

       // 拷贝内嵌在传输数据中的flat_binder_object结构体偏移数组

       if (copy_from_user(offp, tr->data.ptr.offsets, tr->offsets_size)) {

              // 出错处理

       }

       …

       off_end = (void *)offp + tr->offsets_size;       //flat_binder_object结构体偏移数组的结束地址

       for (; offp < off_end; offp++) {

              struct flat_binder_object *fp;

              /* *offp是t->buffer中第一个flat_binder_object结构体的位置偏移,相

当于t->buffer->data的偏移,这里的if语句用来判断binder偏移数组的第一个元素所指向的flat_binder_object结构体地址是否是在t->buffer->data的有效范围内,或者数据的总大小就小于一个flat_binder_object的大小,或者说这个数组的元素根本就没有4字节对齐(一个指针在32位平台上用4个字节表示)。*/

              if (*offp > t->buffer->data_size - sizeof(*fp) ||

                  t->buffer->data_size < sizeof(*fp) ||

                  !IS_ALIGNED(*offp, sizeof(void *))) {

                     binder_user_error("binder: %d:%d got transaction with "

                            "invalid offset, %zd\n",

                            proc->pid, thread->pid, *offp);

                     return_error = BR_FAILED_REPLY;

                     goto err_bad_offset;

              }

              fp = (struct flat_binder_object *)(t->buffer->data + *offp);

              // 取得第一个flat_binder_object结构体指针

              switch (fp->type) {

                     case BINDER_TYPE_BINDER:

                     case BINDER_TYPE_WEAK_BINDER: {

                            // 只有具有binder实体的进程才有权利发送这类binder。

                            struct binder_ref *ref;

                            struct binder_node *node=binder_get_node(proc, fp->binder);

                            /*根据flat_binder_object.binder这个binder实体在进程间的地

址搜索当前进程的binder_proc->nodes红黑树,看看是否已经创建了binder_node内核节点。Note3.2-2_1_1 */

                                   …

                            node->min_priority = fp->flags & FLAT_BINDER_FLAG_PRIORITY_

MASK;

                            node->accept_fds = !!(fp->flags & FLAT_BINDER_FLAG_ACCEPTS_

FDS);

                            // 设置处理Binder请求的线程的最低优先级

                            // 表明节点是否同意接受文件方式的Binder

                            if (fp->cookie != node->cookie) {    // 额外数据校验

                                   …

goto err_binder_get_ref_for_node_failed;

                            }

/* 在目标进程中通过binder_node搜索或者新建一个对应的binder_ref结构体 */

                            ref= binder_get_ref_for_node(target_proc, node);

//note3.2-2_1_2

                            …

                            if (fp->type == BINDER_TYPE_BINDER)

                                   fp->type = BINDER_TYPE_HANDLE;

                            else

                                   fp->type = BINDER_TYPE_WEAK_HANDLE;

                                   // 修改flat_binder_object数据结构的type和handle域,接

下来要传给接收方

                            fp->handle = ref->desc;

                            binder_inc_ref(ref,fp->type==BINDER_TYPE_HANDLE,&thread->todo);// 增加binder_ref的引用计数

                            …

                     } break;

                     case BINDER_TYPE_HANDLE:

                     case BINDER_TYPE_WEAK_HANDLE: {

                            struct binder_ref *ref = binder_get_ref(proc, fp->handle);

                            // 通过引用号取得当前进程中对应的binder_ref结构体

                            if (ref == NULL) {     // 无效的handle

                                   …

                                   return_error = BR_FAILED_REPLY;

                                   goto err_binder_get_ref_failed;

                            }

                            if (ref->node->proc == target_proc) {

                            /* 如果目标进程正好是提供该引用号对应的binder实体的进程,那

么按照下面的方式修改flat_binder_object的相应域: type 和 binder,cookie。*/

                            if (fp->type == BINDER_TYPE_HANDLE)

                                   fp->type = BINDER_TYPE_BINDER;

                            else

                                   fp->type = BINDER_TYPE_WEAK_BINDER;

                            fp->binder = ref->node->ptr;

                            fp->cookie = ref->node->cookie;

                            binder_inc_node(ref->node, fp->type == BINDER_TYPE_BINDER, 0, NULL); // 增加binder_node的引用计数

                            …

                     } else {

                            /* 否则会在目标进程的refs_by_node红黑树中先搜索看是否之前

有创建过对应的binder_ref,如果没有找到,那么就需要为ref->node节点在目标进程中新建一个目标进程的binder_ref挂入红黑树refs_by_node中。*/

                            struct binder_ref *new_ref;

                            new_ref = binder_get_ref_for_node(target_proc, ref->node);

                            if (new_ref == NULL) {// 出错处理

                                   …

                            }

                            fp->handle = new_ref->desc;

// 此时只需要将此域修改为新建binder_ref的引用号

                            binder_inc_ref(new_ref, fp->type == BINDER_TYPE_HANDLE, NULL);// 增加binder_ref的引用计数

                            …

                     } break;

                     case BINDER_TYPE_FD: {

                            int target_fd;

                            struct file *file;

                           

                            if (reply) { // 发送的是回复数据

                                   …

                            } else if (!target_node->accept_fds) {

                                   // 目标进程不接受文件形式的binder

                                   …

                                   return_error = BR_FAILED_REPLY;

                                   goto err_fd_not_allowed;

                            }

                           

                            file = fget(fp->handle);

// 取得当前进程的当前文件描述符对应的struct file指针

                            if (file == NULL) {// 无效的文件描述符

                                   …

                            }

                            target_fd=task_get_unused_fd_flags(target_proc, O_CLOEXEC);

                            // 在目标进程中得到一个未使用的文件描述符返回。

                            if (target_fd < 0) { // 获取不成功

                                   …

                            }

                            task_fd_install(target_proc, target_fd, file);

                            /* 通过该函数将获取的file和文件描述符加入目标进程的files_s

truct结构体中 */

                            …

                            fp->handle=target_fd;//将handle域修改成目标进程中对应的fd

                            /*使用文件Binder打开的文件共享linux VFS中的struct file,s

truct dentry,struct inode结构,这意味着一个进程使用read()/write()/seek()改变了文件指针另一个进程的文件指针也会改变。*/

                     } break;

                     default:

                            binder_user_error("binder: %d:%d got transactio"

                            "n with invalid object type, %lx\n",

                            proc->pid, thread->pid, fp->type);

                            return_error = BR_FAILED_REPLY;

                            goto err_bad_object_type;

} // switch (fp->type)

       } // for (; offp < off_end; offp++)

       if (reply) { // 暂时略过

              …

       } else if (!(t->flags & TF_ONE_WAY)) {// 如果是同步传输

              BUG_ON(t->buffer->async_transaction != 0); // 0 表示同步 1表示异步

              //binder_alloc_buf()函数中会设置async_transaction表示是否异步传输

              t->need_reply = 1;              // 需要回复

              t->from_parent = thread->transaction_stack;

              /* 如果本次发起传输之前,当前task没有处于通讯过程中的话,这里必然为

NULL。而且第一次发起传输时,这里也是为NULl。如果之前有异步传输没处理完,那么这里不为null,如果之前本task正在处理接收请求,这里也不为NULL。*/

thread->transaction_stack = t;/* 这里将传输中间数据结构保存在binder_transaction链表顶部。这个transaction_stack实际上是管理这一个链表,只不过这个指针时时指向最新加入该链表的成员,最先加入的成员在最底部,有点类似于tack,所以这里取名叫transaction_stack。*/

       } else {

              BUG_ON(target_node == NULL);

              BUG_ON(t->buffer->async_transaction != 1);// 异常检查

              // 下面是对异步通讯的分流处理

              if (target_node->has_async_transaction) {

// 表明该节点在to-do队列中有异步交互尚未完成。

                     target_list = &target_node->async_todo;

// 将后来的异步交互转入异步等待队列。

                     target_wait = NULL;

              } else

                     target_node->has_async_transaction = 1;

                     // 否则,将该标志置1,表明当前有一个异步交互正在处理。

       }

       t->work.type = BINDER_WORK_TRANSACTION;

       list_add_tail(&t->work.entry, target_list);

// 将binder_transaction.binder_work加入目标to_od队列中去等待分配处理。

       tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;

       list_add_tail(&tcomplete->entry, &thread->todo);

/* 发送端binder_work加入当前线程的todo队列中,binder驱动通知发送端,

数据已经成功发送出去了。*/

             

              if (target_wait) {

                     …

                     wake_up_interruptible(target_wait);     // 唤醒目标task的等待队列

              }

              return;

              … // 出错处理跳转标签

}// binder_transaction()

/**********************  note3.2-2_1_1 ***********************/

static struct binder_node *binder_new_node(struct binder_proc *proc,

                                      void __user *ptr,

                                      void __user *cookie)

{

       struct rb_node **p = &proc->nodes.rb_node;

       struct rb_node *parent = NULL;

       struct binder_node *node;

       // 先搜索红黑树binder_proc.nodes,如果上面已经挂上了改binder_node,那么直接返回NULL

       while (*p) {

              parent = *p;

              node = rb_entry(parent, struct binder_node, rb_node);

              // 以binder实体在用户进程空间中的地址为索引

              if (ptr < node->ptr)

                     p = &(*p)->rb_left;

              else if (ptr > node->ptr)

                     p = &(*p)->rb_right;

              else

                     return NULL;

       }

       // 新建一个binder_node数据结构

       node = kzalloc(sizeof(*node), GFP_KERNEL);

       …

       binder_stats_created(BINDER_STAT_NODE);   // 全局统计node个数计数

       rb_link_node(&node->rb_node, parent, p);

       rb_insert_color(&node->rb_node, &proc->nodes); // 插入binder_proc.nodes红黑树

       node->debug_id = ++binder_last_id;

       node->proc = proc;

       node->ptr = ptr;

       node->cookie = cookie;

       node->work.type = BINDER_WORK_NODE;

       INIT_LIST_HEAD(&node->work.entry);

       INIT_LIST_HEAD(&node->async_todo);

       …

       return node;

}

/**********************  note3.2-2_1_1 ***********************/

/**********************  note3.2-2_1_2 ***********************/

/* 根据node的地址在proc所代表进程的refs_by_node这颗红黑树上查找对应的binder_ref是否存在。如果存在,表明驱动已经为当前进程建立了与node相对应的binder_ref;

如果不存在,就新建立一个binder_ref数据结构。*/

static struct binder_ref *binder_get_ref_for_node(struct binder_proc *proc,

                                            struct binder_node *node)

{

       struct rb_node *n;

       struct rb_node **p = &proc->refs_by_node.rb_node;

       struct rb_node *parent = NULL;

       struct binder_ref *ref, *new_ref;

 

       while(*p){//以binder_node内存地址为索引查找红黑树binder_proc.refs_by_node。

              parent = *p;

              ref = rb_entry(parent, struct binder_ref, rb_node_node);

              // 以binder_node的内存地址为索引

              if (node < ref->node)

                     p = &(*p)->rb_left;

              else if (node > ref->node)

                     p = &(*p)->rb_right;

              else

                     return ref; // 找到的话直接返回binder_ref的地址

       }

       // 新建binder_ref数据结构

       new_ref = kzalloc(sizeof(*ref), GFP_KERNEL);

       …

       binder_stats_created(BINDER_STAT_REF);       // 全局统计数据结构相应域加1

       new_ref->debug_id = ++binder_last_id;

       new_ref->proc = proc;        // 和当前的binder_proc对应

       new_ref->node = node;              // 和binder_node对应

       rb_link_node(&new_ref->rb_node_node, parent, p);

       rb_insert_color(&new_ref->rb_node_node, &proc->refs_by_node); // 插入红黑树

      

       /* 如果是binder_node处于SMgr进程中,那么对于每个其他进程来说,引用号都是0。否则为1,然后再重新分配。*/

       new_ref->desc = (node == binder_context_mgr_node) ? 0 : 1;

       // 为新binder_ref重新分配引用号

       for (n = rb_first(&proc->refs_by_desc); n != NULL; n = rb_next(n)) {

              ref = rb_entry(n, struct binder_ref, rb_node_desc);

              if (ref->desc > new_ref->desc)

                     break;

              new_ref->desc = ref->desc + 1;

       }// 对于新创建索引的索引值desc,都是refs_by_desc红黑树中最大的索引值加1。

       // 下面以新的引用号来搜索红黑树refs_by_desc。

       p = &proc->refs_by_desc.rb_node;

       while (*p) {

              parent = *p;

              ref = rb_entry(parent, struct binder_ref, rb_node_desc);

 

              if (new_ref->desc < ref->desc)

                     p = &(*p)->rb_left;

              else if (new_ref->desc > ref->desc)

                     p = &(*p)->rb_right;

              else

                     BUG();/* 这里之所以用BUG(),是因为如果在refs_by_node没找到,这里在refs_by_desc也应该没有才合理。*/

       }

       rb_link_node(&new_ref->rb_node_desc, parent, p);

       rb_insert_color(&new_ref->rb_node_desc, &proc->refs_by_desc);

       // 将该binder_ref挂入红黑树refs_by_desc中。

       if (node) {/*如果node存在,这里会将新建好的binder_ref添加进binder_node的refs链表中统一管理。*/

              hlist_add_head(&new_ref->node_entry, &node->refs);

              …

       } else {

              binder_debug(BINDER_DEBUG_INTERNAL_REFS,

                          "binder: %d new ref %d desc %d for "

                          "dead node\n", proc->pid, new_ref->debug_id,

                           new_ref->desc);

       }

       return new_ref;            // 返回新建的binder_ref结构体指针

}

/**********************  note3.2-2_1_2 ***********************/

/**********************  note3.2-2_1 ***********************/

/**********************  note3.2-2 ***********************/

到此,发送请求的过程已经讨论完了,在驱动将数据中转完之后,都会给当前的task发送回复说数据已经成功发送给对端了。那么就下来我们就来看看接收端调用的binder读函数的流程。