请叫我青春蜜糖真名:binder驱动-交互时的传输实现(三)

来源:百度文库 编辑:中财网 时间:2024/04/27 21:49:09

3.3 transaction async request

       对于异步传输,在上层空间传下来的数据结构binder_transcation_data中的flags域中可以体现出来,也就是flags的TF_ONE_WAY位为1,就表示需要异步传输,不需要等待回复数据。

       其实异步和同步传输在发送请求的过程绝大多数是一样的,这里就不再重复,本小节描述一下关于异步传输独有的地方即可。

       关键在函数调用binder_transaction(proc, thread, &tr, cmd == BC_REPLY)中体现了不同:

static void binder_transaction(struct binder_proc *proc,

                            struct binder_thread *thread,

                            struct binder_transaction_data *tr, int reply)

{

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

              …

}else { // 发送的是请求数据

              …

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

                     … // 同步传输的优化

              }

       }

       if (target_thread) {

              e->to_thread = target_thread->pid;

              target_list = &target_thread->todo;

              target_wait = &target_thread->wait;

       } else {

              target_list = &target_proc->todo;

              target_wait = &target_proc->wait;

       }

       …

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

              … // 发送同步非回复数据

       else

              t->from = NULL; /* 如果是BC_REPLY或者是异步传输,这里不需要记录和返回信息相关的东西。*/

       …

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

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

       /* 最后一个参数只有在非回复并且是异步的情况下才会为1,这个值会记录在所分配

的binder_buffer.async_transaction中来表示当前的buffer用在异步传输中还是同步传输中。*/

       …  // flat_binder_object结构体处理

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

              …

       }else if (!(t->flags & TF_ONE_WAY)) { // 同步发送请求

              …

       }else { // 异步发送请求

              BUG_ON(target_node == NULL);// 异步传输时目标一定要明确

              BUG_ON(t->buffer->async_transaction != 1); // 前面提到的重要标志

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

       if (target_node->has_async_transaction) {/* 如果目标task仍然还有一

个异步需要处理的话,该标志为1。*/

                     target_list = &target_node->async_todo;/* 将当前的这个的异步传输任

务转入目标进程binder_node的异步等待队列async_todo中。*/

                     target_wait = NULL;

              } else

                     target_node->has_async_transaction = 1;

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

       } // else

       t->work.type = BINDER_WORK_TRANSACTION;

       list_add_tail(&t->work.entry, target_list); /* 将binder_work加入目标任务队

列中: async_todo。 */

       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);  // target_wait == NULL

       }

       …

}

       对于异步发送请求来说,到这里结束之后,驱动中已经准备好了相应的binder_transaction结构体,只不过极有可能将这个结构体通过其中的binder_work域挂到了目标进程的binder_node.async_todo队列中,直到target_node->has_async_transaction标志为0。接着如果运气好的话,才有可能将binder_transaction移动到目标进程的全局任务队列binder_node.todo中,这时binder驱动就会给他找一个空闲线程来处理这个请求数据包。

 

3.4 receive request

       接收端的task也是利用ioctl()进行读操作的,前面列出的struct binder_write_read结构体中如果有包含读取数据的buffer和预备读取大小,那么在write完该传输的数据之后就会进行read的操作,如下:(整理下思路,以下所讨论的内容均属于另一个进程的某个task,和前面传输部分属于不同的task,所以调用ioctl的时候,所描述的当前task也是接收端的,而非发送端。)

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

int ret;

struct binder_proc *proc = filp->private_data;// 调用ioctl方的binder_proc结构体

struct binder_thread *thread;

unsigned int size = _IOC_SIZE(cmd);

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

       …

       mutex_lock(&binder_lock);

       thread = binder_get_thread(proc);

       …

       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有待发送的数据

                     …

              }

              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.4-1,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.4-1 ***********************/

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

       该函数调用传递的参数有:当前task所在的进程对应的binder_proc和binder_thread结构体指针,用户空间的read_buffer地址,想读取数据的大小,已经打开binder节点的时候是否是非阻塞打开,默认情况下是阻塞打开文件的。

       binder_thread_read()函数也是比较大的,大概300多行,包含了各种不同情况的处理。

 

