Binder内存
为了让大家更好的理解Binder机制,主要是讲解Binder内存,Binder内存我主要
分3个内容来依次讲解,分别为:
1、mmap机制 2、内存分配 3、内存释放
一、mmap机制
上面从代码的角度阐释了binder_mmap,也是Binder进程通信效率高的核心机制所在,如下图:
虚拟进程地址空间(vm_area_struct)和虚拟内核地址空间(vm_struct)都映射到同一块物理内存空间。当 Client端与Server端发送数据时,Client(作为数据发送端)先从自己的进程空间把IPC通信数据 copy_from_user拷贝到内核空间,而Server端(作为数据接收端)与内核共享数据,不再需要拷贝数据, 而是通过内存地址空间的偏移量,即可获悉内存地址,整个过程只要发生一次内存拷贝。一般地做法, 需要Client端进程空间拷贝到内核空间,再由内核空间拷贝到Server进程,会发生两次拷贝。
对于进程和内核虚拟地址映射到同一个物理内存的操作是发生在数据接收端,而数据发送端还是 需要将用户态的数据复制到内核态。到此,可能会有同学会问,为什么不直接让发送端和接收端 直接映射到同一个物理空间,这样不就是连一次复制操作都不需要了,0次复制操作就是与Linux 标准内核的共享内存你的IPC机制没有区别了,对于共享内存虽然效率高,但是对于多进程的同步 问题比较复杂,而管道/消息队列等IPC需要复制2次,效率低。关于Linux效率的对比,请看前面 的文章。总之Android选择Binder是基于速度和安全性的考虑。
下面这图是从Binder在进程间数据通信的流程图,从图中更能明白Binder的内存转移关系。
二、内存分配
Binder内存分配方法通过binder_alloc_buf()方法,内存管理单元为binder_buffffer结构体,只有 在binder_transaction过程中才需要分配buffffer。具体代码在/kernel/drivers/android/binder.c 678行,我选取了重点部分
static struct binder_buffer *binder_alloc_buf(struct binder_proc *proc,
size_t data_size, size_t offsets_size, int is_async)
{
//*** 省略部分代码 ***
//如果不存在虚拟地址空间为,则直接返回
if (proc->vma == NULL) {
pr_err("%d: binder_alloc_buf, no vma\n",
proc->pid);
return NULL;
}
data_offsets_size = ALIGN(data_size, sizeof(void *)) +
ALIGN(offsets_size, sizeof(void *));
//非法的size,直接返回
if (data_offsets_size < data_size || data_offsets_size < offsets_size) {
binder_user_error("%d: got transaction with invalid size %zd-%zd\n",
proc->pid, data_size, offsets_size);
return NULL;
}
size = data_offsets_size + ALIGN(extra_buffers_size, sizeof(void *));
//非法的size,直接返回
if (size < data_offsets_size || size < extra_buffers_size) {
binder_user_error("%d: got transaction with invalid extra_buffers_size
%zd\n",
proc->pid, extra_buffers_size);
return NULL;
}
//如果 剩余的异步空间太少,以至于满足需求,也直接返回
if (is_async &&proc->free_async_space < size + sizeof(struct binder_buffer)) {
binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
"%d: binder_alloc_buf size %zd failed, no async space left\n",
proc->pid, size);
return NULL;
}
//从binder_buffer的红黑树丛中,查找大小相等的buffer块
while (n) {
buffer = rb_entry(n, struct binder_buffer, rb_node);
BUG_ON(!buffer->free);
buffer_size = binder_buffer_size(proc, buffer);
if (size < buffer_size) {
best_fit = n;
n = n->rb_left;
} else if (size > buffer_size)
n = n->rb_right;
else {
best_fit = n;
break;
}
}
//如果内存分配失败,地址为空
if (best_fit == NULL) {
pr_err("%d: binder_alloc_buf size %zd failed, no address space\n",
proc->pid, size);
return NULL;
}
if (n == NULL) {
buffer = rb_entry(best_fit, struct binder_buffer, rb_node);
buffer_size = binder_buffer_size(proc, buffer);
}
binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
"%d: binder_alloc_buf size %zd got buffer %p size %zd\n",
proc->pid, size, buffer, buffer_size);
has_page_addr =
(void *)(((uintptr_t)buffer->data + buffer_size) & PAGE_MASK);
if (n == NULL) {
if (size + sizeof(struct binder_buffer) + 4 >= buffer_size)
buffer_size = size; /* no room for other buffers */
else
buffer_size = size + sizeof(struct binder_buffer);
}
end_page_addr =
(void *)PAGE_ALIGN((uintptr_t)buffer->data + buffer_size);
if (end_page_addr > has_page_addr)
end_page_addr = has_page_addr;
if (binder_update_page_range(proc, 1,
(void *)PAGE_ALIGN((uintptr_t)buffer->data), end_page_addr, NULL))
return NULL;
rb_erase(best_fit, &proc->free_buffers);
buffer->free = 0;
binder_insert_allocated_buffer(proc, buffer);
if (buffer_size != size) {
struct binder_buffer *new_buffer = (void *)buffer->data + size;
三、内存释放
内存释放相关函数:
- binder_free_buf() : 在/kernel/drivers/android/binder.c 847行
- binder_delete_free_buffffer():在/kernel/drivers/android/binder.c 802行
- binder_transaction_buffffer_release():在/kernel/drivers/android/binder.c 1430行
以上是关于binder的内存介绍简单解析;更多有关Android中binder的学习可以前往这个技术文档《Android Binder技术》,需要可以前往了解一下相关。
总结
Andorid系统中的一种进程间通信的方式,虽然Android系统基于Linux,但是它并没有采用Linux自带的进程间通信方式,而是采用了更高效的binder,理解binder对于理解Android系统具有至关重要的作用。
网友评论