readbaramangaonline:binder驱动-交互时的传输实现(二)
来源:百度文库 编辑:偶看新闻 时间:2024/04/29 04:07:29
三、 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读函数的流程。