static int binder_thread_read(struct binder_proc *proc,

                           struct binder_thread *thread,

                           void  __user *buffer, int size,

                           signed long *consumed, int non_block)

{

       void __user *ptr = buffer + *consumed;

       void __user *end = buffer + size;

       int ret = 0;

       int wait_for_proc_work;

      

       // 如果实际读取到的大小等于0,那么将会在返回的数据包中插入BR_NOOP的命令字。

       if (*consumed == 0) {

              if (put_user(BR_NOOP, (uint32_t __user *)ptr)) // put_user()

                     return -EFAULT;

              ptr += sizeof(uint32_t);// 指针向前移动4个字节

       }

retry:

       wait_for_proc_work = thread->transaction_stack == NULL &&

list_empty(&thread->todo);

/* 该标志表示当前task是要去等待处理proc中全局的todo还是自己本task的todo队列中的任务。两个条件决定这个标志是否为1,当前task的binder_transaction这个链表为NULL, 它记录着本task上是否有传输正在进行;第二个条件是当前task的私有任务队列为NULL。*/

       if (thread->return_error != BR_OK && ptr < end) {// 错误处理

              if (thread->return_error2 != BR_OK) {

                     if (put_user(thread->return_error2, (uint32_t __user *)ptr))

                            return -EFAULT;

                     ptr += sizeof(uint32_t);

                     if (ptr == end)

                            goto done;

                     thread->return_error2 = BR_OK;

              }

              if (put_user(thread->return_error, (uint32_t __user *)ptr))

                     return -EFAULT;

              ptr += sizeof(uint32_t);

              thread->return_error = BR_OK;

              goto done;

       }

       thread->looper |= BINDER_LOOPER_STATE_WAITING;// 表明线程即将进入等待状态。

       if (wait_for_proc_work)

              proc->ready_threads++; // 就绪等待任务的空闲线程数加1。

       mutex_unlock(&binder_lock);

       if (wait_for_proc_work) {

              if (!(thread->looper & (BINDER_LOOPER_STATE_REGISTERED |

                                   BINDER_LOOPER_STATE_ENTERED))) {

                     /* Thread waiting for process work after calling BC_REGISTER_LOOPE

R or BC_ENTER_LOOPER */

                     …

                     wait_event_interruptible(binder_user_error_wait,

                                           binder_stop_on_user_error < 2);

              }

              binder_set_nice(proc->default_priority);

// 当前task设置成所属进程的默认优先级。

              …

              if (non_block) {   // 非阻塞打开的binder设备节点

                     if (!binder_has_proc_work(proc, thread))

                            ret = -EAGAIN; // 返回try again的提示。

              } else

                     ret = wait_event_interruptible_exclusive(proc->wait, binder_has_proc_work(proc, thread));// 当前task互斥等待在进程全局的等待队列中。note3.4-1_1

       } else {   // wait_for_proc_work == 0

              if (non_block) {   // 非阻塞打开的binder设备节点

                     if (!binder_has_thread_work(thread))

                            ret = -EAGAIN; // 返回try again的提示。

              } else

                     ret = wait_event_interruptible(thread->wait, binder_has_thread_work(thread));// note3.4-1_1

                     /* 当前task等待在task自己的等待队列中(binder_thread.todo),永远只

有其自己。*/

       }

       /* binder_thread在创建的时候都自带有BINDER_LOOPER_STATE_NEED_RETUR

N,所以第一次经过这里进程不会睡眠下去, 直到后面mask掉该标志位。*/

       mutex_lock(&binder_lock);

       if (wait_for_proc_work)

              proc->ready_threads--;          // 就绪空闲线程少了一个

       thread->looper &= ~BINDER_LOOPER_STATE_WAITING;        // mask掉这个标识

       if (ret)

              return ret;

              // 如果是以非阻塞的方式打开的binder设备节点,那么这里将会返回-EAGAIN。

      

       while (1) {

              uint32_t cmd;

              struct binder_transaction_data tr; // 为用户空间准备的数据结构

              struct binder_work *w;

              struct binder_transaction *t = NULL;

             

              if (!list_empty(&thread->todo))   // 当前task私有todo任务队列里有任务

                     w = list_first_entry(&thread->todo, struct binder_work, entry);

                     // 取出todo队列中第一个binder_work结构体。

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

                     // proc->todo是当前task所属进程的公共todo任务队列

                     w = list_first_entry(&proc->todo, struct binder_work, entry);

              else {

                     if (ptr - buffer == 4 && !(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN)) /* no data added */

/* buffer中只有一个BR_NOOP,  同时当前task的BINDER_LOOPER_STATE_NEED_RETURN标志已被清除,那么就跳转回去重新睡眠等待。*/

                            goto retry;

                     break; // 否则就跳出这个while循环,读函数返回。

              }

             

              if (end - ptr < sizeof(tr) + 4) // 检查剩余的read_buffer的大小是否合理

                     break;

             

switch (w->type) {

              case BINDER_WORK_TRANSACTION: {

                     t = container_of(w, struct binder_transaction, work);

                     // 通过binder_work:w反向找到所属的binder_transaction数据结构指针

              } break;

              case BINDER_WORK_TRANSACTION_COMPLETE: {

              …

              }

             

              if (!t) // 非 BINDER_WORK_TRANSACTION 的情况,放弃执行后面的重新循环

                     continue;

             

              BUG_ON(t->buffer == NULL);/* 这里的t所指向的binder_transaction结构体就

是前面发送者task建立的binder_transaction数据结构,所以这里如果为NULL,说明有异常。*/

              if (t->buffer->target_node) {

              /* 可以为NULL,如: BC_REPLY的时候。这个值是发送者task填入,对发送者来

说才有意义。*/

// 下面开始将binder_transaction转换成binder_transaction_data结构了。

                     struct binder_node *target_node = t->buffer->target_node;

                     tr.target.ptr = target_node->ptr; // binder实体的用户空间指针

                     tr.cookie =  target_node->cookie; // binder实体的额外数据

                     t->saved_priority = task_nice(current); // 保留当前task的nice值

                     …

/* t->priority保存的是发送者task的nice值, target_node->min_prior

ity保存的是binder所在进程中处理请求线程的最低优先级,如果前者优先

级高于后者,那么将当前task的优先级设置成发送者task的nice值。*/

                     if (t->priority < target_node->min_priority &&

                         !(t->flags & TF_ONE_WAY))

                            binder_set_nice(t->priority);// 同步传输

                     else if (!(t->flags & TF_ONE_WAY) ||

                             t->saved_priority > target_node->min_priority)

                            binder_set_nice(target_node->min_priority);

                            /* 如果当前接收task的nice过低(saved_priority),那么使用binder_

node创建时候设置的任务处理线程的最低优先级(min_priority)。*/

                     cmd = BR_TRANSACTION;

                     //接收task上层会收到BR_TRANSACTION|binder_transaction_data的数据包

              } else {         // 如果发送者是发送的回复数据:BC_REPLY

                     tr.target.ptr = NULL;

                     tr.cookie = NULL;

                     cmd = BR_REPLY;            // 收到BR_REPLY

              }

              tr.code = t->code; // 函数调用编号

              tr.flags = t->flags; // transaction_flags: TF_ONE_WAY等

              tr.sender_euid = t->sender_euid; // 发送进程的有效用户ID

             

              if (t->from) {       // 记录发送线程的binder_thread

                     struct task_struct *sender = t->from->proc->tsk;

                     tr.sender_pid = task_tgid_nr_ns(sender,

                                                 current->nsproxy->pid_ns);

              } else {

                     tr.sender_pid = 0;

              }

             

              ///+++++++++++++++++++++++++++++++++++++++++++++

              /* 接收方在这里完成数据从内核空间到用户空间的转移,其实没有实际的数据移

动,而是buffer地址在内核空间和用户空间中的转换: user_buffer_offset。*/

              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;/* 需要加上这个偏移量才是用户空间

的地址,这个偏移量是在mmap函数中计算出来的。*/

              tr.data.ptr.offsets = tr.data.ptr.buffer +

                                   ALIGN(t->buffer->data_size, sizeof(void *));

              ///+++++++++++++++++++++++++++++++++++++++++++++

             

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

              /* 拷贝cmd和binder_transaction_data数据回用户空间,上层只需要准备这二者

的内存空间即可。其余的数据均在binder_buffer之中呆着。*/

             

              binder_stat_br(proc, thread, cmd); // 统计计数

              …

              list_del(&t->work.entry); // 从todo队列中删除对应的binder_work。

              t->buffer->allow_user_free = 1;/* 允许释放binder_buffer空间,驱动处理当

前task的BC_FREE_BUFFER的时候会检查这个。*/

              if (cmd == BR_TRANSACTION && !(t->flags & TF_ONE_WAY)) {//同步,请求数据

                     t->to_parent = thread->transaction_stack;

                     t->to_thread = thread;

                     thread->transaction_stack = t;

                     /* 这个颜色表示了同一个binder_transaction在发送task和接收task中都

有修改的部分。 发送task和接收task的binder_thread.transaction_stac

k指向的是同一个binder_transcation结构体。*/

              } else {// 同步,回复数据;异步

                     …

              }

              break;

       }     // while(1)

 

done:

       *consumed = ptr - buffer;  // 实际接收到的字节数

       /* 检查当前进程中的空闲线程是否够用:如果正在请求的线程数和空闲的线程数都为0, 请求的线程已经开始运行的数目小于运行开辟的最大线程数。*/

       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)) /* the user-space code fails to */

            /*spawn a new thread if we leave this out */) {

              proc->requested_threads++;

              binder_debug(BINDER_DEBUG_THREADS,"binder: %d:%d BR_SPAWN_LOOPER\n",

                                             proc->pid, thread->pid);

              if (put_user(BR_SPAWN_LOOPER, (uint32_t __user *)buffer))

/*当驱动发现接收方所有线程都处于忙碌状态且线程池里的线程总数没有超过BINDER_SET_MAX_THREADS 设置的最大线程数时,向接收方发送该命令要求创建更多线程以备接收数据。*/

                     return -EFAULT;

       }

       return 0;

}// binder_thread_read()

 

