数据交互的相关步骤:第一步、调用进程通过系统调用进入内核态数据交互这个步骤是拷贝了进程间所需要数据的指针;第二步、在内核态将进程间所需要数据拷贝到对应的进程申请的内存中,这个步骤确实发生了进程间所需要数据拷贝;第三步、对应的进程再将数据从内核态拷贝到用户态,这个步骤拷贝的同样是数据指针。这样总共经过3次数据交互,将数据从调用进程传递到目标进程。其实也是经过了3次数据拷贝,只不过第一次和第三次拷贝的都是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;
struct binder_context *context = proc->context;
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.cmd == BR_OK) {
int ret;
if (get_user(cmd, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
…………
switch (cmd) {
…………
case BC_TRANSACTION:
case BC_REPLY: {
struct binder_transaction_data tr;
if (copy_from_user(&tr, ptr, sizeof(tr)))
return -EFAULT;
ptr += sizeof(tr);
binder_transaction(proc, thread, &tr,
cmd == BC_REPLY, 0);
break;
}
…………
}
*consumed = ptr - buffer;
}
return 0;
}
binder_thread_write()函数实现在Binder驱动中,也就是这段代码运行的时候是在内核态。其中变量ptr是用户态内存数据的指针,通过copy_from_user(&tr, ptr, sizeof(tr))将用户态数据拷贝到内核态。这是第一次数据拷贝。其中tr.data.ptr.buffer是进程间交互数据的用户态地址,所以这个步骤只是将用户态数据地址拷贝了过来。
接着就是调用binder_transaction()函数进行处理,在内核态将数据拷贝到对应的目标进程中,就是通过它来处理的,
static void binder_transaction(struct binder_proc *proc,
struct binder_thread *thread,
struct binder_transaction_data *tr, int reply,
binder_size_t extra_buffers_size)
{
int ret;
struct binder_transaction *t;
…………
t = kzalloc(sizeof(*t), GFP_KERNEL);
…………
t->buffer = binder_alloc_new_buf(&target_proc->alloc, tr->data_size,
tr->offsets_size, extra_buffers_size,
!reply && (t->flags & TF_ONE_WAY), current->tgid);
…………
if (binder_alloc_copy_user_to_buffer(
&target_proc->alloc,
t->buffer,
ALIGN(tr->data_size, sizeof(void *)),
(const void __user *)
(uintptr_t)tr->data.ptr.offsets,
tr->offsets_size)) {
binder_user_error("%d:%d got transaction with invalid offsets ptr\n",
proc->pid, thread->pid);
return_error = BR_FAILED_REPLY;
return_error_param = -EFAULT;
return_error_line = __LINE__;
goto err_copy_data_failed;
}
…………
for (buffer_offset = off_start_offset; buffer_offset < off_end_offset;
buffer_offset += sizeof(binder_size_t)) {
struct binder_object_header *hdr;
size_t object_size;
struct binder_object object;
binder_size_t object_offset;
binder_size_t copy_size;
if (binder_alloc_copy_from_buffer(&target_proc->alloc,
&object_offset,
t->buffer,
buffer_offset,
sizeof(object_offset))) {
return_error = BR_FAILED_REPLY;
return_error_param = -EINVAL;
return_error_line = __LINE__;
goto err_bad_offset;
}
/*
* Copy the source user buffer up to the next object
* that will be processed.
*/
copy_size = object_offset - user_offset;
if (copy_size && (user_offset > object_offset ||
binder_alloc_copy_user_to_buffer(
&target_proc->alloc,
t->buffer, user_offset,
user_buffer + user_offset,
copy_size))) {
binder_user_error("%d:%d got transaction with invalid data ptr\n",
proc->pid, thread->pid);
return_error = BR_FAILED_REPLY;
return_error_param = -EFAULT;
return_error_line = __LINE__;
goto err_copy_data_failed;
}
object_size = binder_get_object(target_proc, user_buffer,
t->buffer, object_offset, &object);
…………
hdr = &object.hdr;
off_min = object_offset + object_size;
switch (hdr->type) {
case BINDER_TYPE_BINDER:
case BINDER_TYPE_WEAK_BINDER: {
struct flat_binder_object *fp;
fp = to_flat_binder_object(hdr);
ret = binder_translate_binder(fp, t, thread);
if (ret < 0 ||
binder_alloc_copy_to_buffer(&target_proc->alloc,
t->buffer,
object_offset,
fp, sizeof(*fp))) {
return_error = BR_FAILED_REPLY;
return_error_param = ret;
return_error_line = __LINE__;
goto err_translate_failed;
}
} break;
case BINDER_TYPE_HANDLE:
case BINDER_TYPE_WEAK_HANDLE: {
struct flat_binder_object *fp;
fp = to_flat_binder_object(hdr);
ret = binder_translate_handle(fp, t, thread);
if (ret < 0 ||
binder_alloc_copy_to_buffer(&target_proc->alloc,
t->buffer,
object_offset,
fp, sizeof(*fp))) {
return_error = BR_FAILED_REPLY;
return_error_param = ret;
return_error_line = __LINE__;
goto err_translate_failed;
}
} break;
case BINDER_TYPE_FD: {
struct binder_fd_object *fp = to_binder_fd_object(hdr);
binder_size_t fd_offset = object_offset +
(uintptr_t)&fp->fd - (uintptr_t)fp;
int ret = binder_translate_fd(fp->fd, fd_offset, t,
thread, in_reply_to);
fp->pad_binder = 0;
if (ret < 0 ||
binder_alloc_copy_to_buffer(&target_proc->alloc,
t->buffer,
object_offset,
fp, sizeof(*fp))) {
return_error = BR_FAILED_REPLY;
return_error_param = ret;
return_error_line = __LINE__;
goto err_translate_failed;
}
} break;
…………
}
}
/* Done processing objects, copy the rest of the buffer */
if (binder_alloc_copy_user_to_buffer(
&target_proc->alloc,
t->buffer, user_offset,
user_buffer + user_offset,
tr->data_size - user_offset)) {
binder_user_error("%d:%d got transaction with invalid data ptr\n",
proc->pid, thread->pid);
return_error = BR_FAILED_REPLY;
return_error_param = -EFAULT;
return_error_line = __LINE__;
goto err_copy_data_failed;
}
……………
}
这块就是Binder事务交互中,将调用进程的数据在内核态拷贝给被调用进程的内存中进行处理的代码。
代码11行用来在目标进程中找到空闲内存,并且实际分配内存页框。找到的空闲内存的大小是根据tr->data_size、tr->offsets_size和extra_buffers_size三个加在一起的和来定的。tr->data_size是数据的大小,tr->offsets_size是描写数据中关于Binder对象位置信息数据的大小,extra_buffers_size是额外数据大小,在BC_TRANSACTION命令中是0。binder_alloc_new_buf()函数就是找到管理的空闲内存,并且实际分配内存页框。
代码15行用来将事务数据中的关于Binder对象的相关信息拷贝到刚才找到的空闲内存中。Binder对象的相关信息这块是指,在事务数据中的内存位置,描述每个位置的数据大小是sizeof(binder_size_t)。tr->data.ptr.offsets就是这些数据的内存位置,tr->offsets_size是这些数据的大小。以2个Binder对象来举例子,图1是事务中的数据。这一步骤拷贝之后,被调用进程刚才申请的内存变成图2的内存结构了。
上面就是第二步所做的实际数据拷贝。将数据从调用进程拷贝到被调用进程的内存之中。这里需要明确的一点,被调用进程的这些拷贝的内存也是被用户态线性地址映射了的。这样再将这些内存地址放到合适的数据结构中,被调用进程返回到用户态,就可以操作它了。
这一步是将进程间交互数据地址放到合适的数据结构中,让进程返回到用户态就能操作这些数据了。
static int binder_thread_read(struct binder_proc *proc,
struct binder_thread *thread,
binder_uintptr_t binder_buffer, size_t size,
binder_size_t *consumed, int non_block)
{
void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
void __user *ptr = buffer + *consumed;
void __user *end = buffer + size;
…………
while (1) {
uint32_t cmd;
struct binder_transaction_data_secctx tr;
struct binder_transaction_data *trd = &tr.transaction_data;
struct binder_work *w = NULL;
struct list_head *list = NULL;
struct binder_transaction *t = NULL;
struct binder_thread *t_from;
size_t trsize = sizeof(*trd);
…………
w = binder_dequeue_work_head_ilocked(list);
if (binder_worklist_empty_ilocked(&thread->todo))
thread->process_todo = false;
switch (w->type) {
case BINDER_WORK_TRANSACTION: {
binder_inner_proc_unlock(proc);
t = container_of(w, struct binder_transaction, work);
} break;
…………
}
trd->data_size = t->buffer->data_size;
trd->offsets_size = t->buffer->offsets_size;
trd->data.ptr.buffer = (uintptr_t)t->buffer->user_data;
trd->data.ptr.offsets = trd->data.ptr.buffer +
ALIGN(t->buffer->data_size,
sizeof(void *));
tr.secctx = t->security_ctx;
if (t->security_ctx) {
cmd = BR_TRANSACTION_SEC_CTX;
trsize = sizeof(tr);
}
if (put_user(cmd, (uint32_t __user *)ptr)) {
if (t_from)
binder_thread_dec_tmpref(t_from);
binder_cleanup_transaction(t, "put_user failed",
BR_FAILED_REPLY);
return -EFAULT;
}
ptr += sizeof(uint32_t);
if (copy_to_user(ptr, &tr, trsize)) {
if (t_from)
binder_thread_dec_tmpref(t_from);
binder_cleanup_transaction(t, "copy_to_user failed",
BR_FAILED_REPLY);
return -EFAULT;
}
ptr += trsize;
…………
}
…………
}
代码33行,t->buffer->user_data就是进程间交互数据的用户态起始线性地址。这里只是将数据地址赋值给了trd->data.ptr.buffer。
代码43行,将cmd(可能BR_TRANSACTION或BR_TRANSACTION_SEC_CTX)复制到用户态的内存ptr中。
代码53行,将包含进程交互数据地址的数据结构binder_transaction_data_secctx变量tr也拷贝到用户态地址内存ptr中。
这样,等进程返回到用户态,就能先拿到binder_transaction_data_secctx变量,然后通过它再找到binder_transaction_data结构,最后就能找到它的成员data.ptr.buffer,它就是数据的地址。而数据大小在其成员data_size,数据中包含的Binder对象的数据地址是成员data.ptr.offsets,Binder对象位置大小是成员offsets_size。
以上就是Binder机制中关于数据一次拷贝的过程,可见在真正拷贝数据的是第二步,在内核态执行的数据拷贝。