/**********************  note3.4-1_1 ***********************/

static int binder_has_proc_work(struct binder_proc *proc,

                            struct binder_thread *thread)

{

       return !list_empty(&proc->todo) ||

              (thread->looper & BINDER_LOOPER_STATE_NEED_RETURN);

       /* binder_thread中looper的这个标识: BINDER_LOOPER_STATE_NEED_RETURN, 表示

等待时强制让线程return。binder_thread在创建的时候都自带有BINDER_LOOPER_STATE_NEED_RETURN,所以默认情况下是允许强制return的。*/

}

 

static int binder_has_thread_work(struct binder_thread *thread)

{

       return !list_empty(&thread->todo) || thread->return_error != BR_OK ||

              (thread->looper & BINDER_LOOPER_STATE_NEED_RETURN);

/* binder_thread中looper的这个标识: BINDER_LOOPER_STATE_NEED_RETURN, 表示等待时强制让线程return。binder_thread在创建的时候都自带有BINDER_LOOPER_STATE_NEED_RETURN,所以默认情况下是允许强制return的。*/

}

/**********************  note3.4-1_1 ***********************/

/**********************  note3.4-1 ***********************/

到这里,3.4节进程的task将3.2节中的进程的某task发送过来的数据包接收完了,已经通过binder_transaction_data返回给3.3节task的用户空间中去了。最后在ioctl中的read之后,也会将binder­_write_read这个结构体返回给应用空间。